examples, and rearrangement
[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
3583ca04 178=head1 DISPATCH STRATEGY
179
c21c9f07 180=head2 Examples
181
182 dispatch {
183 # matches: GET /user/1.htm?show_details=1
184 # GET /user/1.htm
185 sub (GET + /user/* + ?show_details~ + .htm|.html|.xhtml) {
186 shift; my ($user_id, $show_details) = @_;
187 ...
188 },
189 # matches: POST /user?username=frew
190 # POST /user?username=mst&first_name=matt&last_name=trout
191 sub (POST + /user + ?username=&*) {
192 shift; my ($username, $misc_params) = @_;
193 ...
194 },
195 # matches: DELETE /user/1/friend/2
196 sub (DELETE + /user/*/friend/*) {
197 shift; my ($user_id, $friend_id) = @_;
198 ...
199 },
200 # matches: PUT /user/1?first_name=Matt&last_name=Trout
201 sub (PUT + /user/* + ?first_name~&last_name~) {
202 shift; my ($user_id, $first_name, $last_name) = @_;
203 ...
204 },
205 sub (/user/*/...) {
206 my $user_id = $_[1];
207 subdispatch sub {
208 [
209 # matches: PUT /user/1/role/1
210 sub (PUT + /role/*) {
211 my $role_id = $_[1];
212 ...
213 },
214 # matches: DELETE /user/1/role/1
215 sub (DELETE + /role/*) {
216 my $role_id = shift;
217 ...
218 },
219 ];
220 }
221 },
222 }
223
81a5b03e 224=head2 Description of the dispatcher object
225
226Web::Simple::Dispatcher objects have three components:
227
228=over 4
229
230=item * match - an optional test if this dispatcher matches the request
231
232=item * call - a routine to call if this dispatcher matches (or has no match)
233
234=item * next - the next dispatcher to call
235
236=back
237
238When a dispatcher is invoked, it checks its match routine against the
239request environment. The match routine may provide alterations to the
240request as a result of matching, and/or arguments for the call routine.
241
242If no match routine has been provided then Web::Simple treats this as
243a success, and supplies the request environment to the call routine as
244an argument.
245
246Given a successful match, the call routine is now invoked in list context
247with any arguments given to the original dispatch, plus any arguments
248provided by the match result.
249
250If this routine returns (), Web::Simple treats this identically to a failure
251to match.
252
253If this routine returns a Web::Simple::Dispatcher, the environment changes
254are merged into the environment and the new dispatcher's next pointer is
255set to our next pointer.
256
257If this routine returns anything else, that is treated as the end of dispatch
258and the value is returned.
259
260On a failed match, Web::Simple invokes the next dispatcher with the same
261arguments and request environment passed to the current one. On a successful
262match that returned a new dispatcher, Web::Simple invokes the new dispatcher
263with the same arguments but the modified request environment.
264
265=head2 How Web::Simple builds dispatcher objects for you
266
267In the case of the Web::Simple L</dispatch> export the match is constructed
268from the subroutine prototype - i.e.
269
270 sub (<match specification>) {
271 <call code>
272 }
273
274and the 'next' pointer is populated with the next element of the array,
275expect for the last element, which is given a next that will throw a 500
276error if none of your dispatchers match. If you want to provide something
277else as a default, a routine with no match specification always matches, so -
278
279 sub () {
280 [ 404, [ 'Content-type', 'text/plain' ], [ 'Error: Not Found' ] ]
281 }
282
283will produce a 404 result instead of a 500 by default. You can also override
284the L<Web::Simple::Application/_build_final_dispatcher> method in your app.
285
286Note that the code in the subroutine is executed as a -method- on your
287application object, so if your match specification provides arguments you
288should unpack them like so:
289
290 sub (<match specification>) {
291 my ($self, @args) = @_;
292 ...
293 }
294
295=head2 Web::Simple match specifications
296
297=head3 Method matches
298
93e30ba3 299 sub (GET) {
15dfe701 300
301A match specification beginning with a capital letter matches HTTP requests
302with that request method.
303
81a5b03e 304=head3 Path matches
305
15dfe701 306 sub (/login) {
307
308A match specification beginning with a / is a path match. In the simplest
309case it matches a specific path. To match a path with a wildcard part, you
310can do:
311
312 sub (/user/*) {
313 $self->handle_user($_[1])
314
315This will match /user/<anything> where <anything> does not include a literal
316/ character. The matched part becomes part of the match arguments. You can
317also match more than one part:
318
319 sub (/user/*/*) {
320 my ($self, $user_1, $user_2) = @_;
321
322 sub (/domain/*/user/*) {
323 my ($self, $domain, $user) = @_;
324
325and so on. To match an arbitrary number of parts, use -
326
327 sub (/page/**) {
328
329This will result in an element per /-separated part so matched. Note that
330you can do
331
332 sub (/page/**/edit) {
333
334to match an arbitrary number of parts up to but not including some final
335part.
336
da8429c9 337Finally,
338
339 sub (/foo/...) {
340
341will match /foo/ on the beginning of the path -and- strip it, much like
342.html strips the extension. This is designed to be used to construct
343nested dispatch structures, but can also prove useful for having e.g. an
344optional language specification at the start of a path.
345
346Note that the '...' is a "maybe something here, maybe not" so the above
347specification will match like this:
348
349 /foo # no match
350 /foo/ # match and strip path to '/'
351 /foo/bar/baz # match and strip path to '/bar/baz'
352
81a5b03e 353=head3 Extension matches
354
15dfe701 355 sub (.html) {
356
357will match and strip .html from the path (assuming the subroutine itself
358returns something, of course). This is normally used for rendering - e.g.
359
360 sub (.html) {
74afe4b7 361 response_filter { $self->render_html($_[1]) }
15dfe701 362 }
363
b8bd7bd1 364Additionally,
365
366 sub (.*) {
367
368will match any extension and supplies the stripped extension as a match
369argument.
370
9b9866ae 371=head3 Query and body parameter matches
372
373Query and body parameters can be match via
374
375 sub (?<param spec>) { # match URI query
376 sub (%<param spec>) { # match body params
377
378The body is only matched if the content type is
379application/x-www-form-urlencoded (note this means that Web::Simple does
380not yet handle uploads; this will be addressed in a later release).
381
382The param spec is elements of one of the following forms -
383
384 param~ # optional parameter
385 param= # required parameter
386 @param~ # optional multiple parameter
387 @param= # required multiple parameter
eb9e0e25 388 :param~ # optional parameter in hashref
389 :param= # required parameter in hashref
390 :@param~ # optional multiple in hashref
391 :@param= # required multiple in hashref
392 * # include all other parameters in hashref
393 @* # include all other parameters as multiple in hashref
9b9866ae 394
eb9e0e25 395separated by the & character. The arguments added to the request are
396one per non-:/* parameter (scalar for normal, arrayref for multiple),
397plus if any :/* specs exist a hashref containing those values.
9b9866ae 398
399So, to match a page parameter with an optional order_by parameter one
400would write:
401
402 sub (?page=&order_by~) {
eb9e0e25 403 my ($self, $page, $order_by) = @_;
404 return unless $page =~ /^\d+$/;
405 $page ||= 'id';
9b9866ae 406 response_filter {
407 $_[1]->search_rs({}, $p);
408 }
409 }
410
411to implement paging and ordering against a L<DBIx::Class::ResultSet> object.
412
8c4ffad3 413Note that if a parameter is specified as single and multiple values are found,
414the last one will be used.
415
eb9e0e25 416To get all parameters as a hashref of arrayrefs, write:
417
418 sub(?@*) {
419 my ($self, $params) = @_;
420 ...
421
8c4ffad3 422To get two parameters as a hashref, write:
423
424 sub(?:user~&:domain~) {
425 my ($self, $params) = @_; # params contains only 'user' and 'domain' keys
426
427You can also mix these, so:
428
429 sub (?foo=&@bar~&:coffee=&@*) {
430 my ($self, $foo, $bar, $params);
431
432where $bar is an arrayref (possibly an empty one), and $params contains
433arrayref values for all parameters -not- mentioned and a scalar value for
434the 'coffee' parameter.
435
81a5b03e 436=head3 Combining matches
437
15dfe701 438Matches may be combined with the + character - e.g.
439
b8bd7bd1 440 sub (GET + /user/*) {
441
442to create an AND match. They may also be combined withe the | character - e.g.
443
444 sub (GET|POST) {
445
446to create an OR match. Matches can be nested with () - e.g.
447
448 sub ((GET|POST) + /user/*) {
449
450and negated with ! - e.g.
451
452 sub (!/user/foo + /user/*) {
453
454! binds to the immediate rightmost match specification, so if you want
455to negate a combination you will need to use
456
457 sub ( !(POST|PUT|DELETE) ) {
458
459and | binds tighter than +, so
460
461 sub ((GET|POST) + /user/*) {
462
463and
464
465 sub (GET|POST + /user/*) {
466
467are equivalent, but
468
469 sub ((GET + .html) | (POST + .html)) {
470
471and
472
473 sub (GET + .html | POST + .html) {
474
475are not - the latter is equivalent to
476
477 sub (GET + (.html|POST) + .html) {
478
479which will never match.
480
481=head3 Whitespace
15dfe701 482
483Note that for legibility you are permitted to use whitespace -
484
44db8e76 485 sub (GET + /user/*) {
15dfe701 486
b8bd7bd1 487but it will be ignored. This is because the perl parser strips whitespace
488from subroutine prototypes, so this is equivalent to
489
490 sub (GET+/user/*) {
15dfe701 491
c21c9f07 492=head1 EXPORTED SUBROUTINES
493
494=head2 default_config
495
496 default_config(
497 one_key => 'foo',
498 another_key => 'bar',
499 );
500
501 ...
502
503 $self->config->{one_key} # 'foo'
504
505This creates the default configuration for the application, by creating a
506
507 sub _default_config {
508 return (one_key => 'foo', another_key => 'bar');
509 }
510
511in the application namespace when executed. Note that this means that
512you should only run default_config once - calling it a second time will
513cause an exception to be thrown.
514
515=head2 dispatch
516
517 dispatch {
518 sub (GET) {
519 [ 200, [ 'Content-type', 'text/plain' ], [ 'Hello world!' ] ]
520 },
521 sub () {
522 [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ]
523 }
524 };
525
526The dispatch subroutine calls NameOfApplication->_setup_dispatcher with
527the return value of the block passed to it, which then creates your Web::Simple
528application's dispatcher from these subs. The prototype of each subroutine
529is expected to be a Web::Simple dispatch specification (see
530L</DISPATCH SPECIFICATIONS> below for more details), and the body of the
531subroutine is the code to execute if the specification matches.
532
533Each dispatcher is given the dispatcher constructed from the next subroutine
534returned as its next dispatcher, except for the final subroutine, which
535is given the return value of NameOfApplication->_build_final_dispatcher
536as its next dispatcher (by default this returns a 500 error response).
537
538See L</DISPATCH STRATEGY> below for details on how the Web::Simple dispatch
539system uses the return values of these subroutines to determine how to
540continue, alter or abort dispatch.
541
542Note that _setup_dispatcher creates a
543
544 sub _dispatcher {
545 return <root dispatcher object here>;
546 }
547
548method in your class so as with default_config, calling dispatch a second time
549will result in an exception.
550
551=head2 response_filter
552
553 response_filter {
554 # Hide errors from the user because we hates them, preciousss
555 if (ref($_[1]) eq 'ARRAY' && $_[1]->[0] == 500) {
556 $_[1] = [ 200, @{$_[1]}[1..$#{$_[1]}] ];
557 }
558 return $_[1];
559 };
560
561The response_filter subroutine is designed for use inside dispatch subroutines.
562
563It creates and returns a special dispatcher that always matches, and calls
564the block passed to it as a filter on the result of running the rest of the
565current dispatch chain.
566
567Thus the filter above runs further dispatch as normal, but if the result of
568dispatch is a 500 (Internal Server Error) response, changes this to a 200 (OK)
569response without altering the headers or body.
570
571=head2 redispatch_to
572
573 redispatch_to '/other/url';
574
575The redispatch_to subroutine is designed for use inside dispatch subroutines.
576
577It creates and returns a special dispatcher that always matches, and instead
578of continuing dispatch re-delegates it to the start of the dispatch process,
579but with the path of the request altered to the supplied URL.
580
581Thus if you receive a POST to '/some/url' and return a redipstch to
582'/other/url', the dispatch behaviour will be exactly as if the same POST
583request had been made to '/other/url' instead.
584
585=head2 subdispatch
586
587 subdispatch sub (/user/*/) {
588 my $u = $self->user($_[1]);
589 [
590 sub (GET) { $u },
591 sub (DELETE) { $u->delete },
592 ]
593 }
594
595The subdispatch subroutine is designed for use in dispatcher construction.
596
597It creates a dispatcher which, if it matches, treats its return value not
598as a final value but an arrayref of dispatch specifications such as could
599be passed to the dispatch subroutine itself. These are turned into a dispatcher
600which is then invoked. Any changes the match makes to the request are in
601scope for this inner dispatcher only - so if the initial match is a
602destructive one like .html the full path will be restored if the
603subdispatch fails.
604
8c4ffad3 605=head1 CHANGES BETWEEN RELEASES
606
607=head2 Changes since Antiquated Perl
608
609=over 4
610
611=item * filter_response renamed to response_filter
612
613This is a pure rename; a global search and replace should fix it.
614
c21c9f07 615=item * dispatch [] changed to dispatch {}
8c4ffad3 616
617Simply changing
618
619 dispatch [ sub(...) { ... }, ... ];
620
621to
622
623 dispatch { sub(...) { ... }, ... };
624
625should work fine.
626
627=back
628
629=head1 COMMUNITY AND SUPPORT
630
631=head2 IRC channel
632
633irc.perl.org #web-simple
634
635=head2 No mailing list yet
636
637Because mst's non-work email is a bombsite so he'd never read it anyway.
638
639=head2 Git repository
640
641Gitweb is on http://git.shadowcat.co.uk/ and the clone URL is:
642
643 git clone git://git.shadowcat.co.uk/catagits/Web-Simple.git
644
645=head1 AUTHOR
646
647Matt S. Trout <mst@shadowcat.co.uk>
648
649=head1 CONTRIBUTORS
650
651None required yet. Maybe this module is perfect (hahahahaha ...).
652
653=head1 COPYRIGHT
654
655Copyright (c) 2009 the Web::Simple L</AUTHOR> and L</CONTRIBUTORS>
656as listed above.
657
658=head1 LICENSE
659
660This library is free software and may be distributed under the same terms
661as perl itself.
662
3583ca04 663=cut
7401408e 664
5c33dda5 6651;