X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FWeb%2FSimple.pm;h=5e38ba8684b1c108ac3a4846b2597d77c9c949bd;hb=74afe4b70dc0f4b5f8de55fb4d17c4863d842c29;hp=d1ded106b75afd0b397a25057da5b459c7788de8;hpb=44db8e762b9dc73b4938910a146327d79aa0a786;p=catagits%2FWeb-Simple.git diff --git a/lib/Web/Simple.pm b/lib/Web/Simple.pm index d1ded10..5e38ba8 100644 --- a/lib/Web/Simple.pm +++ b/lib/Web/Simple.pm @@ -30,12 +30,15 @@ sub _export_into { *{"${app_package}::dispatch"} = sub { $app_package->_setup_dispatcher(@_); }; - *{"${app_package}::filter_response"} = sub (&) { + *{"${app_package}::response_filter"} = sub (&) { $app_package->_construct_response_filter($_[0]); }; *{"${app_package}::redispatch_to"} = sub { $app_package->_construct_redispatch($_[0]); }; + *{"${app_package}::subdispatch"} = sub ($) { + $app_package->_construct_subdispatch($_[0]); + }; *{"${app_package}::default_config"} = sub { $app_package->_setup_default_config(@_); }; @@ -93,9 +96,11 @@ examples and non-CGI deployment, see below. =head1 WHY? -While I originally wrote Web::Simple as part of my Antiquated Perl talk for -Italian Perl Workshop 2009, I've found that having a bare minimum system for -writing web applications that doesn't drive me insane is rather nice. +Web::Simple was originally written to form part of my Antiquated Perl talk for +Italian Perl Workshop 2009, but in writing the bloggery example I realised +that having a bare minimum system for writing web applications that doesn't +drive me insane was rather nice and decided to spend my attempt at nanowrimo +for 2009 improving and documenting it to the point where others could use it. The philosophy of Web::Simple is to keep to an absolute bare minimum, for everything. It is not designed to be used for large scale applications; @@ -143,10 +148,12 @@ It also exports the following subroutines: dispatch [ sub (...) { ... }, ... ]; - filter_response { ... }; + response_filter { ... }; redispatch_to '/somewhere'; + subdispatch sub (...) { ... } + and creates a $self global variable in your application package, so you can use $self in dispatch subs without violating strict (Web::Simple::Application arranges for dispatch subroutines to have the correct $self in scope when @@ -201,8 +208,14 @@ the subroutines passed to it, which then creates your Web::Simple application's dispatcher from these subs. The prototype of the subroutine is expected to be a Web::Simple dispatch specification (see L below for more details), and the body of the -subroutine is the code to execute if the specification matches. See -L below for details on how the Web::Simple dispatch +subroutine is the code to execute if the specification matches. + +Each dispatcher is given the dispatcher constructed from the next element +of the arrayref as its next dispatcher, except for the final element, which +is given the return value of NameOfApplication->_build_final_dispatcher +as its next dispatcher (by default this returns a 500 error response). + +See L below for details on how the Web::Simple dispatch system uses the return values of these subroutines to determine how to continue, alter or abort dispatch. @@ -249,6 +262,26 @@ Thus if you receive a POST to '/some/url' and return a redipstch to '/other/url', the dispatch behaviour will be exactly as if the same POST request had been made to '/other/url' instead. +=head2 subdispatch + + subdispatch sub (/user/*/) { + my $u = $self->user($_[1]); + [ + sub (GET) { $u }, + sub (DELETE) { $u->delete }, + ] + } + +The subdispatch subroutine is designed for use in dispatcher construction. + +It creates a dispatcher which, if it matches, treats its return value not +as a final value but an arrayref of dispatch specifications such as could +be passed to the dispatch subroutine itself. These are turned into a dispatcher +which is then invoked. Any changes the match makes to the request are in +scope for this inner dispatcher only - so if the initial match is a +destructive one like .html the full path will be restored if the +subdispatch fails. + =head1 DISPATCH STRATEGY =head2 Description of the dispatcher object @@ -364,6 +397,22 @@ you can do to match an arbitrary number of parts up to but not including some final part. +Finally, + + sub (/foo/...) { + +will match /foo/ on the beginning of the path -and- strip it, much like +.html strips the extension. This is designed to be used to construct +nested dispatch structures, but can also prove useful for having e.g. an +optional language specification at the start of a path. + +Note that the '...' is a "maybe something here, maybe not" so the above +specification will match like this: + + /foo # no match + /foo/ # match and strip path to '/' + /foo/bar/baz # match and strip path to '/bar/baz' + =head3 Extension matches sub (.html) { @@ -372,20 +421,115 @@ will match and strip .html from the path (assuming the subroutine itself returns something, of course). This is normally used for rendering - e.g. sub (.html) { - filter_response { $self->render_html($_[1]) } + response_filter { $self->render_html($_[1]) } } +Additionally, + + sub (.*) { + +will match any extension and supplies the stripped extension as a match +argument. + +=head3 Query and body parameter matches + +Query and body parameters can be match via + + sub (?) { # match URI query + sub (%) { # match body params + +The body is only matched if the content type is +application/x-www-form-urlencoded (note this means that Web::Simple does +not yet handle uploads; this will be addressed in a later release). + +The param spec is elements of one of the following forms - + + param~ # optional parameter + param= # required parameter + @param~ # optional multiple parameter + @param= # required multiple parameter + * # include all other parameters + @* # include all other parameters as multiple + +separated by the & character. + +So, to match a page parameter with an optional order_by parameter one +would write: + + sub (?page=&order_by~) { + +Parameters selected are turned into a hashref; in the case of singular +parameters then if multiple values are found the last one is used. In the +case of multiple parameters an arrayref of all values (or an empty arrayref +for a missing optional) is used. The resulting hashref is provided as a +match argument. So we might write something like: + + sub (?page=&order_by~) { + my ($self, $p) = @_; + return unless $p->{page} =~ /^\d+$/; + $p->{order_by} ||= 'id'; + response_filter { + $_[1]->search_rs({}, $p); + } + } + +to implement paging and ordering against a L object. + =head3 Combining matches Matches may be combined with the + character - e.g. - sub (GET+/user/*) { + sub (GET + /user/*) { + +to create an AND match. They may also be combined withe the | character - e.g. + + sub (GET|POST) { + +to create an OR match. Matches can be nested with () - e.g. + + sub ((GET|POST) + /user/*) { + +and negated with ! - e.g. + + sub (!/user/foo + /user/*) { + +! binds to the immediate rightmost match specification, so if you want +to negate a combination you will need to use + + sub ( !(POST|PUT|DELETE) ) { + +and | binds tighter than +, so + + sub ((GET|POST) + /user/*) { + +and + + sub (GET|POST + /user/*) { + +are equivalent, but + + sub ((GET + .html) | (POST + .html)) { + +and + + sub (GET + .html | POST + .html) { + +are not - the latter is equivalent to + + sub (GET + (.html|POST) + .html) { + +which will never match. + +=head3 Whitespace Note that for legibility you are permitted to use whitespace - sub (GET + /user/*) { -but it will be ignored. +but it will be ignored. This is because the perl parser strips whitespace +from subroutine prototypes, so this is equivalent to + + sub (GET+/user/*) { =cut