[Catalyst] RFC: Multiple :Chained and/or :PathPart attributes?
Jason Kohles
email at jasonkohles.com
Mon Aug 20 14:07:31 GMT 2007
Currently, attempting to pass multiple arguments to either Chained or
PathPart attributes in a controller simply results in an exception
being thrown indicating that you can't do that, I'm thinking it might
be useful to change that behavior to allow for multiple arguments to
simply build multiple chains that take the same path. As an example,
here is the code that led me to this thinking...
I was working on an application that includes page revisioning, and
ended up with this code, which seems excessively verbose to me...
The requirements were:
* Wiki-like page structure
* /page/SomePage displays the current revision of SomePage
* /page/SomePage/revision/3 displays revision number 3 of SomePage
* /page/SomePage/rev/3 also displays revision number 3 of SomePage
* /page/SomePage/date/2007-01-01 displays whatever revision was
current on 2007-01-01 (or show a selector if there was more than one
change that day)
* /page/SomePage/datetime/2007-01-01T12:10:00 displays whatever
revision was current at that time
* For ALL of the above URLs, the page should be displayed if there
are no other path parts, or if the URL ends with /view
These requirements led to this code...
sub page : Chained('/') PathPart('page') CaptureArgs(1) {
my ( $self, $c, $page ) = @_;
# load page data for the indicated page into the stash
}
sub revision : Chained('page') PathPart('revision') CaptureArgs(1) {
my ( $self, $c, $rev ) = @_;
$c->forward( 'handle_revision', [ $rev ] );
}
sub rev : Chained('page') PathPart('rev') CaptureArgs(1) {
my ( $self, $c, $rev ) = @_;
$c->forward( 'handle_revision', [ $rev ] );
}
sub handle_revision : Private {
my ( $self, $c ) = @_;
# load the revision indicated in $c->request->args[0] into the stash
}
sub date : Chained('page') PathPart('date') CaptureArgs(1) {
my ( $self, $c, $date ) = @_;
$c->forward( 'handle_datetime', [ $date ] );
}
sub datetime : Chained('page') PathPart('datetime') CaptureArgs(1) {
my ( $self, $c, $date ) = @_;
$c->forward( 'handle_datetime', [ $date ] );
}
sub handle_datetime : Private {
my ( $self, $c ) = @_;
# create a datetime object from $c->request->args->[0]
# load the appropriate revision into the stash
}
sub view : Chained('page') PathPart('view') Args(0) {
my ( $self, $c ) = @_;
$c->forward( 'process_view' );
}
# All of these subs have the exact same body as 'view', which does
nothing but forward to 'process_view'
sub view_default : Chained('page') PathPart('') Args(0) {
sub view_revision : Chained('revision') PathPart('view') Args(0) {}
sub view_revision_default : Chained('revision') PathPart('') Args(0) {}
sub view_rev : Chained('rev') PathPart('view') Args(0) {}
sub view_rev_default : Chained('rev') PathPart('') Args(0) {}
sub view_date : Chained('date') PathPart('view') Args(0) {}
sub view_date_default : Chained('date') PathPart('') Args(0) {}
sub view_datetime : Chained('datetime') PathPart('view') Args(0) {}
sub view_datetime_default : Chained('datetime') PathPart('') Args(0) {}
sub process_view : Private {
my ( $self, $c ) = @_;
if ( $c->stash->{ 'matches' } ) {
$c->stash->{ 'template' } = 'select_revision.tt';
} elsif ( $c->stash->{ 'page' } ) {
$c->stash->{ 'template' } = 'display_page.tt';
} else {
$c->stash->{ 'template' } = 'not_found.tt';
}
}
If the Chained and PathPart attributes could take multiple arguments
that would simply build multiple chains, then I could have
significantly reduced the amount of repetition, by doing something
along these lines instead...
sub page : Chained('/') PathPart('page') CaptureArgs(1) {
my ( $self, $c, $page ) = @_;
# load page data for the indicated page into the stash
}
sub revision : Chained('page') PathPart('revision','rev') CaptureArgs
(1) {
my ( $self, $c, $rev ) = @_;
# load the indicated revision into the stash
}
sub date : Chained('page') PathPart('date','datetime') CaptureArgs(1) {
my ( $self, $c, $date ) = @_;
# load the appropriate revision into the stash
}
sub view : Chained('page','revision','date') PathPart('','view') Args
(0) {
my ( $self, $c ) = @_;
if ( $c->stash->{ 'matches' } ) {
$c->stash->{ 'template' } = 'select_revision.tt';
} elsif ( $c->stash->{ 'page' } ) {
$c->stash->{ 'template' } = 'display_page.tt';
} else {
$c->stash->{ 'template' } = 'not_found.tt';
}
}
I tried to put together a patch that could do this, but this is my
first trip into the innards of the dispatcher, and it looks like it's
going to take a while to wrap my brain around it, so if anyone has
suggestions on how to implement this (or reasons that it's a very bad
idea) I'd love to hear them...
--
Jason Kohles
email at jasonkohles.com
http://www.jasonkohles.com/
"A witty saying proves nothing." -- Voltaire
More information about the Catalyst
mailing list