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