Fix background option
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Script / Server.pm
1 package Catalyst::Script::Server;
2 use Moose;
3 use MooseX::Types::Common::Numeric qw/PositiveInt/;
4 use MooseX::Types::Moose qw/ArrayRef Str Bool Int RegexpRef/;
5 use Catalyst::Utils;
6 use namespace::autoclean;
7
8 with 'Catalyst::ScriptRole';
9
10 has debug => (
11     traits        => [qw(Getopt)],
12     cmd_aliases   => 'd',
13     isa           => Bool,
14     is            => 'ro',
15     documentation => q{Force debug mode},
16 );
17
18 has host => (
19     traits        => [qw(Getopt)],
20     cmd_aliases   => 'h',
21     isa           => Str,
22     is            => 'ro',
23     # N.B. undef (the default) means we bind on all interfaces on the host.
24     documentation => 'Specify a hostname or IP on this host for the server to bind to',
25 );
26
27 has fork => (
28     traits        => [qw(Getopt)],
29     cmd_aliases   => 'f',
30     isa           => Bool,
31     is            => 'ro',
32     default       => 0,
33     documentation => 'Fork the server to be able to serve multiple requests at once',
34 );
35
36 has port => (
37     traits        => [qw(Getopt)],
38     cmd_aliases   => 'p',
39     isa           => PositiveInt,
40     is            => 'ro',
41     default       => sub {
42         Catalyst::Utils::env_value(shift->application_name, 'port') || 3000
43     },
44     documentation => 'Specify a different listening port (to the default port 3000)',
45 );
46
47 use Moose::Util::TypeConstraints;
48 class_type 'MooseX::Daemonize::Pid::File';
49 subtype 'MyStr', as Str, where { 1 }; # FIXME - Fuck ugly!
50 coerce 'MooseX::Daemonize::Pid::File', from 'MyStr', via {
51     Class::MOP::load_class("MooseX::Daemonize::Pid::File");
52     MooseX::Daemonize::Pid::File->new( file => $_ );
53 };
54 MooseX::Getopt::OptionTypeMap->add_option_type_to_map(
55     'MooseX::Daemonize::Pid::File' => '=s',
56 );
57 has pidfile => (
58     traits        => [qw(Getopt)],
59     cmd_aliases   => 'pid',
60     isa           => 'MooseX::Daemonize::Pid::File',
61     is            => 'ro',
62     documentation => 'Specify a pidfile',
63     coerce        => 1,
64     predicate     => '_has_pidfile',
65 );
66
67 sub BUILD {
68     my $self = shift;
69     $self->pidfile->write
70         if $self->_has_pidfile;
71     if ($self->background) {
72         # FIXME - This is evil. Should we just add MX::Daemonize to the deps?
73         Class::MOP::load_class('MooseX::Daemonize::Core');
74         MooseX::Daemonize::Core->meta->apply($self);
75     }
76 }
77
78 has keepalive => (
79     traits        => [qw(Getopt)],
80     cmd_aliases   => 'k',
81     isa           => Bool,
82     is            => 'ro',
83     default       => 0,
84     documentation => 'Support keepalive',
85 );
86
87 has background => (
88     traits        => [qw(Getopt)],
89     cmd_aliases   => 'bg',
90     isa           => Bool,
91     is            => 'ro',
92     default       => 0,
93     documentation => 'Run in the background',
94 );
95
96 has restart => (
97     traits        => [qw(Getopt)],
98     cmd_aliases   => 'r',
99     isa           => Bool,
100     is            => 'ro',
101     default       => sub {
102         Catalyst::Utils::env_value(shift->application_name, 'reload') || 0;
103     },
104     documentation => 'use Catalyst::Restarter to detect code changes and restart the application',
105 );
106
107 has restart_directory => (
108     traits        => [qw(Getopt)],
109     cmd_aliases   => [ 'rdir', 'restartdirectory' ],
110     isa           => ArrayRef[Str],
111     is            => 'ro',
112     documentation => 'Restarter directory to watch',
113     predicate     => '_has_restart_directory',
114 );
115
116 has restart_delay => (
117     traits        => [qw(Getopt)],
118     cmd_aliases   => 'rd',
119     isa           => Int,
120     is            => 'ro',
121     documentation => 'Set a restart delay',
122     predicate     => '_has_restart_delay',
123 );
124
125 {
126     use Moose::Util::TypeConstraints;
127
128     my $tc = subtype as RegexpRef;
129     coerce $tc, from Str, via { qr/$_/ };
130
131     MooseX::Getopt::OptionTypeMap->add_option_type_to_map($tc => '=s');
132
133     has restart_regex => (
134         traits        => [qw(Getopt)],
135         cmd_aliases   => 'rr',
136         isa           => $tc,
137         coerce        => 1,
138         is            => 'ro',
139         documentation => 'Restart regex',
140         predicate     => '_has_restart_regex',
141     );
142 }
143
144 has follow_symlinks => (
145     traits        => [qw(Getopt)],
146     cmd_aliases   => 'sym',
147     isa           => Bool,
148     is            => 'ro',
149     default       => 0,
150     documentation => 'Follow symbolic links',
151     predicate     => '_has_follow_symlinks',
152 );
153
154 sub _plack_engine_name {
155     my $self = shift;
156     return $self->fork ? 'Starman' : $self->keepalive ? 'Starman' : 'Standalone';
157 }
158
159 sub _restarter_args {
160     my $self = shift;
161
162     return (
163         argv => $self->ARGV,
164         start_sub => sub { $self->_run_application },
165         ($self->_has_follow_symlinks   ? (follow_symlinks => $self->follow_symlinks)   : ()),
166         ($self->_has_restart_delay     ? (sleep_interval  => $self->restart_delay)     : ()),
167         ($self->_has_restart_directory ? (directories     => $self->restart_directory) : ()),
168         ($self->_has_restart_regex     ? (filter          => $self->restart_regex)     : ()),
169     ),
170     (
171         map { $_ => $self->$_ } qw(application_name host port debug pidfile fork background keepalive)
172     );
173 }
174
175 has restarter_class => (
176     is => 'ro',
177     isa => Str,
178     lazy => 1,
179     default => sub {
180         my $self = shift;
181         Catalyst::Utils::env_value($self->application_name, 'RESTARTER') || 'Catalyst::Restarter';
182     }
183 );
184
185 sub run {
186     my $self = shift;
187
188     local $ENV{CATALYST_DEBUG} = 1
189         if $self->debug;
190
191     if ( $self->restart ) {
192         die "Cannot run in the background and also watch for changed files.\n"
193             if $self->background;
194
195         # If we load this here, then in the case of a restarter, it does not
196         # need to be reloaded for each restart.
197         require Catalyst;
198
199         # If this isn't done, then the Catalyst::Devel tests for the restarter
200         # fail.
201         $| = 1 if $ENV{HARNESS_ACTIVE};
202
203         Catalyst::Utils::ensure_class_loaded($self->restarter_class);
204
205         my $subclass = $self->restarter_class->pick_subclass;
206
207         my $restarter = $subclass->new(
208             $self->_restarter_args()
209         );
210
211         $restarter->run_and_watch;
212     }
213     else {
214         if ($self->background) {
215             $self->daemon_fork;
216
217             return 1 unless $self->is_daemon;
218
219             Class::MOP::load_class($self->application_name);
220
221             $self->daemon_detach;
222         }
223
224         $self->_run_application;
225     }
226
227
228 }
229
230 sub _plack_loader_args {
231     my ($self) = shift;
232     return (
233         port => $self->port,
234         host => $self->host,
235         keepalive => $self->keepalive ? 100 : 1,
236         server_ready => sub {
237             my ($args) = @_;
238
239             my $name  = $args->{server_software} || ref($args); # $args is $server
240             my $host  = $args->{host} || 0;
241             my $proto = $args->{proto} || 'http';
242
243             print STDERR "$name: Accepting connections at $proto://$host:$args->{port}/\n";
244         },
245     );
246 }
247
248 sub _application_args {
249     my ($self) = shift;
250     return (
251         $self->port,
252         $self->host,
253         {
254            argv => $self->ARGV,
255            map { $_ => $self->$_ } qw/
256                 fork
257                 keepalive
258                 background
259                 pidfile
260                 keepalive
261                 follow_symlinks
262             /,
263         },
264     );
265 }
266
267 __PACKAGE__->meta->make_immutable;
268
269 1;
270
271 =head1 NAME
272
273 Catalyst::Script::Server - Catalyst test server
274
275 =head1 SYNOPSIS
276
277  myapp_server.pl [options]
278
279  Options:
280    -d     --debug          force debug mode
281    -f     --fork           handle each request in a new process
282                       (defaults to false)
283           --help           display this help and exits
284    -h     --host           host (defaults to all)
285    -p     --port           port (defaults to 3000)
286    -k     --keepalive      enable keep-alive connections
287    -r     --restart        restart when files get modified
288                        (defaults to false)
289    --rd   --restart_delay  delay between file checks
290                       (ignored if you have Linux::Inotify2 installed)
291    --rr   --restart_regex  regex match files that trigger
292                       a restart when modified
293                       (defaults to '\.yml$|\.yaml$|\.conf|\.pm$')
294    --rdir --restart_directory  the directory to search for
295                       modified files, can be set multiple times
296                       (defaults to '[SCRIPT_DIR]/..')
297    --sym  --follow_symlinks   follow symlinks in search directories
298                       (defaults to false. this is a no-op on Win32)
299    --bg   --background        run the process in the background
300    --pid  --pidfile           specify filename for pid file
301
302  See also:
303    perldoc Catalyst::Manual
304    perldoc Catalyst::Manual::Intro
305
306 =head1 DESCRIPTION
307
308 Run a Catalyst test server for this application.
309
310 =head1 AUTHORS
311
312 Catalyst Contributors, see Catalyst.pm
313
314 =head1 COPYRIGHT
315
316 This library is free software. You can redistribute it and/or modify
317 it under the same terms as Perl itself.
318
319 =cut