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. |