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