1 package Catalyst::Engine::HTTP::Restarter::Watcher;
4 with 'MooseX::Emulate::Class::Accessor::Fast';
9 use Time::HiRes qw/sleep/;
10 use Moose::Util qw/find_meta/;
11 use namespace::clean -except => 'meta';
14 # If we can detect stash changes, then we do magic
15 # to make their metaclass mutable (if they have one)
16 # so that restarting works as expected.
17 eval { require B::Hooks::OP::Check::StashChange; };
18 *DETECT_PACKAGE_COMPILATION = $@
23 has delay => (is => 'rw');
24 has regex => (is => 'rw');
25 has modified => (is => 'rw');
26 has directory => (is => 'rw');
27 has watch_list => (is => 'rw');
28 has follow_symlinks => (is => 'rw');
37 my $watch_list = $self->_index_directory;
38 $self->watch_list($watch_list);
43 files => [ keys %{$watch_list} ],
54 my $delay = ( defined $self->delay ) ? $self->delay : 1;
56 sleep $delay if $delay > 0;
58 eval { @changes = $self->modified->changed };
61 # File::Modified will die if a file is deleted.
62 my ($deleted_file) = $@ =~ /stat '(.+)'/;
63 push @changed_files, $deleted_file || 'unknown file';
68 # update all mtime information
69 $self->modified->update;
71 # check if any files were changed
72 @changed_files = grep { -f $_ } @changes;
74 # Check if only directories were changed. This means
75 # a new file was created.
76 unless (@changed_files) {
78 # re-index to find new files
79 my $new_watch = $self->_index_directory;
81 # look through the new list for new files
82 my $old_watch = $self->watch_list;
83 @changed_files = grep { !defined $old_watch->{$_} }
86 return unless @changed_files;
90 for my $file (@changed_files) {
91 next unless $file =~ /\.pm$/;
92 if ( my $error = $self->_test($file) ) {
93 print STDERR qq/File "$file" modified, not restarting\n\n/;
94 print STDERR '*' x 80, "\n";
96 print STDERR '*' x 80, "\n";
102 return @changed_files;
105 sub _index_directory {
108 my $dir = $self->directory;
109 die "No directory specified" if !$dir or ref($dir) && !@{$dir};
111 my $regex = $self->regex || '\.pm$';
117 my $file = File::Spec->rel2abs($File::Find::name);
118 return unless $file =~ /$regex/;
119 return unless -f $file;
120 $file =~ s{/script/..}{};
123 # also watch the directory for changes
124 my $cur_dir = File::Spec->rel2abs($File::Find::dir);
125 $cur_dir =~ s{/script/..}{};
128 follow_fast => $self->follow_symlinks ? 1 : 0,
131 ref $dir eq 'ARRAY' ? @{$dir} : $dir
137 my ( $self, $file ) = @_;
140 if (DETECT_PACKAGE_COMPILATION) {
141 $id = B::Hooks::OP::Check::StashChange::register(sub {
142 my ($new, $old) = @_;
143 my $meta = find_meta($new);
145 $meta->make_mutable if $meta->is_immutable;
150 delete $INC{$file}; # Remove from %INC so it will reload
151 local $SIG{__WARN__} = sub { };
153 open my $olderr, '>&STDERR';
154 open STDERR, '>', File::Spec->devnull;
155 eval "require '$file'";
156 open STDERR, '>&', $olderr;
158 B::Hooks::OP::Check::StashChange::unregister($id) if $id;
160 return ($@) ? $@ : 0;
168 Catalyst::Engine::HTTP::Restarter::Watcher - Watch for changed application
173 my $watcher = Catalyst::Engine::HTTP::Restarter::Watcher->new(
174 directory => '/path/to/MyApp',
175 regex => '\.yml$|\.yaml$|\.conf|\.pm$',
180 my @changed_files = $watcher->watch();
185 This class monitors a directory of files for changes made to any file
186 matching a regular expression. It correctly handles new files added to the
187 application as well as files that are deleted.
191 =head2 new ( directory => $path [, regex => $regex, delay => $delay ] )
193 Creates a new Watcher object.
197 Returns a list of files that have been added, deleted, or changed since the
198 last time watch was called.
202 L<Catalyst>, L<Catalyst::Engine::HTTP::Restarter>, L<File::Modified>
206 Catalyst Contributors, see Catalyst.pm
210 Many parts are ripped out of C<HTTP::Server::Simple> by Jesse Vincent.
214 This program is free software, you can redistribute it and/or modify it under
215 the same terms as Perl itself.