Catalyst::Utils is not an exporter
[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 => (
25 is => 'rw',
a13b99da 26 isa => 'File::ChangeNotify::Watcher',
8462f41e 27);
28
e45e5e8a 29has _filter => (
30 is => 'rw',
31 isa => 'RegexpRef',
32);
33
8462f41e 34has _child => (
35 is => 'rw',
36 isa => 'Int',
37);
38
2e9609df 39sub pick_subclass {
40 my $class = shift;
41
42 my $subclass;
43 $subclass =
44 defined $ENV{CATALYST_RESTARTER}
45 ? $ENV{CATALYST_RESTARTER}
46 : $^O eq 'MSWin32'
47 ? 'Win32'
48 : 'Forking';
49
50 $subclass = 'Catalyst::Restarter::' . $subclass;
51
9bece793 52 Catalyst::Utils::ensure_class_loaded($subclass);
2e9609df 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
c06769de 77 # keep filter regexp to make sure we don't restart on deleted
e45e5e8a 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;
c06769de 114 # Filter out any events which are the creation / deletion of directories
115 # so that creating an empty directory won't cause a restart
a13b99da 116 for my $event (@events) {
117 my $path = $event->path();
118 my $type = $event->type();
e45e5e8a 119 if ( ( $type ne 'delete' && -f $path )
120 || ( $type eq 'delete' && $path =~ $self->_filter ) )
121 {
122 push @files, { path => $path, type => $type };
123 }
a13b99da 124 }
125
e45e5e8a 126 if (@files) {
127 print STDERR "\n";
128 print STDERR "Saw changes to the following files:\n";
8462f41e 129
e45e5e8a 130 for my $f (@files) {
131 my $path = $f->{path};
132 my $type = $f->{type};
133 print STDERR " - $path ($type)\n";
134 }
8462f41e 135
e45e5e8a 136 print STDERR "\n";
137 print STDERR "Attempting to restart the server\n\n";
138
139 $self->_kill_child;
140
141 $self->_fork_and_start;
142 }
8462f41e 143}
144
02758cf8 145sub DEMOLISH {
146 my $self = shift;
147
148 $self->_kill_child;
149}
150
8462f41e 151__PACKAGE__->meta->make_immutable;
152
1531;
83d2d4a4 154
155__END__
156
157=head1 NAME
158
b8e3feb1 159Catalyst::Restarter - Uses File::ChangeNotify to check for changed files and restart the server
83d2d4a4 160
161=head1 SYNOPSIS
162
36ff1319 163 my $class = Catalyst::Restarter->pick_subclass;
164
165 my $restarter = $class->new(
b8e3feb1 166 directories => '/path/to/MyApp',
167 regex => '\.yml$|\.yaml$|\.conf|\.pm$',
168 start_sub => sub { ... }
83d2d4a4 169 );
170
b8e3feb1 171 $restarter->run_and_watch;
83d2d4a4 172
173=head1 DESCRIPTION
174
36ff1319 175This is the base class for all restarters, and it also provide
176functionality for picking an appropriate restarter subclass for a
177given platform.
178
b8e3feb1 179This class uses L<File::ChangeNotify> to watch one or more directories
180of files and restart the Catalyst server when any of those files
181changes.
83d2d4a4 182
183=head1 METHODS
184
36ff1319 185=head2 pick_subclass
186
187Returns the name of an appropriate subclass for the given platform.
188
b8e3feb1 189=head2 new ( start_sub => sub { ... }, ... )
190
36ff1319 191This method creates a new restarter object, but should be called on a
192subclass, not this class.
83d2d4a4 193
b8e3feb1 194The "start_sub" argument is required. This is a subroutine reference
195that can be used to start the Catalyst server.
83d2d4a4 196
b8e3feb1 197=head2 run_and_watch
83d2d4a4 198
b8e3feb1 199This method forks, starts the server in a child process, and then
200watched for changed files in the parent. When files change, it kills
201the child, forks again, and starts a new server.
83d2d4a4 202
203=head1 SEE ALSO
204
caa3831b 205L<Catalyst>, L<File::ChangeNotify>
83d2d4a4 206
207=head1 AUTHORS
208
209Catalyst Contributors, see Catalyst.pm
210
211=head1 COPYRIGHT
212
213This program is free software, you can redistribute it and/or modify
214it under the same terms as Perl itself.
215
216=cut