Catalyst::Restarter::Forking: clear watcher in child process
[catagits/Catalyst-Devel.git] / lib / Catalyst / Restarter.pm
CommitLineData
8462f41e 1package Catalyst::Restarter;
2
3use Moose;
4
ffbcd711 5use Cwd qw( abs_path );
a13b99da 6use File::ChangeNotify;
dbe481fe 7use File::Spec;
ffbcd711 8use FindBin;
9bece793 9use Catalyst::Utils;
8462f41e 10use namespace::clean -except => 'meta';
11
b8e3feb1 12has start_sub => (
8462f41e 13 is => 'ro',
14 isa => 'CodeRef',
15 required => 1,
16);
17
5ad5350a 18has argv => (
19 is => 'ro',
20 isa => 'ArrayRef',
21 required => 1,
22);
23
8462f41e 24has _watcher => (
835133d2 25 is => 'rw',
26 isa => 'File::ChangeNotify::Watcher',
27 clearer => '_clear_watcher',
8462f41e 28);
29
e45e5e8a 30has _filter => (
31 is => 'rw',
32 isa => 'RegexpRef',
33);
34
8462f41e 35has _child => (
36 is => 'rw',
37 isa => 'Int',
38);
39
2e9609df 40sub pick_subclass {
41 my $class = shift;
42
43 my $subclass;
44 $subclass =
45 defined $ENV{CATALYST_RESTARTER}
46 ? $ENV{CATALYST_RESTARTER}
47 : $^O eq 'MSWin32'
48 ? 'Win32'
49 : 'Forking';
50
51 $subclass = 'Catalyst::Restarter::' . $subclass;
52
9bece793 53 Catalyst::Utils::ensure_class_loaded($subclass);
2e9609df 54
55 return $subclass;
56}
57
8462f41e 58sub BUILD {
59 my $self = shift;
60 my $p = shift;
61
b8e3feb1 62 delete $p->{start_sub};
8462f41e 63
23f28bf0 64 $p->{filter} ||= qr/(?:\/|^)(?![.#_]).+(?:\.yml$|\.yaml$|\.conf|\.pm)$/;
dbe481fe 65
66 my $app_root = abs_path( File::Spec->catdir( $FindBin::Bin, '..' ) );
67
68 # Monitor application root dir
69 $p->{directories} ||= $app_root;
70
71 # exclude t/, root/ and hidden dirs
72 $p->{exclude} ||= [
73 File::Spec->catdir($app_root, 't'),
74 File::Spec->catdir($app_root, 'root'),
75 qr(/\.[^/]*/?$), # match hidden dirs
76 ];
a13b99da 77
c06769de 78 # keep filter regexp to make sure we don't restart on deleted
e45e5e8a 79 # files or directories where we can't check -d
80 $self->_filter( $p->{filter} );
81
8462f41e 82 # We could make this lazily, but this lets us check that we
83 # received valid arguments for the watcher up front.
a13b99da 84 $self->_watcher( File::ChangeNotify->instantiate_watcher( %{$p} ) );
8462f41e 85}
86
87sub run_and_watch {
88 my $self = shift;
89
90 $self->_fork_and_start;
91
92 return unless $self->_child;
93
94 $self->_restart_on_changes;
95}
96
8462f41e 97sub _restart_on_changes {
98 my $self = shift;
99
868361e8 100 # We use this loop in order to avoid having _handle_events() call back
101 # into this method. We used to do that, and the end result was that stack
0fde42f0 102 # traces became longer and longer with every restart. Using this loop, the
103 # portion of the stack trace that covers this code does not grow.
868361e8 104 while (1) {
105 my @events = $self->_watcher->wait_for_events();
106 $self->_handle_events(@events);
107 }
7f564068 108}
8462f41e 109
a13b99da 110sub _handle_events {
111 my $self = shift;
112 my @events = @_;
8462f41e 113
e45e5e8a 114 my @files;
c06769de 115 # Filter out any events which are the creation / deletion of directories
116 # so that creating an empty directory won't cause a restart
a13b99da 117 for my $event (@events) {
118 my $path = $event->path();
119 my $type = $event->type();
3a9fa16a 120 if ( ( ( $type ne 'delete' && -f $path )
121 || ( $type eq 'delete' )
122 )
123 && ( $path =~ $self->_filter )
124 ) {
e45e5e8a 125 push @files, { path => $path, type => $type };
126 }
a13b99da 127 }
128
e45e5e8a 129 if (@files) {
130 print STDERR "\n";
131 print STDERR "Saw changes to the following files:\n";
8462f41e 132
e45e5e8a 133 for my $f (@files) {
134 my $path = $f->{path};
135 my $type = $f->{type};
136 print STDERR " - $path ($type)\n";
137 }
8462f41e 138
e45e5e8a 139 print STDERR "\n";
140 print STDERR "Attempting to restart the server\n\n";
141
142 $self->_kill_child;
143
144 $self->_fork_and_start;
145 }
8462f41e 146}
147
02758cf8 148sub DEMOLISH {
149 my $self = shift;
150
151 $self->_kill_child;
152}
153
8462f41e 154__PACKAGE__->meta->make_immutable;
155
1561;
83d2d4a4 157
158__END__
159
160=head1 NAME
161
b8e3feb1 162Catalyst::Restarter - Uses File::ChangeNotify to check for changed files and restart the server
83d2d4a4 163
164=head1 SYNOPSIS
165
36ff1319 166 my $class = Catalyst::Restarter->pick_subclass;
167
168 my $restarter = $class->new(
b8e3feb1 169 directories => '/path/to/MyApp',
170 regex => '\.yml$|\.yaml$|\.conf|\.pm$',
171 start_sub => sub { ... }
83d2d4a4 172 );
173
b8e3feb1 174 $restarter->run_and_watch;
83d2d4a4 175
176=head1 DESCRIPTION
177
36ff1319 178This is the base class for all restarters, and it also provide
179functionality for picking an appropriate restarter subclass for a
180given platform.
181
b8e3feb1 182This class uses L<File::ChangeNotify> to watch one or more directories
183of files and restart the Catalyst server when any of those files
184changes.
83d2d4a4 185
186=head1 METHODS
187
36ff1319 188=head2 pick_subclass
189
190Returns the name of an appropriate subclass for the given platform.
191
b8e3feb1 192=head2 new ( start_sub => sub { ... }, ... )
193
36ff1319 194This method creates a new restarter object, but should be called on a
195subclass, not this class.
83d2d4a4 196
b8e3feb1 197The "start_sub" argument is required. This is a subroutine reference
198that can be used to start the Catalyst server.
83d2d4a4 199
b8e3feb1 200=head2 run_and_watch
83d2d4a4 201
b8e3feb1 202This method forks, starts the server in a child process, and then
203watched for changed files in the parent. When files change, it kills
204the child, forks again, and starts a new server.
83d2d4a4 205
206=head1 SEE ALSO
207
caa3831b 208L<Catalyst>, L<File::ChangeNotify>
83d2d4a4 209
210=head1 AUTHORS
211
212Catalyst Contributors, see Catalyst.pm
213
214=head1 COPYRIGHT
215
216This program is free software, you can redistribute it and/or modify
217it under the same terms as Perl itself.
218
219=cut