Re: Mapping URLs to code - in search of perlish dispatch schemes
by rhesa (Vicar) on Jul 20, 2006 at 20:12 UTC
|
CGI::Application::Dispatch is another data point. It uses a Rails-like dispatch table definition, which feels pretty comfortable to me. There's also a CGI::Application::Dispatch::Regexp, but I haven't used that one.
As far as I know, there are no dispatchers based on query parameters. There may be a good reason for that.
My first impression is that I'd rather keep parameter validation close to the handler code for a particular url. And I can't see a clear way to handle POST requests either, which to me implies you'd be doing the same thing in two places. | [reply] |
|
|
Thanks for CGI::Application::Dispatch - I'll add that to my above roundup table. I don't quite understand the finer points of the dispatch interface, but some longer study of the documentation will hopefully tell me ;-)
My idea would work transparently for GET and POST queries because CGI (as an example) abstracts both into the ->param() function and even mixes and mashes the parameters together if I tell it to.
To me, the query path and the query parameters are more or less the same, except that the query path has no name, while the query parameters are name-value pairs. The functional programming languages (I'm thinking of Haskell) also have value-based function signatures, and my approach would be similar to that.
| [reply] [d/l] |
|
|
Gotcha. I thought you were just looking at throwing regexes at the request URI, but you're going one further. At the risk of over-stretching the analogy, it looks to me like you're going to do compile-time method dispatching and type checking, instead of at run-time. I can see value in that, although we could debate endlessly whether that is the perlish way ;)
I'm interested to see what you come up with. I suppose I always felt that the uri-path is similar to a method name, while the query parameters are similar to its input parameters. Although CA::Dispatch's named captures muddles the water there, and that's also quite a useful feature. I'm still mostly using old-fashioned urls like /book/edit?id=1 instead of the more modern /book/edit/1, but I'm slowly catching up :)
| [reply] [d/l] [select] |
|
|
| [reply] |
Re: Mapping URLs to code - in search of perlish dispatch schemes
by diotalevi (Canon) on Jul 20, 2006 at 22:16 UTC
|
Let's you and me get Ovid to release a copy of AI::Prolog which can callback into perl. Interpolation of variables in strings would be nice too.
% Edit!
request :-
url( 'edit' ),
not( node( _ ) ),
error( "Edit *which* node?!" ).
request :-
url( 'edit' ),
node( Node ),
do_edit_node( Node ), % dispatch perl
print( " ... " ).
% Create!
request :-
url( 'create' ),
not( code( _ ) ),
error( "You didn't give any code!" ).
request :-
url( 'create' ),
not( name( _ ) ),
error( "You didn't give a name!" ).
request :-
url( 'create' ),
name( Name ),
code( Code ),
do_create_node( Name, Code ), % dispatch perl
print( "Created $Name { $Code }" ).
request :-
error( "I can't handle the stress!" ).
| [reply] [d/l] |
|
|
% prolog follows.
perlcall( Function, Args ) :-
perlcall2( Function, Args ),
!.
do_edit_node( Node ) :-
perlcall( "do_edit_node", [ Node ] ).
error( Message ) :-
perlcall( "error", [ Message ] ).
do_create_node( Node, Code ) :-
perlcall( "do_create_node", [ Node, Code ] ).
| [reply] [d/l] |
|
|
I wish I had seen this earlier. The patch I've looked at seems interesting and I'll see if I can fit it into to an "official" release this weekend. Does it support stuff like functions which keep returning different values, such as a call into a database? I've had some thoughts about that, but it would be tricky.
| [reply] |
|
|
Earlier? I wrote the patch hours before you noticed it. I was just another iteration of wishing outloud for that feature we've talked about before but have now. There are some major deficiencies with my patch. I think I need to call your unify function but didn't. I think that the normal perlcall/2 should only succeed and call the perl function if all the variables are bound. I think I made a mess of converting your Terms back into perl data. I did something that worked but I didn't understand your organization (you didn't document it!) and probably got it wrong. The current perlcall2/2 searches upwards through the call stack to find the function named X. That should probably be saner - maybe introduce a perlpackage/1 builtin. Maybe tell perlcall2/2 how to tell the difference between fully qualified function names and relative names. There should be a perlmethod2/2 as well to support method calls. Yes? Much of this can be experimented with once there's a few builtins for looking back at perl.
perlcall(X,Y) :-
boundp(X), % uh... a predicate for asking that X need no further u
+nification so perlcall only gets complete answers
boundp(Y),
perlcall2(X,Y),
!.
| [reply] [d/l] [select] |
Re: Mapping URLs to code - in search of perlish dispatch schemes
by shmem (Chancellor) on Jul 20, 2006 at 20:19 UTC
|
Hello Corion,
for now I have only skimmed your post, but from what I've seen I want to encourage you to follow the stony path to a clean and solid implemetation of URL mapping and parameter validation - a common dispatch language for web applications so to say, which covers most (if not all) eventualities and flavours of requests.
This is one area of web programming which AFAIK ever since has been buried in frameworks, and decisions were made upon implementation by mere gusto or style or design inclinations than by actually pursuing a path which adheres closest to the perl way (or more perl ways than one) of seeing things.
Thanks for bringing up this question. I would consider it rather a meditation since it's a topic to meditate upon, and not just answer. I'll append my thoughts here as soon as I have time - have to get that train.
update
Sorry I'm thinking aloud here, and most of this might have passed
through your mind during this day, I've seen your thoughts evolving
from the sparse comments in the CB.
Lets first name some things before even thinking of any language.
The big problem in URL to perl function mapping is TIMTOWTDI, a
strength of both perl and fools. While virtually any programming model
can be achieved with perl by including nifty packages (think all the
Class:: stuff around, which gives us reflection, function propagation
through inheritance chains, private and public class stuff, attribute
handling etc), I would encourage an implemetation path which adheres
closely to the perl core, which doesn't preclude any implemetation
flavour you might choose for a particular task or programming paradigma.
Perhaps it might be helpful to examine and classify the bunch of
information given by a HTTP request, and compare its elements to
perl thingies.
Oh, wait... that would lead to a naive approach, such as translating
a PATH_TRANSLATED eq /root/some/foo/request?foo=bar to
Root::Some::Foo->request(foo => bar) inside an application (or calling
of methods w/attributes).
But the parsing requirement might be totally different, key/value pairs
of the QUERY_STRING might elect (or constrain) the function to dispatch
(or reduce the dispatching possibilities). Path elements might be used
as parameters (think of a request like /foo=bar/quux=blorf/what?nothing)
and then there might be requests like /foo/1 /foo/2 /foo/3 which resemble
a server side stored history of requests which are further parametrized
e.g. by cookies or encryption keys and walkable to and fro.
Order of parameters might matter; the QUERY_STRING could resemble a
chain of methods/functions/whatever called in order with (equally?) named
parameters. Part of an URI could be one or multiple keys used for
decryption and/or database lookup, session key and so forth. While there
surely are Best Practices[tm], anything is possible.
So, to get at something generic, the least common denominator for all
possibilities must be found, and since even the least common denominator
is not known, it must be configurable.
This sounds much like either a) a tree or b) a dispatch table ... oh wait.
Same naive approach as above.
There's a least common denominator in each handling method of URI paths
and QUERY_STRINGS. May be more than one. I.e. there's always something
of any type that will be checked first, maybe against a set of
posibilities, which might be ordered in importance or not.
So, your approach should provide for
- a way to set up a grammar in URI/QUERY_STRINGS handling
- iterative matching / processing with and without execution order
- (non-)destructive pipelining of matches
(to be continued..)
cheers,
--shmem
_($_=" "x(1<<5)."?\n".q·/)Oo. G°\ /
/\_¯/(q /
---------------------------- \__(m.====·.(_("always off the crowd"))."·
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
| [reply] |
Re: Mapping URLs to code - in search of perlish dispatch schemes
by CountZero (Bishop) on Jul 20, 2006 at 21:14 UTC
|
Catalyst has a module Catalyst::Plugin::AutoSession which automatically puts request parameters into session variables. It looks to me that this technique could be the beginning of your parameter mapping scheme. The URL mapping scheme exists already in Catalyst: you can perhaps write your parameter mapping scheme into a Catalyst::Plugin and make a helper-script which can translate your declarative language into a Catalyst application.
CountZero "If you have four groups working on a compiler, you'll get a 4-pass compiler." - Conway's Law
| [reply] |
|
|
Hmmm - I'm not sure I understand what Catalyst::Plugin::AutoSession does - it seems to be geared at extracting exactly one parameter out of the URL. I'm looking more in the direction of a mini language that I can use to dispatch any URL to the most-fitting code. I'm not looking for a framework-specific solution because I haven't found a framework that I like.
Of course, any URL-specification must be translated into something that the backend framework can understand, but firstly, I'm looking at what possibilities the frameworks offer to declare my wishes.
| [reply] |
|
|
| [reply] |
Re: Mapping URLs to code - in search of perlish dispatch schemes
by eric256 (Parson) on Jul 20, 2006 at 23:05 UTC
|
A straight regex like that is dangerous because the order of the keys could change. To me it looks like you are trying to map different parameter combinations/values to subs. So how about something like:
map_query($cgi,
{
_page => 'edit',
node => /(\w+)/,
code => sub { print "You're trying to edit '$node'" };
},{
_page => 'create'
name => /(\w+)/,
_code => sub { print "You're trying to create '$name' but gave no
+ code to fill it"; }
},{
_page => 'create',
name => /(\w+)/,
code => /(.*)/,
_code => sub { print "Created as sub $name { $code }"; };
},{
_page => /.*/,
_code => sub { print "Don't know how to handle " . $q->query_path
+; }
}
);
It is just a rough first attack and i think there is another level of abstraction in there, but you get the idea. It would also be friendly to both post/get requests
| [reply] [d/l] |
|
|
That's too wordy for me. I want to write it like a regex, but expect it to match like smarter Perl code - so my "regex"-like syntax would then be translated into your syntax or diotalevi's elegant Prolog example, nothingmuch's Monju model or maybe even an XML document against which XPath queries are run to determine the matching nodes.
| [reply] |
|
|
I like both - thought I think the structured args approach will be mucho clearer for perl newbies and casuals.
I am worried about the efficiency of matching an actual URL against the parsed definitions. Do you have thoughts on how you are going to make this efficient? Efficiency being memory based for some, and speed based for others?
| [reply] |
|
|
Re: Mapping URLs to code - in search of perlish dispatch schemes
by jaa (Friar) on Jul 21, 2006 at 09:47 UTC
|
I am coming to like this URL matching even more... :) are there
any polymorphic lessons to be learnt from other OO languages that might help?
Another thought springs to mind - is there a place for a hierarchy in your
interface? It might also assist in despatch efficiency, or even just remind the
using programmer to consider despatch efficiency. I don't know if it would have any relevance to handling multi-
matching despatches - maybe this is how you specify a multi-despatch?
# flat
/edit?name=(\w+);city=(\w+);... {
$r->print "Edit '$name' in '$city'";
};
/edit?name=(\w+);job=(.*);... {
$r->print "Edit '$name' who does '$job'";
};
# hierarchical1
/edit => [
name=(\w+);city=(\w+);... {
$r->print "Edit '$name' in '$city'";
},
name=(\w+);job=(.*);... {
$r->print "Edit '$name' who does '$job'";
},
]
# hierarchical2
/edit => [
name=(\w+);... => [
city=(\w+);... {
$r->print "Edit '$name' in '$city'";
},
job=(.*);... {
$r->print "Edit '$name' who does '$job'";
},
],
]
# hierarchical3
/edit => {
/contact => [
name=(\w+);city=(\w+);... {
$r->print "Edit '$name' in '$city'";
},
name=(\w+);job=(.*);... {
$r->print "Edit '$name' who does '$job'";
},
],
/basket => [
items=[(\w+)];... {
$r->print "Edit items in basket";
},
delivery=(\w+);... {
$r->print "Edit delivery details";
},
],
}
etc
| [reply] [d/l] |
Re: Mapping URLs to code - in search of perlish dispatch schemes
by jaa (Friar) on Jul 21, 2006 at 09:23 UTC
|
Sounds great!
The following (not very carefully considered) thoughts spring to mind...
Would you consider making it more generic, and not tied specifically to CGI?
It might be nice to have in a mod_perl handler too, where we might assume $r
instead of $q, and any other variations as well.
If you do this, you would no doubt create a base despatch and validation class, and then specialise this to provide natural args in each environment - e.g. a CGI implementation would provide natural CGI
args, and a mod_perl method would provide the request handle $r etc.
The subs will probably need standardised, but more complex arg handling - how
about something like:
/edit?items=[(\w+)];location=(\w+);flags={(\d+)};...
{
if ( $arg->{location} eq 'US' ) {
for my $item ( @{$arg->{items}} ) {
if ( $arg->{flags}{54} ) {
$r->print "item: $item qualifies for discount 54 processing<
+br>\n";
}
}
}
# other optional args that we may / may not have
if ( defined $arg->{style} ) {
$r->print("you have style! $arg->{style}<br>\n"
}
};
# args are placed in hashref $arg
# [] indicates that the named arg should be an array.
# {} indicates that the named arg should be a hash.
# for a URL of flags=54&flags=23&flags=red
# might yeild
# $arg->{flags} = {
# 54 => 54,
# 23 => 23,
# red => 'red',
# };
# so that programmers can use keys values as they are comfy?
# optional args (ie the ...) are available in $arg
On validation of params, you might consider an additional section for validation subs for the named args
:DESPATCH
/edit?node=(\w+);... {
print "You're trying to edit '$node'";
};
:PARAM
node {
my ( $node ) = @_;
my $db = My::Database->new(...);
return $node if $db->selectValue( sql => "select count(*) from nod
+es where node=?", param => [$node] );
print "invalid node: $node";
return undef;
}
The despatch fall-through case /(.*) should probably be handled by the despatch
class, by default - it will have to do something if nothing matches!
You will also have to resolve conflicts where a URL matches more than one sub
/edit?name=(\w+);city=(\w+);... {
print "Edit '$name' in '$city'";
};
/edit?name=(\w+);job=(.*);... {
print "Edit '$name' who does '$job'";
};
Various approaches spring to mind, generally the most specific wins, but if the despatch matches are equally specific?
- ordering of definitions might define a natual priority - call the first
- call all subs that match at highest found specifity in defined order
- let the user decide to fail on multi-despatch
- etc?
Regards,
Jeff
| [reply] [d/l] [select] |
Re: Mapping URLs to code - in search of perlish dispatch schemes
by phaylon (Curate) on Jul 21, 2006 at 10:15 UTC
|
As of Version 5.7, Catalyst has the Chained dispatch type, which makes the common stuff regexes were used for more easy:
# chain to /
sub local : Chained PathPart('foo') { ... }
# chain to the foo action
sub bar : Chained('foo') CaptureArgs(1) { ... }
# endpoint, chain to bar action
sub baz : Chained('bar') Args(1) { ... }
This gives a /foo/bar/*/baz/* resource where * stands for one argument. If /foo/bar/23/baz/17 is requested, first 'foo' will be run, then 'bar' with '23' as argument, and at last 'baz' with an argument of '17'.
For more information see Catalyst::DispatchType::Chained
Ordinary morality is for ordinary people. -- Aleister Crowley
| [reply] [d/l] [select] |
|
|
While this syntax is too verbose for my taste, does the module allow for implicit untainting? I would imagine a second parameter to the Args or CaptureArgs attribute, that specifies the regex or code to be run.
| [reply] [d/l] [select] |
|
|
| [reply] |
Re: Mapping URLs to code - in search of perlish dispatch schemes
by Moron (Curate) on Jul 21, 2006 at 09:50 UTC
|
What I do in these situations is organise the rules into an array of hashes in the order for evaluation, something like: (updated for clarity) use AnyLanguage;
my @lexicon = [ ( REGEXP => regex1, ACTION => sub { code1 } ),
...
( ACTION => sub { default code } )
];
my $MyLanguage = AnyLanguage -> new( \@lexicon );
...
while ( my $input = ... ) {
$MyLanguage -> dispatch{ $input };
}
whose class (for all languages/rule sets) would be something like:
package AnyLanguage;
sub new {
my $self = shift;
my $self -> { LEXICON } = shift;
return bless $self;
}
sub dispatch{ # match down array and react accordingly
my $self = shift;
$_ = shift;
my $aref = $self -> { LEXICON };
for ( my $i = 0; $i < $#$aref; $i++ ) {
my $regexp = $aref -> [$i] -> { REGEXP };
/$regexp/ and
return &($aref -> [$i] -> { ACTION }};
}
return &{$aref -> [$#$aref ] -> { ACTION }};
}
1;
Update: Although as MyLanguage expands in complexity and usage, I would at some point in practice go one step further and package up all the 'action' code for language MyLanguage into its own MyLanguage class and simplify the action code into calls to methods of such a class, so that AnyLanguage ceases to be used directly by the 'outer' code, but by the methods in MyLanguage. In that case, the initialisation of the LEXICON for Mylanguage be moved to the new method of the MyLanguage class, before being passed on to the new method of the AnyLanguage class.
more update: it is also possible to maintain a separate dispatcher and parser (more likely wrapper for e.g. Parse::RecDescent) in the same AnyLanguage module, using a flag to tell AnyLanguage whether MyLanguage is dispatchable or not, (amongst possibly other flags for such critical language characteristic variations) to avoid having to duplicate code for different statement formats.
| [reply] [d/l] [select] |
Re: Mapping URLs to code - in search of perlish dispatch schemes (test cases, and code, but no name)
by Corion (Patriarch) on Jul 23, 2006 at 19:07 UTC
|
I've banged out some test cases and now have some tests and some working code. The code is a very rough cut and quite fragile, as I pick the string apart by using regular expressions instead of Parse::YAPP. The code will most likely break if your regular expressions include one of the chars /; - which I consider unlikely, and ? also doesn't receive enough attention.
The current name is "URL::Deconstruct", but I lean more and more towards URL::HTTP::Deconstruct, as it only understands the path and query components of HTTP requests and nothing else.
First, a discussion of the syntax I've settled on:
The syntax more or less adapts the regular expression
syntax to HTTP request URIs. The matcher returns name/value pairs
of the matched components. The names for the query parameters are
the keys, the names for the path component must be given by you.
Currently the following magic is applied to your
regular expression
/($<foo>.*)/
Will capture into the name foo.
.* and .+ and their nongreedy counterparts
are implicitly translated to mean [^/] and [^/]+.
Examples:
?foo=bar;
will match when there is a HTTP query parameter foo
with the value bar. The pair will be returned.
?foo=bar;baz=bop
will match when there is a HTTP query parameter foo
with the value bar and there is a HTTP query parameter
baz with the value bop. The order of the two parameters
is not relevant. The pairs will be returned in the order specified
in the pattern, not in the actual order of the HTTP request.
?foo=(.*);
will match when there is a HTTP query parameter foo
with any value. The pair foo => $q->param('foo')
will be returned. Note that .* is implicitly translated
to only match up to the next HTTP parameter delimiter.
/view/(?<foo>\w+)/
will match when there the path component matches /view/\w+/
and will capture the second path component into the key
foo. Note that .+ is implicitly translated
into [^/]+
to only match up to the next path component.
Named captures of the path component default to matching \w+, so a shorthand
for the above specification would be:
/view/(?<foo>)/
The module API is object oriented, which should give the possible front ends enough leeway to
incorporate it:
use URL::Deconstruct;
my $my_request = URL::Deconstruct->from_spec(
q'/(?<model>)/(?<action>)/(?<id>\d+)?order_by=(\w+)'
);
if (my %params = $my_request->match('/beer/drink/1?order_by=11') {
print "$params{model}\n"; # beer
print "$params{order_by}\n"; # 11
}
The module currently looks like this and will soon hit CPAN in a slightly more polished form, hopefully with a better name:
package URL::Deconstruct;
use strict;
use base 'Class::Accessor';
use Carp qw(croak);
use List::MoreUtils qw(zip);
use Data::Dumper;
=head1 NAME
URL::Deconstruct - match-and-extract stuff from HTTP request strings
=head1 SYNOPSIS
use URL::Deconstruct;
my $my_request = URL::Deconstruct->from_spec(
q'/(?<model>)/(?<action>)/(?<id>\d+)?order_by=(\w+)'
);
if (my %params = $my_request->match('/beer/drink/1?order_by=11') {
print "$params{model}\n"; # beer
print "$params{order_by}\n"; # 11
}
=head1 REQUEST SYNTAX
The syntax more or less adapts the regular expression
syntax to HTTP request URIs.
Currently the following magic is applied to your
regular expression
/($<foo>.*)/
Will capture into the name C<foo>.
C<.*> and C<.+> and their nongreedy counterparts
are implicitly translated to mean C<[^/]*> and C<[^/]+>.
Examples:
?foo=bar;
will match when there is a HTTP query parameter C<foo>
with the value C<bar>. The pair will be returned.
?foo=bar;baz=bop
will match when there is a HTTP query parameter C<foo>
with the value C<bar> I<and> there is a HTTP query parameter
C<baz> with the value C<bop>. The order of the two parameters
is not relevant. The pairs will be returned in the order specified
in the pattern, not in the actual order of the HTTP request.
?foo=(.*);
will match when there is a HTTP query parameter C<foo>
with any value. The pair C<< foo => $q->param('foo') >>
will be returned. Note that C<.*> is implicitly translated
to only match up to the next HTTP parameter delimiter.
/view/(?$<foo>.+)/
will match when there the path component matches C</view/[^/]+/>
and will capture the second path component into the key
C<foo>. Note that C<.+> is implicitly translated
to only match up to the next path component.
=cut
__PACKAGE__->mk_accessors(qw(re path_names ));
sub from_spec {
my ($package,$spec,%options) = @_;
my ($re,$path_names) = $package->make_cgi_re($spec);
$package->SUPER::new({
re => $re,
path_names => $path_names,
});
}
=head2 C<< $m->require_param NAME, VALUE >>
This internal method returns the regular expression
to match a HTTP query parameter and its name.
NAME is the name of the key into which the value will
be captured.
VALUE is the regular expression that will match
the value.
=cut
sub require_param {
my ($self,$name,$value) = @_;
if ($value =~ /\.\*/) {
$value =~ s/\.\*/[^;&]\*/g;
}
return qr/(?=.*?(?:[;&?])($name)=($value)(?:[;&]|$))/;
}
=head2 C<< $m->make_cgi_re >>
This is the internal method that implements the meat
of the request decoder. It creates a regular expression
that will match and capture the request fields.
=cut
sub make_cgi_re {
my ($package,$re) = @_;
my $res = $re;
my @path_names;
my ($path,$params) = $re,undef;
if ($res =~ /[^(]\?/) {
# Quite crudely split off the query:
if ($res !~ m!^(.*?[^(])\?(\w+=[^/]+)$!) {
croak "Cannot determine path and query parts of $re";
};
($path,$params) = ($1,$2);
};
if ($path) {
my @elems = map {
#warn $_;
if (/^\(\?<(\w+)>(.*)\)$/) {
push @path_names, $1;
# Hack in implicit declaration if none given
#warn ">$2<";
if ("" eq $2) {
$_ = "(\\w+)";
} else {
$_ = "($2)";
}
s!\.([*+])![^/]$1!;
} elsif (/^\(.*\)$/) {
croak "Cannot capture path elements into unnamed field
+s. Use (?<name>)";
}
defined $_ ? $_ : "^"
} split m!/!, $path;
$path = join "/", @elems;
}
if ($params) {
my @elems = map {
# handle foo=(.*) -> (foo=>'bar')
if (/^(\w+)=\((.*)\)$/) {
$_ = $package->require_param($1,$2);
# handle foo=bar -> (foo=>'bar')
} elsif (/^(\w+)=(.+)/) {
$_ = $package->require_param($1,$2);
}
$_
} split /[&;]/, $params;
$params = join "", @elems;
$params = "$params.*?(?:[;&]|\$)";
} else {
$params = "";
}
$res = "^$path$params";
qr/$res/, \@path_names
}
=head2 C<< $m->request_matches($url) >>
Returns a list of captured values
if the request matches.
If the request matches but does not capture anything,
a single 1 is returned. This is ugly but such is life.
=cut
sub match {
my ($self,$req) = @_;
# create a careful splitter that maps the path names
# conveniently into the result...
my $re = $self->re;
#warn "$req =~ /$re/ ?";
if (my @res = $req =~ /$re/) {
# two steps because I'm not sure about order of execution
my $n = $self->path_names;
#warn Dumper \@res;
my @path = splice @res, 0, scalar @$n;
return zip(@$n,@path),@res
} else {
return
}
}
1;
__END__
=head1 TODO
=item * Think about making C<&> a coderef/callback feature
for validation.
An example would be:
sub model_can {
my ($method) = @_;
My::Model->can($method);
}
/beer/(?<method>&model_can)/(?<id>)
This could call the C<model_can> callback. On the other hand,
this is probably total overkill and should be done in a second
validation step instead of cramming it all into one line.
This would need the C<?{}> callback feature so the previous
parameters are already set up. This is exceedingly ugly.
=item * Think about returning a plain string/piece of (templated)
Perl code to enable a dispatcher to be maximally efficient.
=item * Could many requests be mapped into one (huge) regex? Under the
assumption that there will always be enough parameters captured to
later on decode what was actually wanted...
=cut
| [reply] [d/l] [select] |