7a13897b0beb8ffd4ec2df959c386190d85df869
[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 "\n";
68         print STDERR "Saw changes to the following files:\n";
69         print STDERR " - $_->{file} ($_->{status})\n" for @files;
70         print STDERR "\n";
71         print STDERR "Attempting to restart the server\n\n";
72
73         if ( $self->_child ) {
74             kill 2, $self->_child
75                 or die "Cannot send INT to child (" . $self->_child . "): $!";
76         }
77
78         $self->_fork_and_start;
79
80         return unless $self->_child;
81     }
82 }
83
84 sub DEMOLISH {
85     my $self = shift;
86
87     if ( $self->_child ) {
88         kill 2, $self->_child;
89     }
90 }
91
92 __PACKAGE__->meta->make_immutable;
93
94 1;
95
96 __END__
97
98 =head1 NAME
99
100 Catalyst::Restarter - Uses Catalyst::Watcher to check for changed files and restart the server
101
102 =head1 SYNOPSIS
103
104     my $watcher = Catalyst::Watcher->new(
105         directory => '/path/to/MyApp',
106         regex     => '\.yml$|\.yaml$|\.conf|\.pm$',
107         interval  => 3,
108     );
109
110     while (1) {
111         my @changed_files = $watcher->watch();
112     }
113
114 =head1 DESCRIPTION
115
116 This class monitors a directory of files for changes made to any file
117 matching a regular expression. It correctly handles new files added to the
118 application as well as files that are deleted.
119
120 =head1 METHODS
121
122 =head2 new ( directory => $path [, regex => $regex, delay => $delay ] )
123
124 Creates a new Watcher object.
125
126 =head2 find_changed_files
127
128 Returns a list of files that have been added, deleted, or changed
129 since the last time watch was called. Each element returned is a hash
130 reference with two keys. The C<file> key contains the filename, and
131 the C<status> key contains one of "modified", "added", or "deleted".
132
133 =head1 SEE ALSO
134
135 L<Catalyst>, L<Catalyst::Restarter>, <File::Modified>
136
137 =head1 AUTHORS
138
139 Catalyst Contributors, see Catalyst.pm
140
141 =head1 COPYRIGHT
142
143 This program is free software, you can redistribute it and/or modify
144 it under the same terms as Perl itself.
145
146 =cut