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   3618 use Moo::Role;
  6         8  
  6         32  
6 6     6   1402 use MooX::late;
  6         6  
  6         41  
7 6     6   577 use namespace::autoclean;
  6         8  
  6         34  
8 6     6   355 use AnyEvent;
  6         8  
  6         96  
9 6     6   1538 use Linux::Inotify2;
  5         5987  
  5         523  
10 5     5   23 use Carp;
  5         12  
  5         206  
11 5     5   17 use Path::Iterator::Rule;
  5         5  
  5         2037  
12              
13             our $VERSION = '1.20';
14              
15             # use Scalar::Util qw(weaken); # Attempt to address RT#57104, but alas...
16              
17             sub _init {
18 4     4   7 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         159 my $old_fs = $self->_old_fs;
26 4         34 my @dirs = grep { $old_fs->{$_}->{is_dir} } keys %$old_fs;
  28         35  
27              
28             # weaken $self; # Attempt to address RT#57104, but alas...
29              
30 4         9 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   133 sub { my $e = shift; $self->_process_events($e); } );
  28         419  
  28         103  
36             }
37              
38 4         131 $self->_fs_monitor($inotify);
39              
40             $self->_watcher(
41             AnyEvent->io(
42             fh => $inotify->fileno,
43             poll => 'r',
44             cb => sub {
45 18     18   15555 $inotify->poll;
46 4         27 } ) );
47              
48 4         6611 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   16 my ( $self, @raw_events ) = @_;
62 15         13 my @events = ();
63              
64 15         15 for my $e (@raw_events) {
65 15         12 my $type = undef;
66              
67 15 100       20 $type = 'modified' if ( $e->mask & ( IN_MODIFY | IN_ATTRIB ) );
68 15 100       53 $type = 'deleted' if ( $e->mask &
69             ( IN_DELETE | IN_DELETE_SELF | IN_MOVED_FROM | IN_MOVE_SELF ) );
70 15 100       39 $type = 'created' if ( $e->mask & ( IN_CREATE | IN_MOVED_TO ) );
71              
72 15 50       58 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     2192 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         56 while ( my $file = $next->() ) {
87 2 100       129 next if $file eq $e->fullname;
88 1         31 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         141 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 _add_created {
105 32     32   35 my ( $self, @events ) = @_;
106              
107 32         46 for my $event (@events) {
108 28 100 100     139 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   18 sub { my $e = shift; $self->_process_events($e); } );
  4         49  
  4         11  
115              
116             }
117             }
118              
119             1;
120              
121             __END__