Hacking the server script
[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 }
72
73 has keepalive => (
74     traits        => [qw(Getopt)],
75     cmd_aliases   => 'k',
76     isa           => Bool,
77     is            => 'ro',
78     default       => 0,
79     documentation => 'Support keepalive',
80 );
81
82 has background => (
83     traits        => [qw(Getopt)],
84     cmd_aliases   => 'bg',
85     isa           => Bool,
86     is            => 'ro',
87     default       => 0,
88     documentation => 'Run in the background',
89 );
90
91 has restart => (
92     traits        => [qw(Getopt)],
93     cmd_aliases   => 'r',
94     isa           => Bool,
95     is            => 'ro',
96     default       => sub {
97         Catalyst::Utils::env_value(shift->application_name, 'reload') || 0;
98     },
99     documentation => 'use Catalyst::Restarter to detect code changes and restart the application',
100 );
101
102 has restart_directory => (
103     traits        => [qw(Getopt)],
104     cmd_aliases   => [ 'rdir', 'restartdirectory' ],
105     isa           => ArrayRef[Str],
106     is            => 'ro',
107     documentation => 'Restarter directory to watch',
108     predicate     => '_has_restart_directory',
109 );
110
111 has restart_delay => (
112     traits        => [qw(Getopt)],
113     cmd_aliases   => 'rd',
114     isa           => Int,
115     is            => 'ro',
116     documentation => 'Set a restart delay',
117     predicate     => '_has_restart_delay',
118 );
119
120 {
121     use Moose::Util::TypeConstraints;
122
123     my $tc = subtype as RegexpRef;
124     coerce $tc, from Str, via { qr/$_/ };
125
126     MooseX::Getopt::OptionTypeMap->add_option_type_to_map($tc => '=s');
127
128     has restart_regex => (
129         traits        => [qw(Getopt)],
130         cmd_aliases   => 'rr',
131         isa           => $tc,
132         coerce        => 1,
133         is            => 'ro',
134         documentation => 'Restart regex',
135         predicate     => '_has_restart_regex',
136     );
137 }
138
139 has follow_symlinks => (
140     traits        => [qw(Getopt)],
141     cmd_aliases   => 'sym',
142     isa           => Bool,
143     is            => 'ro',
144     default       => 0,
145     documentation => 'Follow symbolic links',
146     predicate     => '_has_follow_symlinks',
147 );
148
149 sub _plack_engine_name {
150     my $self = shift;
151     return $self->fork ? 'Starman' : $self->keepalive ? 'Starman' : 'Standalone';
152 }
153
154 sub _restarter_args {
155     my $self = shift;
156
157     return (
158         argv => $self->ARGV,
159         start_sub => sub { $self->_run_application },
160         ($self->_has_follow_symlinks   ? (follow_symlinks => $self->follow_symlinks)   : ()),
161         ($self->_has_restart_delay     ? (sleep_interval  => $self->restart_delay)     : ()),
162         ($self->_has_restart_directory ? (directories     => $self->restart_directory) : ()),
163         ($self->_has_restart_regex     ? (filter          => $self->restart_regex)     : ()),
164     ),
165     (
166         map { $_ => $self->$_ } qw(application_name host port debug pidfile fork background keepalive)
167     );
168 }
169
170 has restarter_class => (
171     is => 'ro',
172     isa => Str,
173     lazy => 1,
174     default => sub {
175         my $self = shift;
176         Catalyst::Utils::env_value($self->application_name, 'RESTARTER') || 'Catalyst::Restarter';
177     }
178 );
179
180 sub run {
181     my $self = shift;
182
183     local $ENV{CATALYST_DEBUG} = 1
184         if $self->debug;
185
186     if ( $self->restart ) {
187         die "Cannot run in the background and also watch for changed files.\n"
188             if $self->background;
189
190         # If we load this here, then in the case of a restarter, it does not
191         # need to be reloaded for each restart.
192         require Catalyst;
193
194         # If this isn't done, then the Catalyst::Devel tests for the restarter
195         # fail.
196         $| = 1 if $ENV{HARNESS_ACTIVE};
197
198         Catalyst::Utils::ensure_class_loaded($self->restarter_class);
199
200         my $subclass = $self->restarter_class->pick_subclass;
201
202         my $restarter = $subclass->new(
203             $self->_restarter_args()
204         );
205
206         $restarter->run_and_watch;
207     }
208     else {
209         $self->_run_application;
210     }
211
212
213 }
214
215 sub _plack_loader_args {
216     my ($self) = shift;
217     return (
218         port => $self->port,
219         host => $self->host,
220         keepalive => $self->keepalive ? 100 : 1,
221         server_ready => sub {
222             my ($args) = @_;
223
224             my $name  = $args->{server_software} || ref($args); # $args is $server
225             my $host  = $args->{host} || 0;
226             my $proto = $args->{proto} || 'http';
227
228             print STDERR "$name: Accepting connections at $proto://$host:$args->{port}/\n";
229         },
230     );
231 }
232
233 sub _application_args {
234     my ($self) = shift;
235     return (
236         $self->port,
237         $self->host,
238         {
239            argv => $self->ARGV,
240            map { $_ => $self->$_ } qw/
241                 fork
242                 keepalive
243                 background
244                 pidfile
245                 keepalive
246                 follow_symlinks
247             /,
248         },
249     );
250 }
251
252 __PACKAGE__->meta->make_immutable;
253
254 1;
255
256 =head1 NAME
257
258 Catalyst::Script::Server - Catalyst test server
259
260 =head1 SYNOPSIS
261
262  myapp_server.pl [options]
263
264  Options:
265    -d     --debug          force debug mode
266    -f     --fork           handle each request in a new process
267                       (defaults to false)
268           --help           display this help and exits
269    -h     --host           host (defaults to all)
270    -p     --port           port (defaults to 3000)
271    -k     --keepalive      enable keep-alive connections
272    -r     --restart        restart when files get modified
273                        (defaults to false)
274    --rd   --restart_delay  delay between file checks
275                       (ignored if you have Linux::Inotify2 installed)
276    --rr   --restart_regex  regex match files that trigger
277                       a restart when modified
278                       (defaults to '\.yml$|\.yaml$|\.conf|\.pm$')
279    --rdir --restart_directory  the directory to search for
280                       modified files, can be set multiple times
281                       (defaults to '[SCRIPT_DIR]/..')
282    --sym  --follow_symlinks   follow symlinks in search directories
283                       (defaults to false. this is a no-op on Win32)
284    --bg   --background        run the process in the background
285    --pid  --pidfile           specify filename for pid file
286
287  See also:
288    perldoc Catalyst::Manual
289    perldoc Catalyst::Manual::Intro
290
291 =head1 DESCRIPTION
292
293 Run a Catalyst test server for this application.
294
295 =head1 AUTHORS
296
297 Catalyst Contributors, see Catalyst.pm
298
299 =head1 COPYRIGHT
300
301 This library is free software. You can redistribute it and/or modify
302 it under the same terms as Perl itself.
303
304 =cut