package File::Monitor::Simple; use strict; use warnings; use base 'Class::Accessor::Fast'; use File::Find::Rule; use File::Modified; use File::Spec; use vars '$VERSION'; $VERSION = '0.99_1'; __PACKAGE__->mk_accessors( qw/directory modified regex watch_list / ); # directory : scalar location to monitor # modified : File::Modified object with current state # regex : File pattern to monitor. Really a string a not "qr" # watch_list : hashref of monitored files. Keys are names. Values are "1" sub new { my ( $class, %args ) = @_; my $self = {%args}; bless $self, $class; $self->_init; return $self; } sub _init { my $self = shift; my $watch_list = $self->_index_directory; $self->watch_list($watch_list); $self->modified( File::Modified->new( # mtime works on directories, while 'MD5' doesn't method => 'mtime', files => [ keys %{$watch_list} ], ) ); } sub watch { my $self = shift; my @changes; my @changed_files; eval { @changes = $self->modified->changed }; if ($@) { # warn "error: $@"; # File::Modified will die if a file is deleted. my ($deleted_file) = $@ =~ /stat '(.+)'/; push @changed_files, $deleted_file || 'unknown file'; # re-scan to remove the deleted file $self->_init; } if (@changes) { # update all mtime information $self->modified->update; # check if any files were changed @changed_files = grep { defined $_ && -f $_ } @changes; # Check if only directories were changed. This means # a new file was created. if (@changed_files) { # We found some changed files. # Don't bother to look for added files, too. } else { # look through the new list for new files my $old_watch = $self->watch_list; # re-index to find new files $self->_init; @changed_files = grep { !defined $old_watch->{$_} } keys %{ $self->watch_list }; return unless @changed_files; } } else { # no changes found; } return @changed_files; } # =begin private # # =head2 _index_directory() # # $self->_index_directory; # # Looks in $self->directory and compares all files to $self->regex. # Matched files are registered for monitoring. # # =end private # # =cut sub _index_directory { my $self = shift; my $dir = $self->directory || die "No directory specified"; my $regex = $self->regex || '\.pm$'; # Find all the directories in this directory, as well as any files matching the regex my $iterator = File::Find::Rule->any( File::Find::Rule->file->name(qr/$regex/), File::Find::Rule->directory, )->start( $dir ); my %list; while ( my $match = $iterator->match ) { $list{$match} = 1 } return \%list; } 1; __END__ =head1 NAME File::Monitor::Simple - Watch for changed application files =head1 SYNOPSIS my $watcher = File::Monitor::Simple->new( directory => '/path/to/MyApp', regex => '\.yml$|\.yaml$|\.pm$', ); while (sleep 1) { my @changed_files = $watcher->watch; } =head1 DESCRIPTION This class monitors a directory of files for changes made to any file matching a regular expression. It correctly handles new files added to the application as well as files that are deleted. =head1 METHODS =head2 new ( directory => $path [, regex => $regex, delay => $delay ] ) Creates a new Watcher object. =head2 watch Returns a list of files that have been added, deleted, or changed since the last time watch was called. =head1 SEE ALSO L, L, L, L =head1 AUTHORS Sebastian Riedel, Andy Grundman, Mark Stosberg, =head1 COPYRIGHT This program is free software, you can redistribute it and/or modify it under the same terms as Perl itself. =cut