File Coverage

blib/lib/AnyEvent/Filesys/Notify/Role/Inotify2.pm
Criterion Covered Total %
statement 55 55 100.0
branch 14 16 87.5
condition 6 6 100.0
subroutine 13 13 100.0
pod n/a
total 88 90 97.7


line stmt bran cond sub pod time code
1             package AnyEvent::Filesys::Notify::Role::Inotify2;
2              
3             # ABSTRACT: Use Linux::Inotify2 to watch for changed files
4              
5 6     6   3706 use Moo::Role;
  6         9  
  6         38  
6 6     6   1475 use MooX::late;
  6         6  
  6         42  
7 6     6   572 use namespace::autoclean;
  6         6  
  6         39  
8 6     6   368 use AnyEvent;
  6         12  
  6         99  
9 6     6   1632 use Linux::Inotify2;
  5         6127  
  5         491  
10 5     5   22 use Carp;
  5         8  
  5         213  
11 5     5   17 use Path::Iterator::Rule;
  5         5  
  5         2411  
12              
13             our $VERSION = '1.21';
14              
15             # use Scalar::Util qw(weaken); # Attempt to address RT#57104, but alas...
16              
17             sub _init {
18 4     4   4 my $self = shift;
19              
20 4 50       11 my $inotify = Linux::Inotify2->new()
21             or croak "Unable to create new Linux::Inotify2 object";
22              
23             # Need to add all the subdirs to the watch list, this will catch
24             # modifications to files too.
25 4         149 my $old_fs = $self->_old_fs;
26 4         34 my @dirs = grep { $old_fs->{$_}->{is_dir} } keys %$old_fs;
  28         34  
27              
28             # weaken $self; # Attempt to address RT#57104, but alas...
29              
30 4         7 for my $dir (@dirs) {
31             $inotify->watch(
32             $dir,
33             IN_MODIFY | IN_CREATE | IN_DELETE | IN_DELETE_SELF |
34             IN_MOVE | IN_MOVE_SELF | IN_ATTRIB,
35 9     28   141 sub { my $e = shift; $self->_process_events($e); } );
  28         396  
  28         98  
36             }
37              
38 4         102 $self->_fs_monitor($inotify);
39              
40             $self->_watcher(
41             AnyEvent->io(
42             fh => $inotify->fileno,
43             poll => 'r',
44             cb => sub {
45 18     18   16299 $inotify->poll;
46 4         23 } ) );
47              
48 4         6396 return 1;
49             }
50              
51             # Parse the events returned by Inotify2 instead of rescanning the files.
52             # There are small changes in behavior compared to the parent code:
53             #
54             # 1. `touch test` causes an additional "modified" event after the "created"
55             # 2. `mv test2 test` if test exists before, event for test would be "modified"
56             # in parent code, but is "created" here
57             #
58             # Because of these differences, we default to the original behavior unless the
59             # parse_events flag is true.
60             sub _parse_events {
61 15     15   18 my ( $self, @raw_events ) = @_;
62 15         11 my @events = ();
63              
64 15         16 for my $e (@raw_events) {
65 15         12 my $type = undef;
66              
67 15 100       25 $type = 'modified' if ( $e->mask & ( IN_MODIFY | IN_ATTRIB ) );
68 15 100       57 $type = 'deleted' if ( $e->mask &
69             ( IN_DELETE | IN_DELETE_SELF | IN_MOVED_FROM | IN_MOVE_SELF ) );
70 15 100       42 $type = 'created' if ( $e->mask & ( IN_CREATE | IN_MOVED_TO ) );
71              
72 15 50       62 push(
73             @events,
74             AnyEvent::Filesys::Notify::Event->new(
75             path => $e->fullname,
76             type => $type,
77             is_dir => !! $e->IN_ISDIR,
78             ) ) if $type;
79              
80             # New directories are not automatically watched, we will add it to the
81             # list of watched directories in `around '_process_events'` but in
82             # the meantime, we will miss any newly created files in the subdir
83 15 100 100     2381 if ( $e->IN_ISDIR and $type eq 'created' ) {
84 1         9 my $rule = Path::Iterator::Rule->new;
85 1         6 my $next = $rule->iter( $e->fullname );
86 1         64 while ( my $file = $next->() ) {
87 2 100       142 next if $file eq $e->fullname;
88 1         54 push @events,
89             AnyEvent::Filesys::Notify::Event->new(
90             path => $file,
91             type => 'created',
92             is_dir => -d $file,
93             );
94             }
95              
96             }
97             }
98              
99 15         152 return @events;
100             }
101              
102             # Need to add newly created sub-dirs to the watch list.
103             # This is done after filtering. So entire dirs can be ignored efficiently;
104             sub _process_events_for_backend {
105 32     32   39 my ( $self, @events ) = @_;
106              
107 32         44 for my $event (@events) {
108 28 100 100     121 next unless $event->is_dir && $event->is_created;
109              
110             $self->_fs_monitor->watch(
111             $event->path,
112             IN_MODIFY | IN_CREATE | IN_DELETE | IN_DELETE_SELF |
113             IN_MOVE | IN_MOVE_SELF | IN_ATTRIB,
114 2     4   21 sub { my $e = shift; $self->_process_events($e); } );
  4         51  
  4         10  
115              
116             }
117             }
118              
119             1;
120              
121             __END__