http://qs321.pair.com?node_id=159585

Corion has asked for the wisdom of the Perl Monks concerning the following question:

I have now a module that I think would be nice to have in many situations and thus I'm considering a release on the CPAN. The problem is, I don't know if the name I have (until now) is a good name, or if I reinvented the wheel because I didn't find a module that scratched this itch.

The module provides a simple interface to determine if a set of files changed since the last time they were checked. This is mostly intended for long running programs (in my case, a daemon process) that need to reinitialize if their configuration files, their program code or the code of any module they depend on (hence the name) changes.

The code itself is written (and even has 6 tests, which I haven't included as not to bloat this node even further), but I'd like some feedback on it, things that could be much easier, ideas for other methods of file signatures etc. - also, documentation stuff would be very appreciated, including correction of tyops.

As you most likely won't bother to delve into the module without something to whet your appetite, here are the two examples out of the synopsis. The first example is a long running process that checks from time to time if its configuration files have changed and then takes "appropriate action" :

use strict; use File::Dependencies; my $d = File::Dependencies->new(Files=>['Import.cfg','Export.cfg']); while (1) { my (@changes) = $d->changed; if (@changes) { print "$_ was changed\n" for @changes; $d->update(); }; sleep 60; };

The second example is an example of another long running process that restarts if any file of its source code (minus required files and done files) changed. What I want to know here is, if the idea of restarting a process like this is a good one to have in the documentation ...

use strict; use File::Dependencies; my $files = File::Dependencies->new(Files=>[values %INC, $0]); # We want to restart when any module was changed exec $0, @ARGV if $files->changed();

As you've now read this far, you most likely also want to have a look at the module itself - here it comes :

package File::Dependencies; #use 5.006; # shouldn't be necessary use strict; use warnings; require Exporter; our @ISA = qw(Exporter); our $VERSION = '0.01'; sub new { my ($class, %args) = @_; my $method = $args{Method} || "MD5"; my $files = $args{Files} || []; my $self = { Defaultmethod => $method, Files => {}, }; bless $self, $class; $self->addfile($_) for @$files; return $self; }; sub adddependency { my ($self,$filename,$method) = @_; $method ||= $self->{Defaultmethod}; my $signatureclass = "Dependency::Signature::$method"; $self->{Files}->{$filename} = $signatureclass->new($filename); }; sub addfile { my ($self,@files) = @_; $self->adddependency($_) for @files; }; sub update { my ($self) = @_; $_->initialize() for values %{$self->{Files}}; }; sub changed { my ($self) = @_; return map {$_->{Filename}} grep {$_->changed()} (values %{$self->{F +iles}}); }; 1; { package Dependency::Signature; # This is a case where Python would be nicer. With Python, we could +have (paraphrased) # class Dependency::Signature; # def initialize(self): # self.hash = self.identificate() # return self # def signature(self): # return MD5(self.filename) # def changed(self): # return self.hash != self.signature() # and it would work as expected, (almost) regardless of the structur +e that is returned # by self.signature(). This is some DWIMmery that I sometimes miss i +n Perl. # For now, only string comparisions are allowed. sub new { my ($class,$filename) = @_; my $self = { Filename => $filename, }; bless $self, $class; $self->initialize(); return $self; }; sub initialize { my ($self) = @_; $self->{Signature} = $self->signature(); return $self; }; sub changed { my ($self) = @_; my $currsig = $self->signature(); # FIXME: Deep comparision of the two signatures instead of equalit +y ! # And what's this about string comparisions anyway ? if ((ref $currsig) or (ref $self->{Signature})) { die "Implementation error in $self : changed() can't handle refe +rences (yet) !\n"; #return $currsig != $self->{Signature}; } else { return $currsig ne $self->{Signature}; }; }; 1; }; { package Dependency::Signature::mtime; use base 'Dependency::Signature'; sub signature { my ($self) = @_; my @stat = stat $self->{Filename} or die "Couldn't stat '$self->{F +ilename}' : $!"; return $stat[9]; }; 1; }; { package Dependency::Signature::MD5; use base 'Dependency::Signature'; use vars qw( $fallback ); BEGIN { eval "use Digest::MD5;"; if ($@) { #print "Falling back on Dependency::Signature::mtime\n"; $fallback = 1; }; }; # Fall back on simple mtime check unless MD5 is available : sub new { my ($class,$filename) = @_; if ($fallback) { return Dependency::Signature::mtime->new($filename); } else { return $class->SUPER::new($filename); }; }; sub signature { my ($self) = @_; my $result; if (-e $self->{Filename} and -r $self->{Filename}) { local *F; open F, $self->{Filename} or die "Couldn't read from file '$self +->{Filename}' : $!"; $result = Digest::MD5->new()->addfile(*F)->b64digest(); close F; }; return $result; }; 1; }; 1; __END__ =head1 NAME File::Dependencies - Perl extension for detection of changed files. =head1 SYNOPSIS use strict; use File::Dependencies; my $d = File::Dependencies->new(Files=>['Import.cfg','Export.cfg']); while (1) { my (@changes) = $d->changed; if (@changes) { print "$_ was changed\n" for @changes; $d->update(); }; sleep 60; }; Second example - a script that knows when any of its modules have chan +ged : use File::Dependencies; my $files = File::Dependencies->new(Files=>[values %INC, $0]); # We want to restart when any module was changed exec $0, @ARGV if $files->changed(); =head1 DESCRIPTION The Dependencies module is intended as a simple method for programs to + detect whether configuration files (or modules they rely on) have changed. Th +ere are currently two methods of change detection implemented, C<mtime> and C< +MD5>. The C<MD5> method will fall back to use timestamps if the C<Digest::MD +5> module cannot be loaded. =over 4 =item new %ARGS Creates a new instance. The C<%ARGS> hash has two possible keys, C<Method>, which denotes the method used for checking as default, and C<Files>, which takes an array reference to the filenames to watch. =item adddependency filename, method Adds a new file to watch. C<method> is the method (or rather, the subclass of C<Dependency::Signature>) to use to determine whether a file has changed or not. =item addfile LIST Adds a list of files to watch. The method used for watching is t1he default method as set in the constructor. =item update Updates all signatures to the current state. All pending changes are discarded. =item changed Returns a list of the filenames whose files did change since the construction or the last call to C<update> (whichever last occurred). =back =head2 Adding new methods for signatures Adding a new signature method is as simple as creating a new subclass of C<Dependency::Signature>. See C<Dependency::Signature::MD5> for a s +imple example. There is one point of lazyness in the implementation of C<Dep +endency::Signature>, the C<check> method can only compare strings instead of arbitrary stru +ctures (yes, there ARE things that are easier in Python than in Perl). =head2 EXPORT None by default. =head1 AUTHOR Max Maischein, E<lt>corion@informatik.uni-frankfurt.deE<gt> =head1 SEE ALSO L<perl>,L<Digest::MD5>. =cut
perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ; # The $d = new HTTP::Daemon and fork and getprint $d->url and exit;#spider ($c = $d->accept())->get_request(); $c->send_response( new #in the HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web