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