more dispatch strategy documentation
[catagits/Web-Simple.git] / lib / Web / Simple.pm
CommitLineData
5c33dda5 1package Web::Simple;
2
3use strict;
4use warnings FATAL => 'all';
5
6sub import {
7 strict->import;
8 warnings->import(FATAL => 'all');
9 warnings->unimport('syntax');
10 warnings->import(FATAL => qw(
11 ambiguous bareword digit parenthesis precedence printf
12 prototype qw reserved semicolon
13 ));
14 my ($class, $app_package) = @_;
15 $class->_export_into($app_package);
16}
17
18sub _export_into {
19 my ($class, $app_package) = @_;
20 {
21 no strict 'refs';
22 *{"${app_package}::dispatch"} = sub {
23 $app_package->_setup_dispatchables(@_);
24 };
25 *{"${app_package}::filter_response"} = sub (&) {
26 $app_package->_construct_response_filter($_[0]);
27 };
39119082 28 *{"${app_package}::redispatch_to"} = sub {
29 $app_package->_construct_redispatch($_[0]);
30 };
5c33dda5 31 *{"${app_package}::default_config"} = sub {
32 my @defaults = @_;
33 *{"${app_package}::_default_config"} = sub { @defaults };
34 };
35 *{"${app_package}::self"} = \${"${app_package}::self"};
36 require Web::Simple::Application;
37 unshift(@{"${app_package}::ISA"}, 'Web::Simple::Application');
38 }
39}
40
7401408e 41=head1 NAME
42
43Web::Simple - A quick and easy way to build simple web applications
44
45=head1 WARNING
46
47This is really quite new. If you're reading this from git, it means it's
48really really new and we're still playing with things. If you're reading
49this on CPAN, it means the stuff that's here we're probably happy with. But
50only probably. So we may have to change stuff.
51
52If we do find we have to change stuff we'll add a section explaining how to
53switch your code across to the new version, and we'll do our best to make it
54as painless as possible because we've got Web::Simple applications too. But
55we can't promise not to change things at all. Not yet. Sorry.
56
57=head1 SYNOPSIS
58
59 #!/usr/bin/perl
60
61 use Web::Simple 'HelloWorld';
62
63 {
64 package HelloWorld;
65
66 dispatch [
67 sub (GET) {
68 [ 200, [ 'Content-type', 'text/plain' ], [ 'Hello world!' ] ]
69 },
70 sub () {
71 [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ]
72 }
73 ];
74 }
75
76 HelloWorld->run_if_script;
77
78If you save this file into your cgi-bin as hello-world.cgi and then visit
79
80 http://my.server.name/cgi-bin/hello-world.cgi/
81
82you'll get the "Hello world!" string output to your browser. For more complex
83examples and non-CGI deployment, see below.
84
85=head1 WHY?
86
87While I originally wrote Web::Simple as part of my Antiquated Perl talk for
88Italian Perl Workshop 2009, I've found that having a bare minimum system for
89writing web applications that doesn't drive me insane is rather nice.
90
91The philosophy of Web::Simple is to keep to an absolute bare minimum, for
92everything. It is not designed to be used for large scale applications;
93the L<Catalyst> web framework already works very nicely for that and is
94a far more mature, well supported piece of software.
95
96However, if you have an application that only does a couple of things, and
97want to not have to think about complexities of deployment, then Web::Simple
98might be just the thing for you.
99
100The Antiquated Perl talk can be found at L<http://www.shadowcat.co.uk/archive/conference-video/>.
101
102=head1 DESCRIPTION
103
104The only public interface the Web::Simple module itself provides is an
105import based one -
106
107 use Web::Simple 'NameOfApplication';
108
109This imports 'strict' and 'warnings FATAL => "all"' into your code as well,
110so you can skip the usual
111
112 use strict;
113 use warnings;
114
115provided you 'use Web::Simple' at the top of the file. Note that we turn
116on *fatal* warnings so if you have any warnings at any point from the file
117that you did 'use Web::Simple' in, then your application will die. This is,
118so far, considered a feature.
119
120Calling the import also makes NameOfApplication isa Web::Simple::Application
121- i.e. does the equivalent of
122
123 {
124 package NameOfApplication;
125 use base qw(Web::Simple::Application);
126 }
127
128It also exports the following subroutines:
129
130 default_config(
131 key => 'value',
132 ...
133 );
134
135 dispatch [ sub (...) { ... }, ... ];
136
137 filter_response { ... };
138
139 redispatch_to '/somewhere';
140
141and creates the $self global variable in your application package, so you can
142use $self in dispatch subs without violating strict (Web::Simple::Application
143arranges for dispatch subroutines to have the correct $self in scope when
144this happens).
145
146=head1 EXPORTED SUBROUTINES
147
148=head2 default_config
149
150 default_config(
151 one_key => 'foo',
152 another_key => 'bar',
153 );
154
155 ...
156
157 $self->config->{one_key} # 'foo'
158
159This creates the default configuration for the application, by creating a
160
161 sub _default_config {
162 return (one_key => 'foo', another_key => 'bar');
163 }
164
165in the application namespace when executed. Note that this means that
166you should only run default_config once - a second run will cause a warning
167that you are override the _default_config method in your application, which
168under Web::Simple will of course be fatal.
169
170=head2 dispatch
171
172 dispatch [
173 sub (GET) {
174 [ 200, [ 'Content-type', 'text/plain' ], [ 'Hello world!' ] ]
175 },
176 sub () {
177 [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ]
178 }
179 ];
180
181The dispatch subroutine calls NameOfApplication->_setup_dispatchables with
182the subroutines passed to it, which then create's your Web::Simple
183application's dispatcher from these subs. The prototype of the subroutine
184is expected to be a Web::Simple dispatch specification (see
185L</DISPATCH SPECIFICATIONS> below for more details), and the body of the
186subroutine is the code to execute if the specification matches. See
187L</DISPATCH STRATEGY> below for details on how the Web::Simple dispatch
188system uses the return values of these subroutines to determine how to
189continue, alter or abort dispatch.
190
191Note that _setup_dispatchables creates a
192
193 sub _dispatchables {
194 return (<dispatchable objects here>);
195 }
196
197method in your class so as with default_config, calling dispatch a second time
198will result in a fatal warning from your application.
199
200=head2 response_filter
201
202 response_filter {
203 # Hide errors from the user because we hates them, preciousss
204 if (ref($_[1]) eq 'ARRAY' && $_[1]->[0] == 500) {
205 $_[1] = [ 200, @{$_[1]}[1..$#{$_[1]}] ];
206 }
207 return $_[1];
208 };
209
210The response_filter subroutine is designed for use inside dispatch subroutines.
211
212It creates and returns a response filter object to the dispatcher,
213encapsulating the block passed to it as the filter routine to call. See
214L</DISPATCH STRATEGY> below for how a response filter affects dispatch.
215
3583ca04 216=head1 DISPATCH STRATEGY
217
81a5b03e 218=head2 Description of the dispatcher object
219
220Web::Simple::Dispatcher objects have three components:
221
222=over 4
223
224=item * match - an optional test if this dispatcher matches the request
225
226=item * call - a routine to call if this dispatcher matches (or has no match)
227
228=item * next - the next dispatcher to call
229
230=back
231
232When a dispatcher is invoked, it checks its match routine against the
233request environment. The match routine may provide alterations to the
234request as a result of matching, and/or arguments for the call routine.
235
236If no match routine has been provided then Web::Simple treats this as
237a success, and supplies the request environment to the call routine as
238an argument.
239
240Given a successful match, the call routine is now invoked in list context
241with any arguments given to the original dispatch, plus any arguments
242provided by the match result.
243
244If this routine returns (), Web::Simple treats this identically to a failure
245to match.
246
247If this routine returns a Web::Simple::Dispatcher, the environment changes
248are merged into the environment and the new dispatcher's next pointer is
249set to our next pointer.
250
251If this routine returns anything else, that is treated as the end of dispatch
252and the value is returned.
253
254On a failed match, Web::Simple invokes the next dispatcher with the same
255arguments and request environment passed to the current one. On a successful
256match that returned a new dispatcher, Web::Simple invokes the new dispatcher
257with the same arguments but the modified request environment.
258
259=head2 How Web::Simple builds dispatcher objects for you
260
261In the case of the Web::Simple L</dispatch> export the match is constructed
262from the subroutine prototype - i.e.
263
264 sub (<match specification>) {
265 <call code>
266 }
267
268and the 'next' pointer is populated with the next element of the array,
269expect for the last element, which is given a next that will throw a 500
270error if none of your dispatchers match. If you want to provide something
271else as a default, a routine with no match specification always matches, so -
272
273 sub () {
274 [ 404, [ 'Content-type', 'text/plain' ], [ 'Error: Not Found' ] ]
275 }
276
277will produce a 404 result instead of a 500 by default. You can also override
278the L<Web::Simple::Application/_build_final_dispatcher> method in your app.
279
280Note that the code in the subroutine is executed as a -method- on your
281application object, so if your match specification provides arguments you
282should unpack them like so:
283
284 sub (<match specification>) {
285 my ($self, @args) = @_;
286 ...
287 }
288
289=head2 Web::Simple match specifications
290
291=head3 Method matches
292
15dfe701 293 sub (GET ...) {
294
295A match specification beginning with a capital letter matches HTTP requests
296with that request method.
297
81a5b03e 298=head3 Path matches
299
15dfe701 300 sub (/login) {
301
302A match specification beginning with a / is a path match. In the simplest
303case it matches a specific path. To match a path with a wildcard part, you
304can do:
305
306 sub (/user/*) {
307 $self->handle_user($_[1])
308
309This will match /user/<anything> where <anything> does not include a literal
310/ character. The matched part becomes part of the match arguments. You can
311also match more than one part:
312
313 sub (/user/*/*) {
314 my ($self, $user_1, $user_2) = @_;
315
316 sub (/domain/*/user/*) {
317 my ($self, $domain, $user) = @_;
318
319and so on. To match an arbitrary number of parts, use -
320
321 sub (/page/**) {
322
323This will result in an element per /-separated part so matched. Note that
324you can do
325
326 sub (/page/**/edit) {
327
328to match an arbitrary number of parts up to but not including some final
329part.
330
81a5b03e 331=head3 Extension matches
332
15dfe701 333 sub (.html) {
334
335will match and strip .html from the path (assuming the subroutine itself
336returns something, of course). This is normally used for rendering - e.g.
337
338 sub (.html) {
339 filter_response { $self->render_html($_[1]) }
340 }
341
81a5b03e 342=head3 Combining matches
343
15dfe701 344Matches may be combined with the + character - e.g.
345
346 sub (GET+/user/*) {
347
348Note that for legibility you are permitted to use whitespace -
349
350 sub(GET + /user/*) {
351
352but it will be ignored.
353
3583ca04 354=cut
7401408e 355
5c33dda5 3561;