tags:

views:

195

answers:

3

We're trying to make our REST API a bit more friendly, We have a base class for our REST API which inherits from Catalyst::Controller::REST. Each REST class can identify the query parameters it accepts. We thought it would be nice to make this information public and put this into the base class:

sub doc : Regex('/doc$') {
    my ( $self, $c ) = @_;
    $c->stash->{params} = $c->forward('allowed_query_params');
}

And from there, every REST url could have /doc/ added to the end to show which query parameters it accepts.

It doesn't work. $self is always a PIPs::C::API::V1::Franchise instance, no matter which URL is called. This appears to be because of these:

[26 Feb 2009 15:07:40,509] [Catalyst.Dispatcher] [DEBUG] Loaded Private actions:
.-----------------------+--------------------------------------+--------------.
| Private               | Class                                | Method       |
+-----------------------+--------------------------------------+--------------+
...
| /api/v1/franchise/doc | PIPs::C::Api::V1::Franchise          | doc          |

And:

[26 Feb 2009 15:07:40,514] [Catalyst.DispatchType.Regex] [DEBUG] Loaded Regex actions:
.--------------------------------------+--------------------------------------.
| Regex                                | Private                              |
+--------------------------------------+--------------------------------------+
| /doc$                                | /api/v1/franchise/doc                |
| /doc$                                | /api/v1/version/doc                  |
| /doc$                                | /api/v1/creditrole/doc               |
| /doc$                                | /api/v1/doc                          |
| /doc$                                | /api/v1/segmentevent/doc             |
| /doc$                                | /api/v1/collection/doc               |
| /doc$                                | /api/v1/episode/doc                  |

So the very first instance of the "doc" method dispatches through Franchise, even if the controller for a given URL would be API::V1::Version or something like that.

How can I work around this? LocalRegex doesn't work, obviously, and chained actions don't seem appropriate because, due to the nature of our app, we never know how many path parts will be between '/api/v1/' and '/doc/'.

What am I missing?

+1  A: 

I think you want LocalRegex instead of Regex. But why a regex at all, and not just plain Local?

jrockway
mmhh, Ovid mentions explicitly in his question: "LocalRegex doesn't work, obviously" ...
It should work, though. There are important details that are being omitted, apparently.
jrockway
A: 

I think Local won't work because the controller action might accept several arguments, so Controller::Foo::my_action might end up accepting: /foo/my_action/this/1/that/2/the_other

So if I'm reading you correctly you want /foo/my_action/this/1/that/2/the_other/doc, /bar/other_action/thing/4/thang/2/the_other/doc etc.

Well one way of doing it would be to have a sub auto : Private { } in a base controller that checks $c->req->path or $c->req->args for doc at the end and then forwards to the relevant private action if it's there

sub auto : Private {
    my ($self, $c) = @_;
    $c->forward('doc_method) if $c->req->args->[ $@{$c->req->args}  eq 'doc';
}

(untested). Aslo you may want $c->detach rather than forward, not sure.

singingfish
+1  A: 

It depends on how elegant you want to make your application, it seems. You might try something with chained actions, chaining the doc action to every action that you'd like to append '/doc' to. AFAIK, catalyst does not support chaining an action to multiple other actions, but that may have changed. Alternatively, could they not all take one additional argument?

Or, modifying the code above:

sub auto : Private {
    my ($self, $c) = @_;
    if ((my $path = $c->req->path) =~ /\/doc$/) {
        $path =~ s/\/doc//;
        $c->detach($path);
    }
}

That's really probably poor practice, though...

delta