[Catalyst] PathPart help
Matt S Trout
dbix-class at trout.me.uk
Sat Nov 17 11:40:27 GMT 2007
On Fri, Nov 16, 2007 at 01:50:03PM -0500, Jason Kohles wrote:
> On Nov 16, 2007, at 11:11 AM, Christopher H. Laco wrote:
>
> >Interesting twist. Reminds me of the RHOX stuff..
> >
> >users/id/<id>
> >users/name/<name>
> >
> >etc. Damnit. Now you have me thinking again. That setup is a great
> >reason to keep REST and web controllers seperated. :/
> >
>
>
> The only problem with it is that I would love to be able to do this,
> but so far I haven't come up with a reasonable way to say 'chain from
> any of these controllers'. i.e. it would be really nice to be able
> to do this:
>
> sub base : Chained('/') PathPart('users') CaptureArgs(0) { }
>
> sub id : Chained('base') PathPart('id') CaptureArgs(1) { }
> sub name : Chained('base') PathPart('name') CaptureArgs(1) { }
> sub email : Chained('base') PathPart('email') CaptureArgs(1) { }
>
> And then be able to base later controllers on any of these, something
> like:
>
> sub edit : Chained('id','name','email') PathPart('edit') Args(0) { }
>
> I would also be nice to be able to have chain elements that didn't
> correspond to URLs, so you could do something like this:
>
> sub has_user : Chained('id|name|email') NoPathPart CaptureArgs(0) { }
> sub edit : Chained('has_user') PathPart('edit') Args(0) { }
>
>
> I keep planning to dig into the dispatcher and figure out how to
> implement this (and multiple PathParts), but never enough TUITs...
I intentionally -didn't- implement this when I wrote Chained; as soon as
you do that the $c->uri_for($action, ...) syntax becomes ambiguous and a
whole host of other problems crop up.
What I'd do instead is -
package MyApp::ControllerBase::HasObject;
sub has_object :PathPart('') :CaptureArgs(0)
sub edit :Chained('has_user') :PathPart('edit') :Args(0)
package MyApp::ControllerBase::ChainBase;
__PACKAGE__->mk_accessors(qw(object_chains));
__PACKAGE__->config(object_chains => []);
sub COMPONENT {
my $new = shift->NEXT::COMPONENT(@_);
foreach my $chain (@{$new->object_chains}) { # commented using example 'id'
my $action = $self->action_for($chain); # Catalyst::Action for /foo/id
my $pkg = ref($new).'::'.ucfirst($chain); # 'id' -> Controller::Foo::Id
{
no strict 'refs';
@{"${pkg}::ISA"} = 'MyApp::ControllerBase::HasObject'; # inject base class
}
# Set :Chained('id') on Controller::Foo::Id->has_user
$pkg->config(actions => { has_object => { Chained => $action->reverse } });
}
return $new;
}
package MyApp::Controller::Foo;
use base qw(MyApp::ControllerBase::Chains);
__PACKAGE__->config(object_chains = [ qw(id name email) ]);
sub id ...
sub name ...
sub email ...
Now, when Catalyst creates the Controller::Foo instance the stuff after the
component method will create Controller::Foo::Id, ::Name, ::Email - Catalyst
will automatically pick this up (the same way it picks up the sub-models
created by e.g. Model::DBIC::Schema) and will load the Foo/Id.pm etc. files
afterwards -if- they exist.
That way you'll get /foo/id/edit, /foo/name/edit etc. actions which can be
passed happily to $c->uri_for without ambiguity, and still have minimal
repeated code.
(Disclaimer: code typed straight into mail. probably at least one typo or
thinko lurking in there)
If people like this approach, I could write it up as an advent entry ...
--
Matt S Trout Need help with your Catalyst or DBIx::Class project?
Technical Director http://www.shadowcat.co.uk/catalyst/
Shadowcat Systems Ltd. Want a managed development or deployment platform?
http://chainsawblues.vox.com/ http://www.shadowcat.co.uk/servers/
More information about the Catalyst
mailing list