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