| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package File::Monitor::Delta; |
|
2
|
|
|
|
|
|
|
|
|
3
|
6
|
|
|
6
|
|
34
|
use strict; |
|
|
6
|
|
|
|
|
10
|
|
|
|
6
|
|
|
|
|
227
|
|
|
4
|
6
|
|
|
6
|
|
32
|
use warnings; |
|
|
6
|
|
|
|
|
11
|
|
|
|
6
|
|
|
|
|
211
|
|
|
5
|
6
|
|
|
6
|
|
29
|
use Carp; |
|
|
6
|
|
|
|
|
12
|
|
|
|
6
|
|
|
|
|
347
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
6
|
|
|
6
|
|
32
|
use base qw(File::Monitor::Base); |
|
|
6
|
|
|
|
|
12
|
|
|
|
6
|
|
|
|
|
3112
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our $VERSION = '1.00'; |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
my %TAXONOMY; |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
BEGIN { |
|
14
|
|
|
|
|
|
|
my $created = sub { |
|
15
|
90
|
|
|
|
|
145
|
my ( $this, $old, $new, $key ) = @_; |
|
16
|
90
|
|
100
|
|
|
579
|
return ( !defined $old->{mode} && defined $new->{mode} ) || 0; |
|
17
|
6
|
|
|
6
|
|
41
|
}; |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
my $deleted = sub { |
|
20
|
45
|
|
|
|
|
95
|
my ( $this, $old, $new, $key ) = @_; |
|
21
|
45
|
|
|
|
|
113
|
return $created->( $this, $new, $old, $key ); |
|
22
|
6
|
|
|
|
|
25
|
}; |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my $num_diff = sub { |
|
25
|
225
|
|
|
|
|
350
|
my ( $this, $old, $new, $key ) = @_; |
|
26
|
225
|
|
100
|
|
|
1387
|
return ( $new->{$key} || 0 ) - ( $old->{$key} || 0 ); |
|
|
|
|
100
|
|
|
|
|
|
27
|
6
|
|
|
|
|
21
|
}; |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
my $bit_diff = sub { # XOR |
|
30
|
45
|
|
|
|
|
77
|
my ( $this, $old, $new, $key ) = @_; |
|
31
|
45
|
|
100
|
|
|
260
|
return ( $new->{$key} || 0 ) ^ ( $old->{$key} || 0 ); |
|
|
|
|
100
|
|
|
|
|
|
32
|
6
|
|
|
|
|
31
|
}; |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
my $nop = sub { # Just return value |
|
35
|
90
|
|
|
|
|
140
|
my ( $this, $old, $new, $key ) = @_; |
|
36
|
90
|
|
|
|
|
211
|
return $this->{delta}->{$key}; |
|
37
|
6
|
|
|
|
|
32
|
}; |
|
38
|
|
|
|
|
|
|
|
|
39
|
6
|
|
|
|
|
93
|
%TAXONOMY = ( |
|
40
|
|
|
|
|
|
|
change => { |
|
41
|
|
|
|
|
|
|
created => $created, |
|
42
|
|
|
|
|
|
|
deleted => $deleted, |
|
43
|
|
|
|
|
|
|
metadata => { |
|
44
|
|
|
|
|
|
|
time => { |
|
45
|
|
|
|
|
|
|
mtime => $num_diff, |
|
46
|
|
|
|
|
|
|
ctime => $num_diff, |
|
47
|
|
|
|
|
|
|
}, |
|
48
|
|
|
|
|
|
|
perms => { |
|
49
|
|
|
|
|
|
|
uid => $num_diff, |
|
50
|
|
|
|
|
|
|
gid => $num_diff, |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
# Bit delta |
|
53
|
|
|
|
|
|
|
mode => $bit_diff, |
|
54
|
|
|
|
|
|
|
}, |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# Value delta |
|
57
|
|
|
|
|
|
|
size => $num_diff, |
|
58
|
|
|
|
|
|
|
}, |
|
59
|
|
|
|
|
|
|
directory => { |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# List delta |
|
62
|
|
|
|
|
|
|
files_created => $nop, |
|
63
|
|
|
|
|
|
|
files_deleted => $nop |
|
64
|
|
|
|
|
|
|
} |
|
65
|
|
|
|
|
|
|
} |
|
66
|
|
|
|
|
|
|
); |
|
67
|
|
|
|
|
|
|
|
|
68
|
6
|
|
|
|
|
39
|
my @OBJ_ATTR = qw( |
|
69
|
|
|
|
|
|
|
dev inode mode num_links uid gid rdev size mtime ctime |
|
70
|
|
|
|
|
|
|
blk_size blocks error files |
|
71
|
|
|
|
|
|
|
); |
|
72
|
|
|
|
|
|
|
|
|
73
|
6
|
|
|
|
|
273
|
my $IS_ARRAY = qr/^files_/; |
|
74
|
|
|
|
|
|
|
|
|
75
|
6
|
|
|
6
|
|
38
|
no strict 'refs'; |
|
|
6
|
|
|
|
|
16
|
|
|
|
6
|
|
|
|
|
3230
|
|
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# Accessors for old/new attributes |
|
78
|
6
|
|
|
|
|
17
|
for my $pfx ( qw(old new) ) { |
|
79
|
12
|
|
|
|
|
34
|
for my $attr ( @OBJ_ATTR ) { |
|
80
|
168
|
|
|
|
|
315
|
my $func_name = "${pfx}_${attr}"; |
|
81
|
|
|
|
|
|
|
*$func_name = sub { |
|
82
|
168
|
|
|
168
|
|
70824
|
my $self = shift; |
|
83
|
168
|
100
|
|
|
|
14348
|
croak "$func_name is read-only" if @_; |
|
84
|
84
|
|
|
|
|
348
|
return $self->{ $pfx . '_info' }->{$attr}; |
|
85
|
168
|
|
|
|
|
1321
|
}; |
|
86
|
|
|
|
|
|
|
} |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# Accessors for deltas are named after the leaf keys in the taxonomy |
|
90
|
6
|
|
|
|
|
17
|
my @work = \%TAXONOMY; |
|
91
|
6
|
|
|
|
|
42
|
while ( my $obj = shift @work ) { |
|
92
|
36
|
|
|
|
|
144
|
while ( my ( $n, $v ) = each %$obj ) { |
|
93
|
90
|
|
|
|
|
169
|
my $is_name = "is_$n"; |
|
94
|
|
|
|
|
|
|
*$is_name = sub { |
|
95
|
414
|
|
|
414
|
|
554
|
my $self = shift; |
|
96
|
414
|
|
|
|
|
1028
|
return $self->is_event( $n ); |
|
97
|
90
|
|
|
|
|
791
|
}; |
|
98
|
|
|
|
|
|
|
|
|
99
|
90
|
100
|
|
|
|
274
|
if ( ref $v eq 'CODE' ) { |
|
|
|
50
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# Got a leaf item -> make an accessor |
|
102
|
60
|
|
|
|
|
79
|
my $func_name = $n; |
|
103
|
60
|
100
|
|
|
|
219
|
if ( $n =~ $IS_ARRAY ) { |
|
104
|
|
|
|
|
|
|
*$func_name = sub { |
|
105
|
82
|
|
|
82
|
|
46947
|
my $self = shift; |
|
106
|
82
|
100
|
|
|
|
1351
|
croak "$func_name is read-only" if @_; |
|
107
|
76
|
100
|
|
|
|
100
|
return @{ $self->{delta}->{$func_name} || [] }; |
|
|
76
|
|
|
|
|
616
|
|
|
108
|
12
|
|
|
|
|
126
|
}; |
|
109
|
|
|
|
|
|
|
} |
|
110
|
|
|
|
|
|
|
else { |
|
111
|
|
|
|
|
|
|
*$func_name = sub { |
|
112
|
126
|
|
|
126
|
|
55961
|
my $self = shift; |
|
113
|
126
|
100
|
|
|
|
4987
|
croak "$func_name is read-only" if @_; |
|
114
|
102
|
|
|
|
|
473
|
return $self->{delta}->{$func_name}; |
|
115
|
48
|
|
|
|
|
3057
|
}; |
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
} |
|
118
|
|
|
|
|
|
|
elsif ( ref $v eq 'HASH' ) { |
|
119
|
30
|
|
|
|
|
147
|
push @work, $v; |
|
120
|
|
|
|
|
|
|
} |
|
121
|
|
|
|
|
|
|
else { |
|
122
|
0
|
|
|
|
|
0
|
die "\%TAXONOMY contains a ", ref $v; |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
} |
|
126
|
|
|
|
|
|
|
} |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
sub _initialize { |
|
129
|
333
|
|
|
333
|
|
426
|
my $self = shift; |
|
130
|
333
|
|
|
|
|
420
|
my $args = shift; |
|
131
|
|
|
|
|
|
|
|
|
132
|
333
|
|
|
|
|
1056
|
$self->SUPER::_initialize( $args ); |
|
133
|
|
|
|
|
|
|
|
|
134
|
333
|
|
|
|
|
704
|
for my $attr ( qw(object old_info new_info) ) { |
|
135
|
999
|
50
|
|
|
|
2094
|
croak "You must supply a value for $attr" |
|
136
|
|
|
|
|
|
|
unless exists $args->{$attr}; |
|
137
|
999
|
|
|
|
|
3047
|
$self->{$attr} = delete $args->{$attr}; |
|
138
|
|
|
|
|
|
|
} |
|
139
|
|
|
|
|
|
|
|
|
140
|
333
|
|
|
|
|
1084
|
$self->_report_extra( $args ); |
|
141
|
|
|
|
|
|
|
|
|
142
|
333
|
100
|
|
|
|
1144
|
if ( !$self->_deep_compare( $self->{old_info}, $self->{new_info} ) ) { |
|
143
|
45
|
|
|
|
|
4550
|
$self->_compute_delta; |
|
144
|
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
sub object { |
|
148
|
167
|
|
|
167
|
1
|
248
|
my $self = shift; |
|
149
|
167
|
50
|
|
|
|
406
|
croak "object is read-only" if @_; |
|
150
|
167
|
|
|
|
|
882
|
return $self->{object}; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
sub name { |
|
154
|
122
|
|
|
122
|
1
|
71700
|
my $self = shift; |
|
155
|
122
|
|
|
|
|
280
|
return $self->object->name( @_ ); |
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
sub _deep_compare { |
|
159
|
333
|
|
|
333
|
|
514
|
my ( $self, $this, $that ) = @_; |
|
160
|
6
|
|
|
6
|
|
6957
|
use Storable qw/freeze/; |
|
|
6
|
|
|
|
|
37979
|
|
|
|
6
|
|
|
|
|
14031
|
|
|
161
|
333
|
|
|
|
|
552
|
local $Storable::canonical = 1; |
|
162
|
333
|
|
|
|
|
1049
|
return freeze( $this ) eq freeze( $that ); |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub _diff_list { |
|
166
|
45
|
|
|
45
|
|
86
|
my ( $this, $that ) = @_; |
|
167
|
|
|
|
|
|
|
|
|
168
|
45
|
|
|
|
|
129
|
my %which = map { $_ => 1 } @$this; |
|
|
81
|
|
|
|
|
207
|
|
|
169
|
45
|
|
|
|
|
258
|
$which{$_} |= 2 for @$that; |
|
170
|
|
|
|
|
|
|
|
|
171
|
45
|
|
|
|
|
128
|
my @diff = ( [], [] ); |
|
172
|
45
|
|
|
|
|
172
|
while ( my ( $v, $w ) = each %which ) { |
|
173
|
157
|
100
|
|
|
|
394
|
push @{ $diff[ $w - 1 ] }, $v if $w < 3; |
|
|
95
|
|
|
|
|
452
|
|
|
174
|
|
|
|
|
|
|
} |
|
175
|
|
|
|
|
|
|
|
|
176
|
45
|
|
|
|
|
235
|
return @diff; |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub _walk_taxo { |
|
180
|
270
|
|
|
270
|
|
337
|
my $self = shift; |
|
181
|
270
|
|
|
|
|
413
|
my $taxo = shift; |
|
182
|
|
|
|
|
|
|
|
|
183
|
270
|
|
|
|
|
600
|
my $change_found = 0; |
|
184
|
|
|
|
|
|
|
|
|
185
|
270
|
|
|
|
|
1111
|
while ( my ( $n, $v ) = each %$taxo ) { |
|
186
|
675
|
100
|
|
|
|
1268
|
if ( ref $v eq 'CODE' ) { |
|
187
|
450
|
|
|
|
|
1121
|
my $diff |
|
188
|
|
|
|
|
|
|
= $v->( $self, $self->{old_info}, $self->{new_info}, $n ); |
|
189
|
450
|
100
|
|
|
|
5866
|
if ( $diff ) { |
|
190
|
173
|
|
|
|
|
352
|
$self->{delta}->{$n} = $diff; |
|
191
|
173
|
|
|
|
|
335
|
$self->{"_is_event"}->{$n}++; |
|
192
|
173
|
|
|
|
|
687
|
$change_found++; |
|
193
|
|
|
|
|
|
|
} |
|
194
|
|
|
|
|
|
|
} |
|
195
|
|
|
|
|
|
|
else { |
|
196
|
225
|
100
|
|
|
|
485
|
if ( $self->_walk_taxo( $v ) ) { |
|
197
|
166
|
|
|
|
|
312
|
$self->{"_is_event"}->{$n}++; |
|
198
|
166
|
|
|
|
|
581
|
$change_found++; |
|
199
|
|
|
|
|
|
|
} |
|
200
|
|
|
|
|
|
|
} |
|
201
|
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
|
|
203
|
270
|
|
|
|
|
843
|
return $change_found; |
|
204
|
|
|
|
|
|
|
} |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
sub _compute_delta { |
|
207
|
45
|
|
|
45
|
|
76
|
my $self = shift; |
|
208
|
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# Compute the file list deltas as a special case first |
|
210
|
45
|
|
100
|
|
|
417
|
my @df = _diff_list( |
|
|
|
|
100
|
|
|
|
|
|
211
|
|
|
|
|
|
|
$self->{old_info}->{files} || [], |
|
212
|
|
|
|
|
|
|
$self->{new_info}->{files} || [] |
|
213
|
|
|
|
|
|
|
); |
|
214
|
|
|
|
|
|
|
|
|
215
|
45
|
|
|
|
|
147
|
my $monitor = $self->object->owner; |
|
216
|
45
|
|
|
|
|
156
|
for my $attr ( qw(files_deleted files_created) ) { |
|
217
|
90
|
|
|
|
|
183
|
my @ar = map { $monitor->_make_absolute( $_ ) } sort @{ shift @df }; |
|
|
95
|
|
|
|
|
467
|
|
|
|
90
|
|
|
|
|
222
|
|
|
218
|
90
|
100
|
|
|
|
661
|
$self->{delta}->{$attr} = \@ar if @ar; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
|
|
221
|
45
|
|
|
|
|
120
|
$self->{_is_event} = {}; |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
# Now do everything else |
|
224
|
45
|
|
|
|
|
152
|
$self->_walk_taxo( \%TAXONOMY ); |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub is_event { |
|
228
|
838
|
|
|
838
|
1
|
31747
|
my $self = shift; |
|
229
|
838
|
|
|
|
|
1070
|
my $event = shift; |
|
230
|
|
|
|
|
|
|
|
|
231
|
838
|
|
|
|
|
13099
|
return $self->{_is_event}->{$event}; |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
sub _trigger_callbacks { |
|
235
|
84
|
|
|
84
|
|
112
|
my $self = shift; |
|
236
|
84
|
|
100
|
|
|
376
|
my $callbacks = shift || {}; |
|
237
|
84
|
|
|
|
|
256
|
my $name = $self->name; |
|
238
|
|
|
|
|
|
|
|
|
239
|
84
|
50
|
|
|
|
204
|
if ( $self->is_change ) { |
|
240
|
84
|
|
|
|
|
491
|
while ( my ( $event, $cb ) = each %$callbacks ) { |
|
241
|
240
|
100
|
|
|
|
1120
|
if ( $self->is_event( $event ) ) { |
|
242
|
118
|
|
|
|
|
362
|
$cb->( $name, $event, $self ); |
|
243
|
|
|
|
|
|
|
} |
|
244
|
|
|
|
|
|
|
} |
|
245
|
|
|
|
|
|
|
} |
|
246
|
|
|
|
|
|
|
} |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
1; |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
=head1 NAME |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
File::Monitor::Delta - Encapsulate a change to a file or directory |
|
253
|
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=head1 VERSION |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
This document describes File::Monitor::Delta version 1.00 |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
use File::Monitor; |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
my $monitor = File::Monitor->new(); |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
# Watch some files |
|
265
|
|
|
|
|
|
|
for my $file (qw( myfile.txt yourfile.txt otherfile.txt some_directory )) { |
|
266
|
|
|
|
|
|
|
$monitor->watch( $file ); |
|
267
|
|
|
|
|
|
|
} |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
# First scan just finds out about the monitored files. No changes |
|
270
|
|
|
|
|
|
|
# will be reported. |
|
271
|
|
|
|
|
|
|
$object->scan; |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# After the first scan we get a list of File::Monitor::Delta objects |
|
274
|
|
|
|
|
|
|
# that describe any changes |
|
275
|
|
|
|
|
|
|
my @changes = $object->scan; |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
for my $change (@changes) { |
|
278
|
|
|
|
|
|
|
# Call methods on File::Monitor::Delta to discover what changed |
|
279
|
|
|
|
|
|
|
if ($change->is_size) { |
|
280
|
|
|
|
|
|
|
my $name = $change->name; |
|
281
|
|
|
|
|
|
|
my $old_size = $change->old_size; |
|
282
|
|
|
|
|
|
|
my $new_size = $change->new_size; |
|
283
|
|
|
|
|
|
|
print "$name has changed size from $old_size to $new_size\n"; |
|
284
|
|
|
|
|
|
|
} |
|
285
|
|
|
|
|
|
|
} |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
288
|
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
When L or L detects a change to a |
|
290
|
|
|
|
|
|
|
file or directory it packages the details of the change in a |
|
291
|
|
|
|
|
|
|
C object. |
|
292
|
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
Methods exist to discover the nature of the change (C et al.), |
|
294
|
|
|
|
|
|
|
retrieve the attributes of the file or directory before and after the |
|
295
|
|
|
|
|
|
|
change (C, C, C, C etc), |
|
296
|
|
|
|
|
|
|
retrieve details of the change in a convenient form (C, |
|
297
|
|
|
|
|
|
|
C) and gain access to the L for |
|
298
|
|
|
|
|
|
|
which the change was observed (C |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
Unless you are writing a subclass of C it |
|
301
|
|
|
|
|
|
|
isn't normally necessary to instantiate C |
|
302
|
|
|
|
|
|
|
objects directly. |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=head2 Changes Classified |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
Various types of change are identified and classified into the following |
|
307
|
|
|
|
|
|
|
hierarchy: |
|
308
|
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
change |
|
310
|
|
|
|
|
|
|
created |
|
311
|
|
|
|
|
|
|
deleted |
|
312
|
|
|
|
|
|
|
metadata |
|
313
|
|
|
|
|
|
|
time |
|
314
|
|
|
|
|
|
|
mtime |
|
315
|
|
|
|
|
|
|
ctime |
|
316
|
|
|
|
|
|
|
perms |
|
317
|
|
|
|
|
|
|
uid |
|
318
|
|
|
|
|
|
|
gid |
|
319
|
|
|
|
|
|
|
mode |
|
320
|
|
|
|
|
|
|
size |
|
321
|
|
|
|
|
|
|
directory |
|
322
|
|
|
|
|
|
|
files_created |
|
323
|
|
|
|
|
|
|
files_deleted |
|
324
|
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
The terminal nodes of that tree (C, C, C, |
|
326
|
|
|
|
|
|
|
C, C, C, C, C, C and |
|
327
|
|
|
|
|
|
|
C) represent actual change events. Non terminal nodes |
|
328
|
|
|
|
|
|
|
represent broader classifications of events. For example if a file's |
|
329
|
|
|
|
|
|
|
mtime changes the resulting C object will return |
|
330
|
|
|
|
|
|
|
true for each of |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
$delta->is_mtime; # The actual change |
|
333
|
|
|
|
|
|
|
$delta->is_time; # One of the file times changed |
|
334
|
|
|
|
|
|
|
$delta->is_metadata; # The file's metadata changed |
|
335
|
|
|
|
|
|
|
$delta->is_change; # This is true for any change |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
This event classification is used to target callbacks at specific events |
|
338
|
|
|
|
|
|
|
or categories of events. See L and |
|
339
|
|
|
|
|
|
|
L for more information about callbacks. |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
=head2 Accessors |
|
342
|
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
Various accessors allow the state of the object before and after the |
|
344
|
|
|
|
|
|
|
change and the details of the change to be queried. |
|
345
|
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
These accessors return information about the state of the file or |
|
347
|
|
|
|
|
|
|
directory before the detected change: |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
old_dev old_inode old_mode old_num_links old_uid old_gid |
|
350
|
|
|
|
|
|
|
old_rdev old_size old_mtime old_ctime old_blk_size old_blocks |
|
351
|
|
|
|
|
|
|
old_error old_files |
|
352
|
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
For example: |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
my $mode_was = $delta->old_mode; |
|
356
|
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
These accessors return information about the state of the file or |
|
358
|
|
|
|
|
|
|
directory after the detected change: |
|
359
|
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
new_dev new_inode new_mode new_num_links new_uid new_gid |
|
361
|
|
|
|
|
|
|
new_rdev new_size new_mtime new_ctime new_blk_size new_blocks |
|
362
|
|
|
|
|
|
|
new_error new_files |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
For example: |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
my $new_size = $delta->new_size; |
|
367
|
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
These accessors return a value that reflects the change in the |
|
369
|
|
|
|
|
|
|
corresponding attribute: |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
created deleted mtime ctime uid gid mode size |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
With the exception of C, C and C they return |
|
374
|
|
|
|
|
|
|
the difference between the old value and the new value. This is only |
|
375
|
|
|
|
|
|
|
really useful in the case of C: |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
my $grown_by = $delta->size; |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
Is equivalent to |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
my $grown_by = $delta->new_size - $delta->old_size; |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
For the other values the subtraction is performed merely to ensure that |
|
384
|
|
|
|
|
|
|
these values are non-zero. |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
# Get the difference between the old and new UID. Unlikely to be |
|
387
|
|
|
|
|
|
|
# interesting. |
|
388
|
|
|
|
|
|
|
my $delta_uid = $delta->uid; |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
As a special case the delta value for C is computed as old_mode ^ |
|
391
|
|
|
|
|
|
|
new_mode. The old mode is XORed with the new mode so that |
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
my $bits_changed = $delta->mode; |
|
394
|
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
gets a bitmask of the mode bits that have changed. |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
If the detected change was the creation or deletion of a file C |
|
398
|
|
|
|
|
|
|
or C respectively will be true. |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
if ( $delta->created ) { |
|
401
|
|
|
|
|
|
|
print "Yippee! We exist\n"; |
|
402
|
|
|
|
|
|
|
} |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
if ( $delta->deleted ) { |
|
405
|
|
|
|
|
|
|
print "Boo! We got deleted\n"; |
|
406
|
|
|
|
|
|
|
} |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
For a directory which is being monitored with the C or C |
|
409
|
|
|
|
|
|
|
options (see L for details) C and |
|
410
|
|
|
|
|
|
|
C will contain respectively the list of new files below |
|
411
|
|
|
|
|
|
|
this directory and the list of files that have been deleted. |
|
412
|
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
my @new_files = $delta->files_created; |
|
414
|
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
for my $file ( @new_files ) { |
|
416
|
|
|
|
|
|
|
print "$file created\n"; |
|
417
|
|
|
|
|
|
|
} |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
my @gone_away = $delta->files_deletedl |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
for my $file ( @gone_away ) { |
|
422
|
|
|
|
|
|
|
print "$file deleted\n"; |
|
423
|
|
|
|
|
|
|
} |
|
424
|
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=head1 INTERFACE |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
=over |
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
=item C<< new( $args ) >> |
|
430
|
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
Create a new C object. You don't normally need to |
|
432
|
|
|
|
|
|
|
do this; deltas are created as necessary by L. |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
The single argument is a reference to a hash that must contain the |
|
435
|
|
|
|
|
|
|
following keys: |
|
436
|
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
=over |
|
438
|
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
=item object |
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
The L for which this change is being reported. |
|
442
|
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=item old_info |
|
444
|
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
A hash describing the state of the file or directory before the change. |
|
446
|
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=item new_info |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
A hash describing the state of the file or directory after the change. |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=back |
|
452
|
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
=item C<< is_event( $event ) >> |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
Returns true if this delta represents the specified event. For example, |
|
456
|
|
|
|
|
|
|
if a file's size changes the following will all return true: |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
$delta->is_event('size'); # The actual change |
|
459
|
|
|
|
|
|
|
$delta->is_event('metadata'); # The file's metadata changed |
|
460
|
|
|
|
|
|
|
$delta->is_event('change'); # This is true for any change |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
Valid eventnames are |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
change created deleted metadata time mtime ctime perms uid gid |
|
465
|
|
|
|
|
|
|
mode size directory files_created files_deleted |
|
466
|
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
As an alternative interface you may call CI directly. |
|
468
|
|
|
|
|
|
|
For example |
|
469
|
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
$delta->is_size; |
|
471
|
|
|
|
|
|
|
$delta->is_metadata; |
|
472
|
|
|
|
|
|
|
$delta->is_change; |
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
Unless the event you wish to test for is variable this is a cleaner, |
|
475
|
|
|
|
|
|
|
less error prone interface. |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
Normally your code won't see a C for which |
|
478
|
|
|
|
|
|
|
C returns false. Any change causes C to be true |
|
479
|
|
|
|
|
|
|
and the C methods of C and C |
|
480
|
|
|
|
|
|
|
don't return deltas for unchanged files. |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=item C<< name >> |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
The name of the file for which the change is being reported. Read only. |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
=item C<< object >> |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
The L for which this change is being reported. |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
=back |
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
=head2 Other methods |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
As mentioned above a large number of other accessors are provided to get |
|
495
|
|
|
|
|
|
|
the state of the object before and after the change and query details of |
|
496
|
|
|
|
|
|
|
the change: |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
old_dev old_inode old_mode old_num_links old_uid old_gid old_rdev |
|
499
|
|
|
|
|
|
|
old_size old_mtime old_ctime old_blk_size old_blocks old_error |
|
500
|
|
|
|
|
|
|
old_files new_dev new_inode new_mode new_num_links new_uid new_gid |
|
501
|
|
|
|
|
|
|
new_rdev new_size new_mtime new_ctime new_blk_size new_blocks |
|
502
|
|
|
|
|
|
|
new_error new_files created deleted mtime ctime uid gid mode size |
|
503
|
|
|
|
|
|
|
files_created files_deleted name |
|
504
|
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
See L for details of these. |
|
506
|
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
=head1 DIAGNOSTICS |
|
508
|
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
=over |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=item C<< %s is read-only >> |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
C is an immutable description of a change in a |
|
514
|
|
|
|
|
|
|
file's state. None of its accessors allow values to be changed. |
|
515
|
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
=item C<< You must supply a value for %s >> |
|
517
|
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
The three options that C (C, C and C |
|
519
|
|
|
|
|
|
|
are all mandatory. |
|
520
|
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
=back |
|
522
|
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=head1 CONFIGURATION AND ENVIRONMENT |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
File::Monitor::Delta requires no configuration files or environment variables. |
|
526
|
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=head1 DEPENDENCIES |
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
None. |
|
530
|
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
=head1 INCOMPATIBILITIES |
|
532
|
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
None reported. |
|
534
|
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
=head1 BUGS AND LIMITATIONS |
|
536
|
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
No bugs have been reported. |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
Please report any bugs or feature requests to |
|
540
|
|
|
|
|
|
|
C, or through the web interface at |
|
541
|
|
|
|
|
|
|
L. |
|
542
|
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
=head1 AUTHOR |
|
544
|
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
Andy Armstrong C<< >> |
|
546
|
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
Faycal Chraibi originally registered the File::Monitor namespace and |
|
548
|
|
|
|
|
|
|
then kindly handed it to me. |
|
549
|
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=head1 LICENCE AND COPYRIGHT |
|
551
|
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
Copyright (c) 2007, Andy Armstrong C<< >>. All rights reserved. |
|
553
|
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
This module is free software; you can redistribute it and/or |
|
555
|
|
|
|
|
|
|
modify it under the same terms as Perl itself. See L. |
|
556
|
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
=head1 DISCLAIMER OF WARRANTY |
|
558
|
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY |
|
560
|
|
|
|
|
|
|
FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN |
|
561
|
|
|
|
|
|
|
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES |
|
562
|
|
|
|
|
|
|
PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER |
|
563
|
|
|
|
|
|
|
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
564
|
|
|
|
|
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE |
|
565
|
|
|
|
|
|
|
ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH |
|
566
|
|
|
|
|
|
|
YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL |
|
567
|
|
|
|
|
|
|
NECESSARY SERVICING, REPAIR, OR CORRECTION. |
|
568
|
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
|
570
|
|
|
|
|
|
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR |
|
571
|
|
|
|
|
|
|
REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE |
|
572
|
|
|
|
|
|
|
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, |
|
573
|
|
|
|
|
|
|
OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE |
|
574
|
|
|
|
|
|
|
THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING |
|
575
|
|
|
|
|
|
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A |
|
576
|
|
|
|
|
|
|
FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF |
|
577
|
|
|
|
|
|
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |
|
578
|
|
|
|
|
|
|
SUCH DAMAGES. |