99e64363e9d47458f7db9ea9583990edd0c5654c
[catagits/Web-Simple.git] / lib / Web / Simple / Application.pm
1 package Web::Simple::Application;
2
3 use Moo;
4
5 has 'config' => (
6   is => 'ro',
7   default => sub {
8     my ($self) = @_;
9     +{ $self->default_config }
10   },
11   trigger => sub {
12     my ($self, $value) = @_;
13     my %default = $self->default_config;
14     my @not = grep !exists $value->{$_}, keys %default;
15     @{$value}{@not} = @default{@not};
16   }
17 );
18
19 sub default_config { () }
20
21 has '_dispatcher' => (is => 'lazy');
22
23 sub _build__dispatcher {
24   my $self = shift;
25   require Web::Dispatch;
26   require Web::Simple::DispatchNode;
27   my $final = $self->_build_final_dispatcher;
28   Web::Dispatch->new(
29     app => sub { $self->dispatch_request(@_), $final },
30     node_class => 'Web::Simple::DispatchNode',
31     node_args => { app_object => $self }
32   );
33 }
34
35 sub _build_final_dispatcher {
36   [ 404, [ 'Content-type', 'text/plain' ], [ 'Not found' ] ]
37 }
38
39 sub run_if_script {
40   # ->to_psgi_app is true for require() but also works for plackup
41   return $_[0]->to_psgi_app if caller(1);
42   my $self = ref($_[0]) ? $_[0] : $_[0]->new;
43   $self->run(@_);
44 }
45
46 sub _run_cgi {
47   my $self = shift;
48   require Plack::Server::CGI;
49   Plack::Server::CGI->run($self->to_psgi_app);
50 }
51
52 sub _run_fcgi {
53   my $self = shift;
54   require Plack::Server::FCGI;
55   Plack::Server::FCGI->run($self->to_psgi_app);
56 }
57
58 sub to_psgi_app {
59   my $self = ref($_[0]) ? $_[0] : $_[0]->new;
60   $self->_dispatcher->to_app;
61 }
62
63 sub run {
64   my $self = shift;
65   if ($ENV{PHP_FCGI_CHILDREN} || $ENV{FCGI_ROLE} || $ENV{FCGI_SOCKET_PATH}) {
66     return $self->_run_fcgi;
67   } elsif ($ENV{GATEWAY_INTERFACE}) {
68     return $self->_run_cgi;
69   }
70   unless (@ARGV && $ARGV[0] =~ m{^/}) {
71     return $self->_run_cli(@ARGV);
72   }
73
74   my $path = shift @ARGV;
75
76   require HTTP::Request::Common;
77   require Plack::Test;
78   local *GET = \&HTTP::Request::Common::GET;
79
80   my $request = GET($path);
81   my $response;
82   Plack::Test::test_psgi($self->to_psgi_app, sub { $response = shift->($request) });
83   print $response->as_string;
84 }
85
86 sub _run_cli {
87   my $self = shift;
88   die $self->_cli_usage;
89 }
90
91 sub _cli_usage {
92   "To run this script in CGI test mode, pass a URL path beginning with /:\n".
93   "\n".
94   "  $0 /some/path\n".
95   "  $0 /\n"
96 }
97
98 1;
99
100 =head1 NAME
101
102 Web::Simple::Application - A base class for your Web-Simple application
103
104 =head1 DESCRIPTION
105
106 This is a base class for your L<Web::Simple> application.  You probably don't
107 need to construct this class yourself, since L<Web::Simple> does the 'heavy
108 lifting' for you in that regards.
109
110 =head1 METHODS
111
112 This class exposes the following public methods.
113
114 =head2 default_config
115
116 Merges with the C<config> initializer to provide configuration information for
117 your application.  For example:
118
119   sub default_config {
120     (
121       title => 'Bloggery',
122       posts_dir => $FindBin::Bin.'/posts',
123     );
124   }
125
126 Now, the C<config> attribute of C<$self>  will be set to a HashRef
127 containing keys 'title' and 'posts_dir'.
128
129 If you construct your application like:
130
131   MyWebSimpleApp::Web->new(config=>{environment=>'dev'})
132
133 then C<config> will have a C<environment> key with a value of 'dev'.
134
135 =head2 run_if_script
136
137 In the case where you wish to run your L<Web::Simple> based application as a 
138 stand alone CGI application, you can simple do:
139
140   ## my_web_simple_app.pl
141   use MyWebSimpleApp::Web;
142   MyWebSimpleApp::Web->run_if_script.
143
144 Or (even more simply) just inline the entire application:
145
146   ## my_web_simple_app.pl
147   #!/usr/bin/env perl
148   use Web::Simple 'HelloWorld';
149
150   {
151     package HelloWorld;
152
153     sub dispatch_request {
154       sub (GET) {
155         [ 200, [ 'Content-type', 'text/plain' ], [ 'Hello world!' ] ]
156       },
157       sub () {
158         [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ]
159       }
160     }
161   }
162
163   HelloWorld->run_if_script;
164
165 Additionally, you can treat the above script as though it were a standard PSGI
166 application file (*.psgi).  For example you can start up up with C<plackup>
167
168   plackup my_web_simple_app.pl
169
170 Which means you can write a L<Web::Simple> application as a plain old CGI
171 application and seemlessly migrate to a L<Plack> based solution when you are
172 ready for that.
173
174 Lastly, L</run_if_script> will automatically detect and support a Fast CGI
175 environment.
176
177 =head2 to_psgi_app
178
179 Given a L<Web::Simple> application root namespace, return it in a form suitable
180 to run in inside a L<Plack> container, or in L<Plack::Builder> or in a C<*.psgi>
181 file:
182
183   ## app.psgi
184   use strictures 1;
185   use Plack::Builder;
186   use MyWebSimpleApp::Web;
187
188   builder {
189     ## enable middleware
190     enable 'StackTrace';
191     enable 'Debug';
192
193     ## return application
194     MyWebSimpleApp::Web->to_psgi_app;
195   };
196
197 This could be run via C<plackup>, etc.  Please note the L<Plack::Builder> DSL
198 is optional, if you are enabling L<Plack::Middleware> internally in your
199 L<Web::Simple> application; your app.psgi could be as simple as:
200
201   use MyWebSimpleApp::Web;
202   MyWebSimpleApp::Web->to_psgi_app;
203
204 This means if you want to provide a 'default' set of middleware, one option is
205 to modify this method:
206
207   use Web::Simple 'HelloWorld';
208   use Plack::Builder;
209  
210   {
211     package HelloWorld;
212
213   
214     around 'to_psgi_app', sub {
215       my ($orig, $self) = (shift, shift);
216       my $app = $self->$orig(@_); 
217       builder {
218         enable ...; ## whatever middleware you want
219         $app;
220       };
221     };
222   }
223
224 As always, mix and match the pieces you actually need and remember the 
225 L<Web::Simple> philosophy of trying to keep it as minimal and simple as possible.
226
227 =head2 run
228
229 Used for running your application under stand-alone CGI and FCGI modes. Also
230 useful for testing:
231
232     my $app = MyWebSimpleApp::Web->new;
233     my $c = HTTP::Request::AsCGI->new(@args)->setup;
234     $app->run;
235
236 =head1 AUTHOR
237
238 Matt S. Trout <mst@shadowcat.co.uk>
239
240 =head1 CONTRIBUTORS
241
242 None required yet. Maybe this module is perfect (hahahahaha ...).
243
244 =head1 COPYRIGHT
245
246 Copyright (c) 2010 the Web::Simple L</AUTHOR> and L</CONTRIBUTORS>
247 as listed above.
248
249 =head1 LICENSE
250
251 This library is free software and may be distributed under the same terms
252 as perl itself.
253
254 =cut