7a7c6ffa2ade65cf2d70a86775f7497a15f056ce
[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     my @events = $self->_watcher->wait_for_events();
79     $self->_handle_events(@events);
80 }
81
82 sub _handle_events {
83     my $self   = shift;
84     my @events = @_;
85
86     print STDERR "\n";
87     print STDERR "Saw changes to the following files:\n";
88
89     for my $event (@events) {
90         my $path = $event->path();
91         my $type = $event->type();
92
93         print STDERR " - $path ($type)\n";
94     }
95
96     print STDERR "\n";
97     print STDERR "Attempting to restart the server\n\n";
98
99     $self->_kill_child;
100
101     $self->_fork_and_start;
102
103     $self->_restart_on_changes;
104 }
105
106 sub DEMOLISH {
107     my $self = shift;
108
109     $self->_kill_child;
110 }
111
112 __PACKAGE__->meta->make_immutable;
113
114 1;
115
116 __END__
117
118 =head1 NAME
119
120 Catalyst::Restarter - Uses File::ChangeNotify to check for changed files and restart the server
121
122 =head1 SYNOPSIS
123
124     my $class = Catalyst::Restarter->pick_subclass;
125
126     my $restarter = $class->new(
127         directories => '/path/to/MyApp',
128         regex       => '\.yml$|\.yaml$|\.conf|\.pm$',
129         start_sub => sub { ... }
130     );
131
132     $restarter->run_and_watch;
133
134 =head1 DESCRIPTION
135
136 This is the base class for all restarters, and it also provide
137 functionality for picking an appropriate restarter subclass for a
138 given platform.
139
140 This class uses L<File::ChangeNotify> to watch one or more directories
141 of files and restart the Catalyst server when any of those files
142 changes.
143
144 =head1 METHODS
145
146 =head2 pick_subclass
147
148 Returns the name of an appropriate subclass for the given platform.
149
150 =head2 new ( start_sub => sub { ... }, ... )
151
152 This method creates a new restarter object, but should be called on a
153 subclass, not this class.
154
155 The "start_sub" argument is required. This is a subroutine reference
156 that can be used to start the Catalyst server.
157
158 =head2 run_and_watch
159
160 This method forks, starts the server in a child process, and then
161 watched for changed files in the parent. When files change, it kills
162 the child, forks again, and starts a new server.
163
164 =head1 SEE ALSO
165
166 L<Catalyst>, <File::ChangeNotify>
167
168 =head1 AUTHORS
169
170 Catalyst Contributors, see Catalyst.pm
171
172 =head1 COPYRIGHT
173
174 This program is free software, you can redistribute it and/or modify
175 it under the same terms as Perl itself.
176
177 =cut