File Coverage

blib/lib/File/Modified.pm
Criterion Covered Total %
statement 393 401 98.0
branch 19 28 67.8
condition 8 19 42.1
subroutine 118 119 99.1
pod 5 5 100.0
total 543 572 94.9


line stmt bran cond sub pod time code
1             package File::Modified;
2              
3 1     1   83705 use 5.006;
  1         3  
  1         33  
4 1     1   5 use strict;
  1         1  
  1         29  
5 1     1   6 use warnings;
  1         6  
  1         188  
6              
7             our $VERSION = '0.09';
8             our @ISA;
9              
10             sub new {
11 18     18 1 3014946 my ($class, %args) = @_;
12              
13 18   50     72 my $method = $args{method} || "MD5";
14 18   100     102 my $files = $args{files} || [];
15              
16 18         65 my $self = {
17             Defaultmethod => $method,
18             Files => {},
19             };
20              
21 18         51 bless $self, $class;
22              
23 18         62 $self->addfile(@$files);
24              
25 18         101 return $self;
26             };
27              
28             sub _make_digest_signature {
29 3     3   6 my ($self,$digest) = @_;
30              
31 1     1   6 eval "use Digest::$digest";
  1     1   1  
  1     1   33  
  1         475  
  0         0  
  0         0  
  1         867  
  0         0  
  0         0  
  3         243  
32              
33 3 100       20 if (! $@) {
34 1     1   6 no strict 'refs';
  1         2  
  1         854  
35 1 50       1 if (@{"Digest::${digest}::ISA"}) {
  1         6  
36 1         1 @{"File::Modified::Signature::${digest}::ISA"} = qw(File::Modified::Signature::Digest);
  1         22  
37 1         3 return 1;
38             };
39             };
40 2         10 return undef;
41             };
42              
43             sub add {
44 276     276 1 436 my ($self,$filename,$method) = @_;
45 276   33     1019 $method ||= $self->{Defaultmethod};
46              
47 276         501 my $signatureclass = "File::Modified::Signature::$method";
48 276         288 my $s = eval { $signatureclass->new($filename) };
  276         1255  
49 276 100       510 if (! $@) {
50 273         1582 return $self->{Files}->{$filename} = $s;
51             } else {
52             # retry and try Digest::$method
53              
54 3 100       9 if ($self->_make_digest_signature($method)) {
55 1         8 my $s = $signatureclass->new($filename);
56 1         7 return $self->{Files}->{$filename} = $s;
57             } else {
58 2         15 return undef;
59             };
60             };
61             };
62              
63             sub addfile {
64 27     27 1 2011 my ($self,@files) = @_;
65              
66 27         36 my @result;
67              
68             # We only return something if the caller wants it
69 27 100       62 if (defined wantarray) {
70 9         30 push @result, $self->add($_) for @files;
71 9         58 return @result;
72             } else {
73 18         59 $self->add($_) for @files;
74             };
75             };
76              
77             sub update {
78 0     0 1 0 my ($self) = @_;
79              
80 0         0 $_->initialize() for values %{$self->{Files}};
  0         0  
81             };
82              
83             sub changed {
84 11     11 1 2504 my ($self) = @_;
85              
86 11         21 return map {$_->{Filename}} grep {$_->changed()} (values %{$self->{Files}});
  4         42  
  250         6518  
  11         74  
87             };
88              
89             1;
90              
91             {
92             package File::Modified::Signature;
93              
94             # This is a case where Python would be nicer. With Python, we could have (paraphrased)
95             # class File::Modified::Signature;
96             # def initialize(self):
97             # self.hash = self.identificate()
98             # return self
99             # def signature(self):
100             # return MD5(self.filename)
101             # def changed(self):
102             # return self.hash != self.signature()
103             # and it would work as expected, (almost) regardless of the structure that is returned
104             # by self.signature(). This is some DWIMmery that I sometimes miss in Perl.
105             # For now, only string comparisions are allowed.
106              
107             sub create {
108 277     277   314 my ($class,$filename,$signature) = @_;
109              
110 277         781 my $self = {
111             Filename => $filename,
112             Signature => $signature,
113             };
114              
115 277         835 bless $self, $class;
116             };
117              
118             sub new {
119 274     274   332 my ($class,$filename) = @_;
120              
121 274         703 my $self = $class->create($filename);
122 274         694 $self->initialize();
123              
124 274         514 return $self;
125             };
126              
127             sub initialize {
128 274     274   302 my ($self) = @_;
129 274         593 $self->{Signature} = $self->signature();
130 274         358 return $self;
131             };
132              
133             sub from_scalar {
134 3     3   22 my ($baseclass,$scalar) = @_;
135 3 50       25 die "Strange value in from_scalar: $scalar\n" unless $scalar =~ /^([^|]+)\|([^|]+)\|(.+)$/;
136 3         15 my ($class,$filename,$signature) = ($1,$2,$3);
137 3         28 return $class->create($filename,$signature);
138             };
139              
140             sub as_scalar {
141 3     3   29 my ($self) = @_;
142 3         18 return ref($self) . "|" . $self->{Filename} . "|" . $self->{Signature};
143             };
144              
145             sub changed {
146 250     250   292 my ($self) = @_;
147 250         402 my $currsig = $self->signature();
148              
149             # FIXME: Deep comparision of the two signatures instead of equality !
150             # And what's this about string comparisions anyway ?
151 250 50 33     1347 if ((ref $currsig) or (ref $self->{Signature})) {
152 0         0 die "Implementation error in $self : changed() can't handle references and complex structures (yet) !\n";
153             #return $currsig != $self->{Signature};
154             } else {
155 250         1082 return $currsig ne $self->{Signature};
156             };
157             };
158             };
159              
160             {
161             package File::Modified::Signature::mtime;
162 1     1   8 use base 'File::Modified::Signature';
  1         2  
  1         878  
163              
164             sub signature {
165 174     174   174 my ($self) = @_;
166              
167 174 50       5156 my @stat = stat $self->{Filename} or die "Couldn't stat '$self->{Filename}' : $!";
168              
169 174         464 return $stat[9];
170             };
171             };
172              
173             {
174             package File::Modified::Signature::Checksum;
175 1     1   8 use base 'File::Modified::Signature';
  1         2  
  1         653  
176              
177             sub signature {
178 174     174   206 my ($self) = @_;
179 174         154 my $result;
180 174 50 33     10215 if (-e $self->{Filename} and -r $self->{Filename}) {
181 174         390 local *F;
182 174 50       7395 open F, $self->{Filename} or die "Couldn't read from file '$self->{Filename}' : $!";
183 174         383 binmode F;
184              
185 174         177 my $buf;
186 174         3570 while (read(F,$buf,32768)) {
187 208         11974 $result += unpack("%32C*", $buf);
188 208         1476 $result %= 0xFFFFFFFF;
189             };
190              
191 174         1761 close F;
192             };
193 174         382 return $result;
194             };
195             };
196              
197             {
198             package File::Modified::Signature::Digest;
199 1     1   6 use base 'File::Modified::Signature';
  1         2  
  1         1312  
200              
201             sub digestname {
202 92     92   163 my ($class) = @_;
203 92   33     215 $class = ref $class || $class;
204 92 50       628 return $1 if ($class =~ /^File::Modified::Signature::([^:]+)$/);
205             };
206              
207             sub digest {
208 176     176   201 my ($self) = @_;
209 176 100       378 if (! exists $self->{Digest}) {
210 92         293 my $digestclass = "Digest::" . $self->digestname;
211 1     1   5 eval "use $digestclass";
  1     1   1  
  1     1   19  
  1     1   7  
  1     1   1  
  1     1   37  
  1     1   4  
  1     1   1  
  1     1   21  
  1     1   5  
  1     1   2  
  1     1   21  
  1     1   5  
  1     1   2  
  1     1   27  
  1     1   5  
  1     1   2  
  1     1   21  
  1     1   4  
  1     1   2  
  1     1   22  
  1     1   4  
  1     1   1  
  1     1   21  
  1     1   5  
  1     1   1  
  1     1   21  
  1     1   6  
  1     1   3  
  1     1   28  
  1     1   6  
  1     1   2  
  1     1   35  
  1     1   6  
  1     1   2  
  1     1   26  
  1     1   5  
  1     1   1  
  1     1   36  
  1     1   5  
  1     1   2  
  1     1   24  
  1     1   4  
  1     1   1  
  1     1   20  
  1     1   5  
  1     1   2  
  1     1   20  
  1     1   4  
  1     1   3  
  1     1   23  
  1     1   7  
  1     1   2  
  1     1   25  
  1     1   6  
  1     1   3  
  1     1   29  
  1     1   12  
  1     1   2  
  1     1   29  
  1     1   6  
  1     1   2  
  1     1   29  
  1     1   5  
  1     1   2  
  1     1   22  
  1     1   6  
  1     1   2  
  1     1   31  
  1     1   4  
  1     1   2  
  1     1   21  
  1     1   5  
  1     1   1  
  1     1   20  
  1     1   5  
  1     1   2  
  1     1   24  
  1     1   4  
  1     1   2  
  1     1   25  
  1     1   5  
  1     1   2  
  1     1   38  
  1     1   5  
  1     1   1  
  1     1   53  
  1     1   4  
  1     1   2  
  1     1   19  
  1     1   5  
  1     1   2  
  1         30  
  1         6  
  1         2  
  1         28  
  1         6  
  1         2  
  1         26  
  1         6  
  1         2  
  1         26  
  1         6  
  1         1  
  1         23  
  1         5  
  1         1  
  1         25  
  1         4  
  1         2  
  1         22  
  1         5  
  1         1  
  1         43  
  1         5  
  1         2  
  1         26  
  1         6  
  1         2  
  1         28  
  1         7  
  1         3  
  1         25  
  1         5  
  1         2  
  1         22  
  1         6  
  1         2  
  1         25  
  1         5  
  1         10  
  1         23  
  1         6  
  1         3  
  1         29  
  1         5  
  1         3  
  1         29  
  1         5  
  1         2  
  1         22  
  1         4  
  1         3  
  1         23  
  1         4  
  1         4  
  1         20  
  1         5  
  1         3  
  1         21  
  1         7  
  1         2  
  1         24  
  1         5  
  1         3  
  1         22  
  1         4  
  1         2  
  1         19  
  1         5  
  1         1  
  1         24  
  1         5  
  1         1  
  1         21  
  1         6  
  1         1  
  1         26  
  1         4  
  1         2  
  1         20  
  1         5  
  1         2  
  1         23  
  1         5  
  1         2  
  1         20  
  1         5  
  1         2  
  1         20  
  1         5  
  1         2  
  1         21  
  1         5  
  1         2  
  1         19  
  1         5  
  1         2  
  1         21  
  1         5  
  1         1  
  1         21  
  1         5  
  1         1  
  1         24  
  1         6  
  1         2  
  1         33  
  1         6  
  1         1  
  1         25  
  1         6  
  1         1  
  1         30  
  1         6  
  1         2  
  1         23  
  1         5  
  1         1  
  1         21  
  1         6  
  1         2  
  1         28  
  1         6  
  1         2  
  1         26  
  1         5  
  1         1  
  1         23  
  1         6  
  1         2  
  1         23  
  1         5  
  1         3  
  1         21  
  1         5  
  1         1  
  1         21  
  1         4  
  1         1  
  1         21  
  1         5  
  1         2  
  1         23  
  1         7  
  1         2  
  1         26  
  1         6  
  1         2  
  1         23  
  1         5  
  1         1  
  1         20  
  1         4  
  1         2  
  1         20  
  1         9  
  1         2  
  1         45  
  1         7  
  1         2  
  1         35  
  1         6  
  1         3  
  1         29  
  1         6  
  1         1  
  1         27  
  1         5  
  1         3  
  1         26  
  1         6  
  1         2  
  1         24  
  1         7  
  1         2  
  1         27  
  1         8  
  1         2  
  1         36  
  1         14  
  1         5  
  1         61  
  1         9  
  1         2  
  1         45  
  92         5303  
212 92         538 $self->{Digest} = $digestclass->new();
213             };
214 176         15704 return $self->{Digest};
215             };
216              
217             sub signature {
218 176     176   187 my ($self) = @_;
219 176         156 my $result;
220 176 50 33     9558 if (-e $self->{Filename} and -r $self->{Filename}) {
221 176         348 local *F;
222 176 50       7184 open F, $self->{Filename} or die "Couldn't read from file '$self->{Filename}' : $!";
223 176         334 binmode F;
224 176         541 $result = $self->digest->addfile(*F)->b64digest();
225 176         1855 close F;
226             };
227 176         397 return $result;
228             };
229             };
230              
231             1;
232              
233             __END__