change filter_response to response_filter in the places I got it wrong
[catagits/Web-Simple.git] / lib / Web / Simple.pm
index d1ded10..5e38ba8 100644 (file)
@@ -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</DISPATCH SPECIFICATIONS> below for more details), and the body of the
-subroutine is the code to execute if the specification matches. See
-L</DISPATCH STRATEGY> 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</DISPATCH STRATEGY> 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 (?<param spec>) { # match URI query
+  sub (%<param spec>) { # 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<DBIx::Class::ResultSet> 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