5 use warnings::illegalproto ();
7 our $VERSION = '0.004';
10 my ($class, $app_package) = @_;
11 $class->_export_into($app_package||caller);
12 eval "package $class; use Web::Dispatch::Wrapper; use Moo;";
14 warnings::illegalproto->unimport;
18 my ($class, $app_package) = @_;
21 *{"${app_package}::PSGI_ENV"} = sub () { -1 };
22 require Web::Simple::Application;
23 unshift(@{"${app_package}::ISA"}, 'Web::Simple::Application');
25 (my $name = $app_package) =~ s/::/\//g;
26 $INC{"${name}.pm"} = 'Set by "use Web::Simple;" invocation';
31 Web::Simple - A quick and easy way to build simple web applications
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.
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.
51 use Web::Simple 'HelloWorld';
56 sub dispatch_request {
58 [ 200, [ 'Content-type', 'text/plain' ], [ 'Hello world!' ] ]
61 [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ]
66 HelloWorld->run_if_script;
68 If you save this file into your cgi-bin as hello-world.cgi and then visit
70 http://my.server.name/cgi-bin/hello-world.cgi/
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.
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.
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.
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.
93 The Antiquated Perl talk can be found at L<http://www.shadowcat.co.uk/archive/conference-video/>.
97 The only public interface the Web::Simple module itself provides is an
100 use Web::Simple 'NameOfApplication';
102 This imports 'strict' and 'warnings FATAL => "all"' into your code as well,
103 so you can skip the usual
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.
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
117 package NameOfApplication;
119 extends 'Web::Simple::Application';
122 It also exports the following subroutines for use in dispatchers:
124 response_filter { ... };
126 redispatch_to '/somewhere';
130 $INC{"NameOfApplication.pm"} = 'Set by "use Web::Simple;" invocation';
132 so that perl will not attempt to load the application again even if
134 require NameOfApplication;
136 is encountered in other code.
138 =head1 DISPATCH STRATEGY
142 sub dispatch_request {
143 # matches: GET /user/1.htm?show_details=1
145 sub (GET + /user/* + ?show_details~ + .htm|.html|.xhtml) {
146 my ($self, $user_id, $show_details) = @_;
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) = @_;
155 # matches: DELETE /user/1/friend/2
156 sub (DELETE + /user/*/friend/*) {
157 my ($self, $user_id, $friend_id) = @_;
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) = @_;
167 # matches: PUT /user/1/role/1
168 sub (PUT + /role/*) {
172 # matches: DELETE /user/1/role/1
173 sub (DELETE + /role/*) {
181 Description of the dispatcher object
183 Web::Simple::Dispatcher objects have three components:
187 =item * match - an optional test if this dispatcher matches the request
189 =item * call - a routine to call if this dispatcher matches (or has no match)
191 =item * next - the next dispatcher to call
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.
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
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.
207 If this routine returns (), Web::Simple treats this identically to a failure
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.
214 If this routine returns anything else, that is treated as the end of dispatch
215 and the value is returned.
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.
222 =head2 How Web::Simple builds dispatcher objects for you
224 In the case of the Web::Simple L</dispatch> export the match is constructed
225 from the subroutine prototype - i.e.
227 sub (<match specification>) {
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 -
237 [ 404, [ 'Content-type', 'text/plain' ], [ 'Error: Not Found' ] ]
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.
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:
247 sub (<match specification>) {
248 my ($self, @args) = @_;
252 =head2 Web::Simple match specifications
254 =head3 Method matches
258 A match specification beginning with a capital letter matches HTTP requests
259 with that request method.
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
270 $self->handle_user($_[1])
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:
277 my ($self, $user_1, $user_2) = @_;
279 sub (/domain/*/user/*) {
280 my ($self, $domain, $user) = @_;
282 and so on. To match an arbitrary number of parts, use -
286 This will result in an element per /-separated part so matched. Note that
289 sub (/page/**/edit) {
291 to match an arbitrary number of parts up to but not including some final
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.
303 Note that the '...' is a "maybe something here, maybe not" so the above
304 specification will match like this:
307 /foo/ # match and strip path to '/'
308 /foo/bar/baz # match and strip path to '/bar/baz'
310 =head3 Extension matches
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.
318 response_filter { $self->render_html($_[1]) }
325 will match any extension and supplies the stripped extension as a match
328 =head3 Query and body parameter matches
330 Query and body parameters can be match via
332 sub (?<param spec>) { # match URI query
333 sub (%<param spec>) { # match body params
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).
339 The param spec is elements of one of the following forms -
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
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.
356 So, to match a page parameter with an optional order_by parameter one
359 sub (?page=&order_by~) {
360 my ($self, $page, $order_by) = @_;
361 return unless $page =~ /^\d+$/;
364 $_[1]->search_rs({}, $p);
368 to implement paging and ordering against a L<DBIx::Class::ResultSet> object.
370 Note that if a parameter is specified as single and multiple values are found,
371 the last one will be used.
373 To get all parameters as a hashref of arrayrefs, write:
376 my ($self, $params) = @_;
379 To get two parameters as a hashref, write:
381 sub(?:user~&:domain~) {
382 my ($self, $params) = @_; # params contains only 'user' and 'domain' keys
384 You can also mix these, so:
386 sub (?foo=&@bar~&:coffee=&@*) {
387 my ($self, $foo, $bar, $params);
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.
393 =head3 Combining matches
395 Matches may be combined with the + character - e.g.
397 sub (GET + /user/*) {
399 to create an AND match. They may also be combined withe the | character - e.g.
403 to create an OR match. Matches can be nested with () - e.g.
405 sub ((GET|POST) + /user/*) {
407 and negated with ! - e.g.
409 sub (!/user/foo + /user/*) {
411 ! binds to the immediate rightmost match specification, so if you want
412 to negate a combination you will need to use
414 sub ( !(POST|PUT|DELETE) ) {
416 and | binds tighter than +, so
418 sub ((GET|POST) + /user/*) {
422 sub (GET|POST + /user/*) {
426 sub ((GET + .html) | (POST + .html)) {
430 sub (GET + .html | POST + .html) {
432 are not - the latter is equivalent to
434 sub (GET + (.html|POST) + .html) {
436 which will never match.
440 Note that for legibility you are permitted to use whitespace -
442 sub (GET + /user/*) {
444 but it will be ignored. This is because the perl parser strips whitespace
445 from subroutine prototypes, so this is equivalent to
449 =head3 Accessing the PSGI env hash
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.
456 sub (GET + /foo + ?some_param=) {
457 my($self, $some_param, $env) = @_[0, 1, PSGI_ENV];
459 =head2 Dispatcher return values
461 A dispatcher returns one of:
463 =head1 EXPORTED SUBROUTINES
465 =head2 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]}] ];
475 The response_filter subroutine is designed for use inside dispatch subroutines.
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.
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.
487 redispatch_to '/other/url';
489 The redispatch_to subroutine is designed for use inside dispatch subroutines.
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.
495 Thus if you receive a POST to '/some/url' and return a redipstch to
496 '/other/url', the dispatch behaviour will be exactly as if the same POST
497 request had been made to '/other/url' instead.
501 subdispatch sub (/user/*/) {
502 my $u = $self->user($_[1]);
505 sub (DELETE) { $u->delete },
509 The subdispatch subroutine is designed for use in dispatcher construction.
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
519 =head1 CHANGES BETWEEN RELEASES
521 =head2 Changes between 0.004 and 0.005
525 =item * dispatch {} replaced by declaring a dispatch_request method
527 dispatch {} has gone away - instead, you write:
529 sub dispatch_request {
530 sub (GET /foo/) { ... },
534 Note that this method is still -returning- the dispatch code - just like
537 =item * subdispatch deleted - all dispatchers can now subdispatch
539 In earlier releases you needed to write:
541 subdispatch sub (/foo/...) {
544 sub (GET /bar/) { ... },
549 As of 0.005, you can instead write simply:
554 sub (GET /bar/) { ... },
559 =head2 Changes since Antiquated Perl
563 =item * filter_response renamed to response_filter
565 This is a pure rename; a global search and replace should fix it.
567 =item * dispatch [] changed to dispatch {}
571 dispatch [ sub(...) { ... }, ... ];
575 dispatch { sub(...) { ... }, ... };
581 =head1 COMMUNITY AND SUPPORT
585 irc.perl.org #web-simple
587 =head2 No mailing list yet
589 Because mst's non-work email is a bombsite so he'd never read it anyway.
591 =head2 Git repository
593 Gitweb is on http://git.shadowcat.co.uk/ and the clone URL is:
595 git clone git://git.shadowcat.co.uk/catagits/Web-Simple.git
599 Matt S. Trout <mst@shadowcat.co.uk>
603 None required yet. Maybe this module is perfect (hahahahaha ...).
607 Copyright (c) 2009 the Web::Simple L</AUTHOR> and L</CONTRIBUTORS>
612 This library is free software and may be distributed under the same terms