Reformatted documentation
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Engine / HTTP / Restarter / Watcher.pm
CommitLineData
65586a18 1package Catalyst::Engine::HTTP::Restarter::Watcher;
2
3use strict;
4use warnings;
5use base 'Class::Accessor::Fast';
6use File::Find;
7use File::Modified;
8use File::Spec;
9use Time::HiRes qw/sleep/;
10
b5ecfcf0 11__PACKAGE__->mk_accessors(
12 qw/delay
13 directory
14 modified
15 regex
16 watch_list/
17);
65586a18 18
19sub new {
20 my ( $class, %args ) = @_;
b5ecfcf0 21
22 my $self = {%args};
23
65586a18 24 bless $self, $class;
b5ecfcf0 25
65586a18 26 $self->_init;
b5ecfcf0 27
65586a18 28 return $self;
29}
30
31sub _init {
32 my $self = shift;
b5ecfcf0 33
65586a18 34 my $watch_list = $self->_index_directory;
b5ecfcf0 35 $self->watch_list($watch_list);
36
65586a18 37 $self->modified(
38 File::Modified->new(
39 method => 'mtime',
40 files => [ keys %{$watch_list} ],
41 )
42 );
43}
44
45sub watch {
46 my $self = shift;
b5ecfcf0 47
65586a18 48 my @changes;
49 my @changed_files;
b5ecfcf0 50
65586a18 51 sleep $self->delay || 1;
b5ecfcf0 52
65586a18 53 eval { @changes = $self->modified->changed };
b5ecfcf0 54 if ($@) {
55
65586a18 56 # File::Modified will die if a file is deleted.
57 my ($deleted_file) = $@ =~ /stat '(.+)'/;
58 push @changed_files, $deleted_file || 'unknown file';
59 }
b5ecfcf0 60
61 if (@changes) {
62
65586a18 63 # update all mtime information
64 $self->modified->update;
b5ecfcf0 65
65586a18 66 # check if any files were changed
67 @changed_files = grep { -f $_ } @changes;
b5ecfcf0 68
65586a18 69 # Check if only directories were changed. This means
70 # a new file was created.
b5ecfcf0 71 unless (@changed_files) {
72
65586a18 73 # re-index to find new files
74 my $new_watch = $self->_index_directory;
b5ecfcf0 75
65586a18 76 # look through the new list for new files
77 my $old_watch = $self->watch_list;
b5ecfcf0 78 @changed_files = grep { !defined $old_watch->{$_} }
79 keys %{$new_watch};
80
65586a18 81 return unless @changed_files;
82 }
83
84 # Test modified pm's
b5ecfcf0 85 for my $file (@changed_files) {
65586a18 86 next unless $file =~ /\.pm$/;
87 if ( my $error = $self->_test($file) ) {
b5ecfcf0 88 print STDERR qq/File "$file" modified, not restarting\n\n/;
65586a18 89 print STDERR '*' x 80, "\n";
90 print STDERR $error;
91 print STDERR '*' x 80, "\n";
92 return;
93 }
94 }
95 }
b5ecfcf0 96
65586a18 97 return @changed_files;
98}
99
100sub _index_directory {
101 my $self = shift;
b5ecfcf0 102
103 my $dir = $self->directory || die "No directory specified";
104 my $regex = $self->regex || '\.pm$';
65586a18 105 my %list;
b5ecfcf0 106
65586a18 107 finddepth(
108 {
109 wanted => sub {
110 my $file = File::Spec->rel2abs($File::Find::name);
111 return unless $file =~ /$regex/;
112 return unless -f $file;
113 $file =~ s{/script/..}{};
114 $list{$file} = 1;
b5ecfcf0 115
65586a18 116 # also watch the directory for changes
117 my $cur_dir = File::Spec->rel2abs($File::Find::dir);
b5ecfcf0 118 $cur_dir =~ s{/script/..}{};
65586a18 119 $list{$cur_dir} = 1;
120 },
121 no_chdir => 1
122 },
123 $dir
124 );
125 return \%list;
126}
127
128sub _test {
129 my ( $self, $file ) = @_;
b5ecfcf0 130
65586a18 131 delete $INC{$file};
132 local $SIG{__WARN__} = sub { };
b5ecfcf0 133
65586a18 134 open my $olderr, '>&STDERR';
135 open STDERR, '>', File::Spec->devnull;
136 eval "require '$file'";
137 open STDERR, '>&', $olderr;
b5ecfcf0 138
65586a18 139 return ($@) ? $@ : 0;
b5ecfcf0 140}
65586a18 141
1421;
143__END__
144
145=head1 NAME
146
147Catalyst::Engine::HTTP::Restarter::Watcher - Watch for changed application
148files
149
150=head1 SYNOPSIS
151
152 my $watcher = Catalyst::Engine::HTTP::Restarter::Watcher->new(
153 directory => '/path/to/MyApp',
154 regex => '\.yml$|\.yaml$|\.pm$',
155 delay => 1,
156 );
157
158 while (1) {
159 my @changed_files = $watcher->watch();
160 }
161
162=head1 DESCRIPTION
163
164This class monitors a directory of files for changes made to any file
165matching a regular expression. It correctly handles new files added to the
166application as well as files that are deleted.
167
168=head1 METHODS
169
170=head2 new ( directory => $path [, regex => $regex, delay => $delay ] )
171
172Creates a new Watcher object.
173
174=head2 watch
175
176Returns a list of files that have been added, deleted, or changed since the
177last time watch was called.
178
179=head1 SEE ALSO
180
181L<Catalyst>, L<Catalyst::Engine::HTTP::Restarter>, L<File::Modified>
182
183=head1 AUTHORS
184
185Sebastian Riedel, <sri@cpan.org>
186
187Andy Grundman, <andy@hybridized.org>
188
189=head1 THANKS
190
191Many parts are ripped out of C<HTTP::Server::Simple> by Jesse Vincent.
192
193=head1 COPYRIGHT
194
195This program is free software, you can redistribute it and/or modify it under
196the same terms as Perl itself.
197
198=cut