5307bbba5b5c9814110432dc9c1eb1e1899c3cb1
[catagits/Web-Simple.git] / lib / Web / Simple.pm
1 package Web::Simple;
2
3 use strict;
4 use warnings FATAL => 'all';
5
6 sub setup_all_strictures {
7   strict->import;
8   warnings->import(FATAL => 'all');
9 }
10
11 sub setup_dispatch_strictures {
12   setup_all_strictures();
13   warnings->unimport('syntax');
14   warnings->import(FATAL => qw(
15     ambiguous bareword digit parenthesis precedence printf
16     prototype qw reserved semicolon
17   ));
18 }
19
20 sub import {
21   setup_dispatch_strictures();
22   my ($class, $app_package) = @_;
23   $class->_export_into($app_package);
24 }
25
26 sub _export_into {
27   my ($class, $app_package) = @_;
28   {
29     no strict 'refs';
30     *{"${app_package}::dispatch"} = sub {
31       $app_package->_setup_dispatcher(@_);
32     };
33     *{"${app_package}::filter_response"} = sub (&) {
34       $app_package->_construct_response_filter($_[0]);
35     };
36     *{"${app_package}::redispatch_to"} = sub {
37       $app_package->_construct_redispatch($_[0]);
38     };
39     *{"${app_package}::default_config"} = sub {
40       $app_package->_setup_default_config(@_);
41     };
42     *{"${app_package}::self"} = \${"${app_package}::self"};
43     require Web::Simple::Application;
44     unshift(@{"${app_package}::ISA"}, 'Web::Simple::Application');
45   }
46   (my $name = $app_package) =~ s/::/\//g;
47   $INC{"${name}.pm"} = 'Set by "use Web::Simple;" invocation';
48 }
49
50 =head1 NAME
51
52 Web::Simple - A quick and easy way to build simple web applications
53
54 =head1 WARNING
55
56 This is really quite new. If you're reading this from git, it means it's
57 really really new and we're still playing with things. If you're reading
58 this on CPAN, it means the stuff that's here we're probably happy with. But
59 only probably. So we may have to change stuff.
60
61 If we do find we have to change stuff we'll add a section explaining how to
62 switch your code across to the new version, and we'll do our best to make it
63 as painless as possible because we've got Web::Simple applications too. But
64 we can't promise not to change things at all. Not yet. Sorry.
65
66 =head1 SYNOPSIS
67
68   #!/usr/bin/perl
69
70   use Web::Simple 'HelloWorld';
71
72   {
73     package HelloWorld;
74
75     dispatch [
76       sub (GET) {
77         [ 200, [ 'Content-type', 'text/plain' ], [ 'Hello world!' ] ]
78       },
79       sub () {
80         [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ]
81       }
82     ];
83   }
84
85   HelloWorld->run_if_script;
86
87 If you save this file into your cgi-bin as hello-world.cgi and then visit
88
89   http://my.server.name/cgi-bin/hello-world.cgi/
90
91 you'll get the "Hello world!" string output to your browser. For more complex
92 examples and non-CGI deployment, see below.
93
94 =head1 WHY?
95
96 Web::Simple was originally written to form part of my Antiquated Perl talk for
97 Italian Perl Workshop 2009, but in writing the bloggery example I realised
98 that having a bare minimum system for writing web applications that doesn't
99 drive me insane was rather nice and decided to spend my attempt at nanowrimo
100 for 2009 improving and documenting it to the point where others could use it.
101
102 The philosophy of Web::Simple is to keep to an absolute bare minimum, for
103 everything. It is not designed to be used for large scale applications;
104 the L<Catalyst> web framework already works very nicely for that and is
105 a far more mature, well supported piece of software.
106
107 However, if you have an application that only does a couple of things, and
108 want to not have to think about complexities of deployment, then Web::Simple
109 might be just the thing for you.
110
111 The Antiquated Perl talk can be found at L<http://www.shadowcat.co.uk/archive/conference-video/>.
112
113 =head1 DESCRIPTION
114
115 The only public interface the Web::Simple module itself provides is an
116 import based one -
117
118   use Web::Simple 'NameOfApplication';
119
120 This imports 'strict' and 'warnings FATAL => "all"' into your code as well,
121 so you can skip the usual
122
123   use strict;
124   use warnings;
125
126 provided you 'use Web::Simple' at the top of the file. Note that we turn
127 on *fatal* warnings so if you have any warnings at any point from the file
128 that you did 'use Web::Simple' in, then your application will die. This is,
129 so far, considered a feature.
130
131 Calling the import also makes NameOfApplication isa Web::Simple::Application
132 - i.e. does the equivalent of
133
134   {
135     package NameOfApplication;
136     use base qw(Web::Simple::Application);
137   }
138
139 It also exports the following subroutines:
140
141   default_config(
142     key => 'value',
143     ...
144   );
145
146   dispatch [ sub (...) { ... }, ... ];
147
148   filter_response { ... };
149
150   redispatch_to '/somewhere';
151
152 and creates a $self global variable in your application package, so you can
153 use $self in dispatch subs without violating strict (Web::Simple::Application
154 arranges for dispatch subroutines to have the correct $self in scope when
155 this happens).
156
157 Finally, import sets
158
159   $INC{"NameOfApplication.pm"} = 'Set by "use Web::Simple;" invocation';
160
161 so that perl will not attempt to load the application again even if
162
163   require NameOfApplication;
164
165 is encountered in other code.
166
167 =head1 EXPORTED SUBROUTINES
168
169 =head2 default_config
170
171   default_config(
172     one_key => 'foo',
173     another_key => 'bar',
174   );
175
176   ...
177
178   $self->config->{one_key} # 'foo'
179
180 This creates the default configuration for the application, by creating a
181
182   sub _default_config {
183      return (one_key => 'foo', another_key => 'bar');
184   }
185
186 in the application namespace when executed. Note that this means that
187 you should only run default_config once - calling it a second time will
188 cause an exception to be thrown.
189
190 =head2 dispatch
191
192   dispatch [
193     sub (GET) {
194       [ 200, [ 'Content-type', 'text/plain' ], [ 'Hello world!' ] ]
195     },
196     sub () {
197       [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ]
198     }
199   ];
200
201 The dispatch subroutine calls NameOfApplication->_setup_dispatcher with
202 the subroutines passed to it, which then creates your Web::Simple
203 application's dispatcher from these subs. The prototype of the subroutine
204 is expected to be a Web::Simple dispatch specification (see
205 L</DISPATCH SPECIFICATIONS> below for more details), and the body of the
206 subroutine is the code to execute if the specification matches. See
207 L</DISPATCH STRATEGY> below for details on how the Web::Simple dispatch
208 system uses the return values of these subroutines to determine how to
209 continue, alter or abort dispatch.
210
211 Note that _setup_dispatcher creates a
212
213   sub _dispatcher {
214     return <root dispatcher object here>;
215   }
216
217 method in your class so as with default_config, calling dispatch a second time
218 will result in an exception.
219
220 =head2 response_filter
221
222   response_filter {
223     # Hide errors from the user because we hates them, preciousss
224     if (ref($_[1]) eq 'ARRAY' && $_[1]->[0] == 500) {
225       $_[1] = [ 200, @{$_[1]}[1..$#{$_[1]}] ];
226     }
227     return $_[1];
228   };
229
230 The response_filter subroutine is designed for use inside dispatch subroutines.
231
232 It creates and returns a special dispatcher that always matches, and calls
233 the block passed to it as a filter on the result of running the rest of the
234 current dispatch chain.
235
236 Thus the filter above runs further dispatch as normal, but if the result of
237 dispatch is a 500 (Internal Server Error) response, changes this to a 200 (OK)
238 response without altering the headers or body.
239
240 =head2 redispatch_to
241
242   redispatch_to '/other/url';
243
244 The redispatch_to subroutine is designed for use inside dispatch subroutines.
245
246 It creates and returns a special dispatcher that always matches, and instead
247 of continuing dispatch re-delegates it to the start of the dispatch process,
248 but with the path of the request altered to the supplied URL.
249
250 Thus if you receive a POST to '/some/url' and return a redipstch to
251 '/other/url', the dispatch behaviour will be exactly as if the same POST
252 request had been made to '/other/url' instead.
253
254 =head1 DISPATCH STRATEGY
255
256 =head2 Description of the dispatcher object
257
258 Web::Simple::Dispatcher objects have three components:
259
260 =over 4
261
262 =item * match - an optional test if this dispatcher matches the request
263
264 =item * call - a routine to call if this dispatcher matches (or has no match)
265
266 =item * next - the next dispatcher to call
267
268 =back
269
270 When a dispatcher is invoked, it checks its match routine against the
271 request environment. The match routine may provide alterations to the
272 request as a result of matching, and/or arguments for the call routine.
273
274 If no match routine has been provided then Web::Simple treats this as
275 a success, and supplies the request environment to the call routine as
276 an argument.
277
278 Given a successful match, the call routine is now invoked in list context
279 with any arguments given to the original dispatch, plus any arguments
280 provided by the match result.
281
282 If this routine returns (), Web::Simple treats this identically to a failure
283 to match.
284
285 If this routine returns a Web::Simple::Dispatcher, the environment changes
286 are merged into the environment and the new dispatcher's next pointer is
287 set to our next pointer.
288
289 If this routine returns anything else, that is treated as the end of dispatch
290 and the value is returned.
291
292 On a failed match, Web::Simple invokes the next dispatcher with the same
293 arguments and request environment passed to the current one. On a successful
294 match that returned a new dispatcher, Web::Simple invokes the new dispatcher
295 with the same arguments but the modified request environment.
296
297 =head2 How Web::Simple builds dispatcher objects for you
298
299 In the case of the Web::Simple L</dispatch> export the match is constructed
300 from the subroutine prototype - i.e.
301
302   sub (<match specification>) {
303     <call code>
304   }
305
306 and the 'next' pointer is populated with the next element of the array,
307 expect for the last element, which is given a next that will throw a 500
308 error if none of your dispatchers match. If you want to provide something
309 else as a default, a routine with no match specification always matches, so -
310
311   sub () {
312     [ 404, [ 'Content-type', 'text/plain' ], [ 'Error: Not Found' ] ]
313   }
314
315 will produce a 404 result instead of a 500 by default. You can also override
316 the L<Web::Simple::Application/_build_final_dispatcher> method in your app.
317
318 Note that the code in the subroutine is executed as a -method- on your
319 application object, so if your match specification provides arguments you
320 should unpack them like so:
321
322   sub (<match specification>) {
323     my ($self, @args) = @_;
324     ...
325   }
326
327 =head2 Web::Simple match specifications
328
329 =head3 Method matches
330
331   sub (GET ...) {
332
333 A match specification beginning with a capital letter matches HTTP requests
334 with that request method.
335
336 =head3 Path matches
337
338   sub (/login) {
339
340 A match specification beginning with a / is a path match. In the simplest
341 case it matches a specific path. To match a path with a wildcard part, you
342 can do:
343
344   sub (/user/*) {
345     $self->handle_user($_[1])
346
347 This will match /user/<anything> where <anything> does not include a literal
348 / character. The matched part becomes part of the match arguments. You can
349 also match more than one part:
350
351   sub (/user/*/*) {
352     my ($self, $user_1, $user_2) = @_;
353
354   sub (/domain/*/user/*) {
355     my ($self, $domain, $user) = @_;
356
357 and so on. To match an arbitrary number of parts, use -
358
359   sub (/page/**) {
360
361 This will result in an element per /-separated part so matched. Note that
362 you can do
363
364   sub (/page/**/edit) {
365
366 to match an arbitrary number of parts up to but not including some final
367 part.
368
369 Finally,
370
371   sub (/foo/...) {
372
373 will match /foo/ on the beginning of the path -and- strip it, much like
374 .html strips the extension. This is designed to be used to construct
375 nested dispatch structures, but can also prove useful for having e.g. an
376 optional language specification at the start of a path.
377
378 Note that the '...' is a "maybe something here, maybe not" so the above
379 specification will match like this:
380
381   /foo         # no match
382   /foo/        # match and strip path to '/'
383   /foo/bar/baz # match and strip path to '/bar/baz'
384
385 =head3 Extension matches
386
387   sub (.html) {
388
389 will match and strip .html from the path (assuming the subroutine itself
390 returns something, of course). This is normally used for rendering - e.g.
391
392   sub (.html) {
393     filter_response { $self->render_html($_[1]) }
394   }
395
396 Additionally,
397
398   sub (.*) {
399
400 will match any extension and supplies the stripped extension as a match
401 argument.
402
403 =head3 Combining matches
404
405 Matches may be combined with the + character - e.g.
406
407   sub (GET + /user/*) {
408
409 to create an AND match. They may also be combined withe the | character - e.g.
410
411   sub (GET|POST) {
412
413 to create an OR match. Matches can be nested with () - e.g.
414
415   sub ((GET|POST) + /user/*) {
416
417 and negated with ! - e.g.
418
419   sub (!/user/foo + /user/*) {
420
421 ! binds to the immediate rightmost match specification, so if you want
422 to negate a combination you will need to use
423
424   sub ( !(POST|PUT|DELETE) ) {
425
426 and | binds tighter than +, so
427
428   sub ((GET|POST) + /user/*) {
429
430 and
431
432   sub (GET|POST + /user/*) {
433
434 are equivalent, but
435
436   sub ((GET + .html) | (POST + .html)) {
437
438 and
439
440   sub (GET + .html | POST + .html) {
441
442 are not - the latter is equivalent to
443
444   sub (GET + (.html|POST) + .html) {
445
446 which will never match.
447
448 =head3 Whitespace
449
450 Note that for legibility you are permitted to use whitespace -
451
452   sub (GET + /user/*) {
453
454 but it will be ignored. This is because the perl parser strips whitespace
455 from subroutine prototypes, so this is equivalent to
456
457   sub (GET+/user/*) {
458
459 =cut
460
461 1;