Fix the "eternally growing stack trace" problem with the restarter.
[catagits/Catalyst-Devel.git] / lib / Catalyst / Restarter.pm
1 package Catalyst::Restarter;
2
3 use Moose;
4
5 use Cwd qw( abs_path );
6 use File::ChangeNotify;
7 use FindBin;
8 use namespace::clean -except => 'meta';
9
10 has start_sub => (
11     is       => 'ro',
12     isa      => 'CodeRef',
13     required => 1,
14 );
15
16 has argv =>  (
17     is       => 'ro',
18     isa      => 'ArrayRef',
19     required => 1,
20 );
21
22 has _watcher => (
23     is  => 'rw',
24     isa => 'File::ChangeNotify::Watcher',
25 );
26
27 has _child => (
28     is  => 'rw',
29     isa => 'Int',
30 );
31
32 sub pick_subclass {
33     my $class = shift;
34
35     my $subclass;
36     $subclass =
37         defined $ENV{CATALYST_RESTARTER}
38             ? $ENV{CATALYST_RESTARTER}
39             :  $^O eq 'MSWin32'
40             ? 'Win32'
41             : 'Forking';
42
43     $subclass = 'Catalyst::Restarter::' . $subclass;
44
45     eval "use $subclass";
46     die $@ if $@;
47
48     return $subclass;
49 }
50
51 sub BUILD {
52     my $self = shift;
53     my $p    = shift;
54
55     delete $p->{start_sub};
56
57     $p->{filter} ||= qr/(?:\/|^)(?![.#_]).+(?:\.yml$|\.yaml$|\.conf|\.pm)$/;
58     $p->{directories} ||= abs_path( File::Spec->catdir( $FindBin::Bin, '..' ) );
59
60     # We could make this lazily, but this lets us check that we
61     # received valid arguments for the watcher up front.
62     $self->_watcher( File::ChangeNotify->instantiate_watcher( %{$p} ) );
63 }
64
65 sub run_and_watch {
66     my $self = shift;
67
68     $self->_fork_and_start;
69
70     return unless $self->_child;
71
72     $self->_restart_on_changes;
73 }
74
75 sub _restart_on_changes {
76     my $self = shift;
77
78     # We use this loop in order to avoid having _handle_events() call back
79     # into this method. We used to do that, and the end result was that stack
80     # traces become longer and longer with every restart. Using the loop, the
81     # portion of the stack trace that covers this code never changes.
82     while (1) {
83         my @events = $self->_watcher->wait_for_events();
84         $self->_handle_events(@events);
85     }
86 }
87
88 sub _handle_events {
89     my $self   = shift;
90     my @events = @_;
91
92     print STDERR "\n";
93     print STDERR "Saw changes to the following files:\n";
94
95     for my $event (@events) {
96         my $path = $event->path();
97         my $type = $event->type();
98
99         print STDERR " - $path ($type)\n";
100     }
101
102     print STDERR "\n";
103     print STDERR "Attempting to restart the server\n\n";
104
105     $self->_kill_child;
106
107     $self->_fork_and_start;
108 }
109
110 sub DEMOLISH {
111     my $self = shift;
112
113     $self->_kill_child;
114 }
115
116 __PACKAGE__->meta->make_immutable;
117
118 1;
119
120 __END__
121
122 =head1 NAME
123
124 Catalyst::Restarter - Uses File::ChangeNotify to check for changed files and restart the server
125
126 =head1 SYNOPSIS
127
128     my $class = Catalyst::Restarter->pick_subclass;
129
130     my $restarter = $class->new(
131         directories => '/path/to/MyApp',
132         regex       => '\.yml$|\.yaml$|\.conf|\.pm$',
133         start_sub => sub { ... }
134     );
135
136     $restarter->run_and_watch;
137
138 =head1 DESCRIPTION
139
140 This is the base class for all restarters, and it also provide
141 functionality for picking an appropriate restarter subclass for a
142 given platform.
143
144 This class uses L<File::ChangeNotify> to watch one or more directories
145 of files and restart the Catalyst server when any of those files
146 changes.
147
148 =head1 METHODS
149
150 =head2 pick_subclass
151
152 Returns the name of an appropriate subclass for the given platform.
153
154 =head2 new ( start_sub => sub { ... }, ... )
155
156 This method creates a new restarter object, but should be called on a
157 subclass, not this class.
158
159 The "start_sub" argument is required. This is a subroutine reference
160 that can be used to start the Catalyst server.
161
162 =head2 run_and_watch
163
164 This method forks, starts the server in a child process, and then
165 watched for changed files in the parent. When files change, it kills
166 the child, forks again, and starts a new server.
167
168 =head1 SEE ALSO
169
170 L<Catalyst>, <File::ChangeNotify>
171
172 =head1 AUTHORS
173
174 Catalyst Contributors, see Catalyst.pm
175
176 =head1 COPYRIGHT
177
178 This program is free software, you can redistribute it and/or modify
179 it under the same terms as Perl itself.
180
181 =cut