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