Lots of changes to how the restarter & watcher work.
[catagits/Catalyst-Devel.git] / lib / Catalyst / Restarter.pm
1 package Catalyst::Restarter;
2
3 use Moose;
4
5 use Catalyst::Watcher;
6 use namespace::clean -except => 'meta';
7
8 has restart_sub => (
9     is       => 'ro',
10     isa      => 'CodeRef',
11     required => 1,
12 );
13
14 has _watcher => (
15     is  => 'rw',
16     isa => 'Catalyst::Watcher',
17 );
18
19 has _child => (
20     is  => 'rw',
21     isa => 'Int',
22 );
23
24 sub BUILD {
25     my $self = shift;
26     my $p    = shift;
27
28     delete $p->{restart_sub};
29
30     # We could make this lazily, but this lets us check that we
31     # received valid arguments for the watcher up front.
32     $self->_watcher( Catalyst::Watcher->instantiate_subclass( %{$p} ) );
33 }
34
35 sub run_and_watch {
36     my $self = shift;
37
38     $self->_fork_and_start;
39
40     return unless $self->_child;
41
42     $self->_restart_on_changes;
43 }
44
45 sub _fork_and_start {
46     my $self = shift;
47
48     if ( my $pid = fork ) {
49         $self->_child($pid);
50     }
51     else {
52         $self->restart_sub->();
53     }
54 }
55
56 sub _restart_on_changes {
57     my $self = shift;
58
59     $self->_watcher->watch($self);
60 }
61
62 sub handle_changes {
63     my $self  = shift;
64     my @files = @_;
65
66     print STDERR "\n";
67     print STDERR "Saw changes to the following files:\n";
68     print STDERR " - $_->{file} ($_->{status})\n" for @files;
69     print STDERR "\n";
70     print STDERR "Attempting to restart the server\n\n";
71
72     $self->_kill_child;
73
74     $self->_fork_and_start;
75
76     $self->_restart_on_changes;
77 }
78
79 sub _kill_child {
80     my $self = shift;
81
82     return unless $self->_child;
83
84     return unless kill 0, $self->_child;
85
86     local $SIG{CHLD} = 'IGNORE';
87     unless ( kill 'INT', $self->_child ) {
88         # The kill 0 thing does not work on Windows, but the restarter
89         # seems to work fine on Windows with this hack.
90         return if $^O eq 'MSWin32';
91         die "Cannot send INT signal to ", $self->_child, ": $!";
92     }
93 }
94
95 sub DEMOLISH {
96     my $self = shift;
97
98     $self->_kill_child;
99 }
100
101 __PACKAGE__->meta->make_immutable;
102
103 1;
104
105 __END__
106
107 =head1 NAME
108
109 Catalyst::Restarter - Uses Catalyst::Watcher to check for changed files and restart the server
110
111 =head1 SYNOPSIS
112
113     my $watcher = Catalyst::Watcher->new(
114         directory => '/path/to/MyApp',
115         regex     => '\.yml$|\.yaml$|\.conf|\.pm$',
116         interval  => 3,
117     );
118
119     while (1) {
120         my @changed_files = $watcher->watch();
121     }
122
123 =head1 DESCRIPTION
124
125 This class monitors a directory of files for changes made to any file
126 matching a regular expression. It correctly handles new files added to the
127 application as well as files that are deleted.
128
129 =head1 METHODS
130
131 =head2 new ( directory => $path [, regex => $regex, delay => $delay ] )
132
133 Creates a new Watcher object.
134
135 =head2 find_changed_files
136
137 Returns a list of files that have been added, deleted, or changed
138 since the last time watch was called. Each element returned is a hash
139 reference with two keys. The C<file> key contains the filename, and
140 the C<status> key contains one of "modified", "added", or "deleted".
141
142 =head1 SEE ALSO
143
144 L<Catalyst>, L<Catalyst::Restarter>, <File::Modified>
145
146 =head1 AUTHORS
147
148 Catalyst Contributors, see Catalyst.pm
149
150 =head1 COPYRIGHT
151
152 This program is free software, you can redistribute it and/or modify
153 it under the same terms as Perl itself.
154
155 =cut