requires 'Catalyst::Plugin::ConfigLoader';
requires 'Class::Accessor::Fast';
requires 'Config::General'; # as of 1.07, we use .conf and not .yaml
+requires 'File::ChangeNotify';
requires 'File::Copy::Recursive';
requires 'Module::Install' => '0.64';
requires 'parent'; # as of 1.04
if $follow_symlinks;
$args{directories} = $watch_directory
if defined $watch_directory;
- $args{interval} = $check_interval
+ $args{sleep_interval} = $check_interval
if defined $check_interval;
- $args{regex} = qr/$file_regex/
+ $args{filter} = qr/$file_regex/
if defined $file_regex;
my $restarter = Catalyst::Restarter->new(
use Moose;
-use Catalyst::Watcher;
+use File::ChangeNotify;
use namespace::clean -except => 'meta';
has restart_sub => (
has _watcher => (
is => 'rw',
- isa => 'Catalyst::Watcher',
+ isa => 'File::ChangeNotify::Watcher',
);
has _child => (
delete $p->{restart_sub};
+ $p->{filter} ||= qr/(?:\/|^)(?!\.\#).+(?:\.yml$|\.yaml$|\.conf|\.pm)$/;
+
# We could make this lazily, but this lets us check that we
# received valid arguments for the watcher up front.
- $self->_watcher( Catalyst::Watcher->instantiate_subclass( %{$p} ) );
+ $self->_watcher( File::ChangeNotify->instantiate_watcher( %{$p} ) );
}
sub run_and_watch {
sub _restart_on_changes {
my $self = shift;
- $self->_watcher->watch($self);
+ my @events = $self->_watcher->wait_for_events();
+ $self->_handle_events(@events);
}
-sub handle_changes {
- my $self = shift;
- my @files = @_;
+sub _handle_events {
+ my $self = shift;
+ my @events = @_;
print STDERR "\n";
print STDERR "Saw changes to the following files:\n";
- print STDERR " - $_->{file} ($_->{status})\n" for @files;
+
+ for my $event (@events) {
+ my $path = $event->path();
+ my $type = $event->type();
+
+ print STDERR " - $path ($type)\n";
+ }
+
print STDERR "\n";
print STDERR "Attempting to restart the server\n\n";
+++ /dev/null
-package Catalyst::Watcher;
-
-use Moose;
-use Moose::Util::TypeConstraints;
-
-use Cwd qw( abs_path );
-use File::Spec;
-use FindBin;
-use namespace::clean -except => 'meta';
-
-has regex => (
- is => 'ro',
- isa => 'RegexpRef',
- default => sub { qr/(?:\/|^)(?!\.\#).+(?:\.yml|\.yaml|\.conf|\.pm)$/ },
-);
-
-my $dir = subtype
- as 'Str'
- => where { -d $_ }
- => message { "$_ is not a valid directory" };
-
-my $array_of_dirs = subtype
- as 'ArrayRef[Str]',
- => where { map { -d } @{$_} }
- => message { "@{$_} is not a list of valid directories" };
-
-coerce $array_of_dirs
- => from $dir
- => via { [ $_ ] };
-
-has directories => (
- is => 'ro',
- isa => $array_of_dirs,
- default => sub { [ abs_path( File::Spec->catdir( $FindBin::Bin, '..' ) ) ] },
- coerce => 1,
-);
-
-has follow_symlinks => (
- is => 'ro',
- isa => 'Bool',
- default => 0,
-);
-
-sub instantiate_subclass {
- my $class = shift;
-
- if ( eval { require Catalyst::Watcher::Inotify; 1; } ) {
- return Catalyst::Watcher::Inotify->new(@_);
- }
- else {
- die $@ if $@ && $@ !~ /Can't locate/;
- require Catalyst::Watcher::FileModified;
- return Catalyst::Watcher::FileModified->new(@_);
- }
-}
-
-__PACKAGE__->meta->make_immutable;
-
-1;
+++ /dev/null
-package Catalyst::Watcher::FileModified;
-
-use Moose;
-
-use File::Find;
-use File::Modified;
-use File::Spec;
-use Time::HiRes qw/sleep/;
-use namespace::clean -except => 'meta';
-
-extends 'Catalyst::Watcher';
-
-has interval => (
- is => 'ro',
- isa => 'Int',
- default => 1,
-);
-
-has _watched_files => (
- is => 'ro',
- isa => 'HashRef[Str]',
- lazy_build => 1,
- clearer => '_clear_watched_files',
-);
-
-has _modified => (
- is => 'rw',
- isa => 'File::Modified',
- lazy_build => 1,
- clearer => '_clear_modified',
-);
-
-
-sub _build__watched_files {
- my $self = shift;
-
- my $regex = $self->regex;
-
- my %list;
- finddepth(
- {
- wanted => sub {
- my $path = File::Spec->rel2abs($File::Find::name);
- return unless $path =~ /$regex/;
- return unless -f $path;
-
- $list{$path} = 1;
-
- # also watch the directory for changes
- my $cur_dir = File::Spec->rel2abs($File::Find::dir);
- $cur_dir =~ s{/script/..}{};
- $list{$cur_dir} = 1;
- },
- follow_fast => $self->follow_symlinks ? 1 : 0,
- no_chdir => 1
- },
- @{ $self->directories }
- );
-
- return \%list;
-}
-
-sub _build__modified {
- my $self = shift;
-
- return File::Modified->new(
- method => 'mtime',
- files => [ keys %{ $self->_watched_files } ],
- );
-}
-
-sub watch {
- my $self = shift;
- my $restarter = shift;
-
- while (1) {
- sleep $self->interval if $self->interval > 0;
-
- my @changes = $self->_changed_files;
-
- next unless @changes;
-
- $restarter->handle_changes(@changes);
-
- last;
- }
-}
-
-sub _changed_files {
- my $self = shift;
-
- my @changes;
-
- eval {
- @changes = map { { file => $_, status => 'modified' } }
- grep { -f $_ } $self->_modified->changed;
- };
-
- if ($@) {
- # File::Modified will die if a file is deleted.
- die unless $@ =~ /stat '(.+)'/;
-
- push @changes, {
- file => $1 || 'unknown file',
- status => 'deleted',
- };
-
- $self->_clear_watched_files;
- $self->_clear_modified;
- }
- else {
- $self->_modified->update;
-
- my $old_watch = $self->_watched_files;
-
- $self->_clear_watched_files;
-
- my $new_watch = $self->_watched_files;
-
- my @new_files = grep { !defined $old_watch->{$_} }
- grep {-f}
- keys %{$new_watch};
-
- if (@new_files) {
- $self->_clear_modified;
- push @changes, map { { file => $_, status => 'added' } } @new_files;
- }
- }
-
- return @changes;
-}
-
-__PACKAGE__->meta->make_immutable;
-
-1;
-
-__END__
-
-=head1 NAME
-
-Catalyst::Watcher::FileModified - Watch for changed application files using File::Modified
-
-=head1 SYNOPSIS
-
- my $watcher = Catalyst::Watcher::FileModified->new(
- directories => '/path/to/MyApp',
- regex => '\.yml$|\.yaml$|\.conf|\.pm$',
- );
-
- while (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 find_changed_files
-
-Returns a list of files that have been added, deleted, or changed
-since the last time watch was called. Each element returned is a hash
-reference with two keys. The C<file> key contains the filename, and
-the C<status> key contains one of "modified", "added", or "deleted".
-
-=head1 SEE ALSO
-
-L<Catalyst>, L<Catalyst::Watcher>, L<Catalyst::Restarter>,
-<File::Modified>
-
-=head1 AUTHORS
-
-Catalyst Contributors, see Catalyst.pm
-
-=head1 COPYRIGHT
-
-This program is free software, you can redistribute it and/or modify
-it under the same terms as Perl itself.
-
-=cut
+++ /dev/null
-package Catalyst::Watcher::Inotify;
-
-use Moose;
-
-use File::Find;
-use Linux::Inotify2;
-use namespace::clean -except => 'meta';
-
-extends 'Catalyst::Watcher';
-
-has _inotify => (
- is => 'rw',
- isa => 'Linux::Inotify2',
- default => sub { Linux::Inotify2->new },
- init_arg => undef,
-);
-
-has _mask => (
- is => 'rw',
- isa => 'Int',
- lazy_build => 1,
-);
-
-sub BUILD {
- my $self = shift;
-
- # If this is done via a lazy_build then the call to
- # ->_add_directory ends up causing endless recursion when it calls
- # ->_inotify itself.
- $self->_add_directory($_) for @{ $self->directories };
-
- return $self;
-}
-
-sub watch {
- my $self = shift;
- my $restarter = shift;
-
- my @events = $self->_wait_for_events;
-
- $restarter->handle_changes( map { $self->_event_to_change($_) } @events );
-
- return;
-}
-
-sub _wait_for_events {
- my $self = shift;
-
- my $regex = $self->regex;
-
- while (1) {
- # This is a blocking read, so it will not return until
- # something happens. The restarter will end up calling ->watch
- # again after handling the changes.
- my @events = $self->_inotify->read;
-
- my @interesting;
- for my $event (@events) {
- if ( $event->IN_CREATE && $event->IN_ISDIR ) {
- $self->_add_directory( $event->fullname );
- push @interesting, $event;
- }
- elsif ( $event->IN_DELETE_SELF
- || $event->fullname =~ /$regex/ ) {
- push @interesting, $event;
- }
- }
-
- return @interesting if @interesting;
- }
-}
-
-sub _build__mask {
- my $self = shift;
-
- my $mask = IN_MODIFY | IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MOVE_SELF;
- $mask |= IN_DONT_FOLLOW unless $self->follow_symlinks;
-
- return $mask;
-}
-
-sub _add_directory {
- my $self = shift;
- my $dir = shift;
-
- finddepth(
- {
- wanted => sub {
- my $path = File::Spec->rel2abs($File::Find::name);
- return unless -d $path;
-
- $self->_inotify->watch( $path, $self->_mask );
- },
- follow_fast => $self->follow_symlinks ? 1 : 0,
- no_chdir => 1
- },
- $dir
- );
-}
-
-sub _event_to_change {
- my $self = shift;
- my $event = shift;
-
- my %change = ( file => $event->fullname );
- if ( $event->IN_CREATE ) {
- $change{status} = 'added';
- }
- elsif ( $event->IN_MODIFY ) {
- $change{status} = 'modified';
- }
- elsif ( $event->IN_DELETE ) {
- $change{status} = 'deleted';
- }
- else {
- $change{status} = 'containing directory modified';
- }
-
- return \%change;
-}
-
-__PACKAGE__->meta->make_immutable;
-
-1;
-
-__END__
-
-=head1 NAME
-
-Catalyst::Watcher - Watch for changed application files
-
-=head1 SYNOPSIS
-
- my $watcher = Catalyst::Watcher->new(
- directory => '/path/to/MyApp',
- regex => '\.yml$|\.yaml$|\.conf|\.pm$',
- interval => 3,
- );
-
- while (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 find_changed_files
-
-Returns a list of files that have been added, deleted, or changed
-since the last time watch was called. Each element returned is a hash
-reference with two keys. The C<file> key contains the filename, and
-the C<status> key contains one of "modified", "added", or "deleted".
-
-=head1 SEE ALSO
-
-L<Catalyst>, L<Catalyst::Restarter>, <File::Modified>
-
-=head1 AUTHORS
-
-Catalyst Contributors, see Catalyst.pm
-
-=head1 COPYRIGHT
-
-This program is free software, you can redistribute it and/or modify
-it under the same terms as Perl itself.
-
-=cut