[Catalyst] Dispatch matching more than the path?

Garrett Goebel ggoebel at goebel.ws
Tue Aug 22 21:16:54 CEST 2006

On Aug 22, 2006, at 3:49 AM, Matt S Trout wrote:
 > > Garrett Goebel wrote:
 > >
 > > I hacked something into the Path and Regex dispatchers to get  
 > > matching on method and path working in the prototype.
 > You shouldn't need to hack anything into these; just use a custom  
 > that overrides $action->match to introspect onto the request  
method and
 > anything else you need (this is already how :Args is handled, see the
 > Catalyst::Action source for the implementation).

Thanks. I'd found the :Args code in Catalyst::Action last night and  
managed to shoehorn the request method and path parameter matching  
checks into it. How to subclass or override it was going to be my  
next question. I'll check out using a custom ActionClass.

Last night I also sub-classed the Request class to add:
__PACKAGE__->mk_accessors(qw/path_parameters accept_extension/);

And I'm currently subclassing the Dispatcher to override  
prepare_action in order to:
o  remove uri path parameters from $c->request->path and add to
o  remove "file" extension from last path segment and add to
o  filter body parameters from using content_type implied by the
    accept_extension (json, yaml, etc) and add to $c->request- 
    Perhaps I should consider just using the $c->request->content_type?
o  check POST requests for hidden _method=DELETE|PUT parameter and  
    $c->request->method accordingly

These are all things I want to do once per request, not once per  
action. Certainly there are better places to perform some of these  
tasks. And I would like to hear any advice on the best place to  
override catalyst for each. In the mean time, at least I've got a  
proof of concept working.

My controller now is able to look like:

# GET http://foo.com/model
# GET http://foo.com/model.json
sub index :GET :Path('') Args(0) {
     my ($self, $c) = @_;
     my @models = grep { UNIVERSAL::can($c->model($_),  
'result_source') }
     $self->out($c, \@models);

# GET http://foo.com/model/Person
sub show :GET :Path('') :Args(1) {
     my ($self, $c, $model) = @_;
     my @pkcols = $c->model($model)->result_source->primary_columns;
     my @pk_tuples = map(
         { my $tuple = $_; csv_encode(map({ $tuple->$_ } @pkcols)) }
                                   {columns  => \@pkcols,
                                    distinct => 1,
                                    order_by => \@pkcols})
     $self->out($c, \@pk_tuples);

# GET http://foo.com/model/Person;edit
sub edit :GET :Path('') :PathParam(edit) :Args(1) {...}

# GET http://foo.com/model/Person;add
sub add :GET :Path('') :PathParam(add) :Args(1) {...}

# GET http://foo.com/model/Person;column_info
# GET http://foo.com/model/Person.yaml;column_info
sub show_column_info :GET :Path('') :PathParam(column_info) :Args(1) {
     my ($self, $c, $model) = @_;
     my $rs  = $c->model($model)->result_source;
     my %column_info = map { $_ => $rs->column_info($_)} $rs->columns;
     $self->out($c, \%column_info);

# POST http://foo.com/model/Person;new
sub create :POST :Path('') :PathParam(new) :Args(1) {...}

# PUT http://foo.com/model/Person/32
sub update :PUT :Path('') :Args(2) {...}

# DELETE http://foo.com/model/Person/32
sub destroy :DELETE :Path('') :Args(2) {...}

More information about the Catalyst mailing list