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