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