File Coverage

blib/lib/File/Tail/Inotify2.pm
Criterion Covered Total %
statement 61 64 95.3
branch 13 18 72.2
condition 6 15 40.0
subroutine 15 16 93.7
pod 2 2 100.0
total 97 115 84.3


line stmt bran cond sub pod time code
1             package File::Tail::Inotify2;
2              
3 3     3   166751 use 5.008008;
  3         14  
  3         125  
4 3     3   19 use strict;
  3         7  
  3         98  
5 3     3   15 use warnings;
  3         23  
  3         88  
6 3     3   3625 use Linux::Inotify2;
  3         11113  
  3         538  
7 3     3   27 use File::Basename qw(dirname);
  3         6  
  3         261  
8 3     3   16 use Fcntl qw(SEEK_SET SEEK_END);
  3         5  
  3         199  
9 3     3   17 use Carp ();
  3         6  
  3         172  
10              
11             our $VERSION = '1.00';
12              
13             use constant {
14 3         2719 FMASK => IN_MODIFY | IN_MOVE_SELF,
15             DMASK => IN_CREATE | IN_MOVED_FROM,
16 3     3   17 };
  3         5  
17              
18             sub new {
19 2     2 1 2004 my $class = shift;
20 2         15 my %args = (
21             file => undef,
22             on_read => undef,
23             @_,
24             );
25              
26 2         7 for (qw(file on_read)) {
27 4 50       19 Carp::croak "Mandatory parameter $_ was missing."
28             unless $args{$_};
29             }
30              
31 2 50 33     33 unless ( ref $args{on_read} && ref $args{on_read} eq 'CODE' ) {
32 0         0 Carp::croak "on_read must be CODE";
33             }
34              
35 2 50       22 my $inotify = Linux::Inotify2->new
36             or Carp::croak "Cannot create Inotify2 object: $!";
37              
38 2   33     86 my $self = bless {
39             file => $args{file},
40             on_read => $args{on_read},
41             curpos => 0,
42             in_move => 0,
43             inotify => $inotify,
44             },
45             ref $class || $class;
46              
47 2         10 $self->_set_watcher;
48              
49 2         66 return $self;
50             }
51              
52             sub poll {
53 0     0 1 0 my $self = shift;
54 0   0     0 1 while $self->{inotify}->poll || $self->{in_move};
55             }
56              
57             sub _set_watcher {
58 2     2   6 my $self = shift;
59 2         10 $self->_set_file_watcher;
60 2         106 $self->_set_dir_watcher;
61             }
62              
63             sub _set_file_watcher {
64 3     3   6 my $self = shift;
65 3         111 my $size = (stat $self->{file})[7];
66 3         8 $self->{curpos} = $size;
67 3         13 $self->{inotify}->watch( $self->{file}, FMASK, $self->_in_modify );
68             }
69              
70             sub _in_modify {
71 3     3   6 my $self = shift;
72             return sub {
73 3     3   738 my $event = shift;
74 3 100       13 if ( $event->IN_MODIFY ) {
75 2 50       27 open my $fh, '<', $event->fullname
76             or Carp::croak "Cannot open " . $event->fullname . ": $!";
77 2         114 seek $fh, $self->{curpos}, SEEK_SET;
78              
79 2         48 while (<$fh>) {
80 2         9 $self->{on_read}->($_);
81             }
82              
83 2         2167 $self->{curpos} = tell $fh;
84 2 50       34 close $fh
85             or Carp::croak "Cannot close " . $event->fullname . ": $!";
86             }
87 3 100       17 if ( $event->IN_MOVE_SELF ) {
88 1         7 $event->w->cancel;
89             }
90 3         28 };
91             }
92              
93             sub _set_dir_watcher {
94 2     2   4 my $self = shift;
95             $self->{inotify}->watch(
96             dirname( $self->{file} ),
97             DMASK,
98             sub {
99 2     2   1104 my $event = shift;
100 2 100 66     9 if ( $event->IN_MOVED_FROM && $event->fullname eq $self->{file} ) {
101 1         22 $self->{in_move} = 1;
102             }
103 2 100 66     30 if ( $event->IN_CREATE && $event->fullname eq $self->{file} ) {
104 1         19 $self->{curpos} = (stat $event->fullname)[7];
105 1         29 $self->_set_file_watcher;
106 1         31 $self->{in_move} = 0;
107             }
108             }
109 2         168 );
110             }
111              
112             1;
113             __END__