[Catalyst] Re: Catalyst::Action::REST

Matt S Trout dbix-class at trout.me.uk
Tue Nov 21 15:16:41 GMT 2006


Garrett Goebel wrote:
> 
> On Nov 20, 2006, at 9:02 AM, A. Pagaltzis wrote:
> 
>> * Matt S Trout <dbix-class at trout.me.uk> [2006-11-20 15:25]:
>>> Feel free to propose a variant on the syntax marcus proposed
>>> that  will allow the implementation of equivalent functionality
>>> without the  addition of multimethods to the perl core.
>>
>> Oh! Now I feel silly.
>>
>> Eh.
>>
>> OK, to lay out my own thinking a little, when I was doing my
>> silly PATHINFO-based hack-up of CGI::Prototype::Hidden, the plan
>> eventually became to have one class per handler, with methods
>> named after, err, methods, eg.:
>>
>>     sub GET  { ... }
>>     sub POST { ... }
>>     sub HEAD { ... }
>>
>> I still think that’s cleanest approach.
>>
> 
> That's pretty much what I wound up doing. Except I was more focused on a 
> RESTful rewrite of InstantCRUD to generate one controller class per 
> table. It is very clean and simple. All my per-table controllers 
> inherited from a single base controller which implemented actions for 
> create, read, update, delete, edit, show, index.

This is basically how Reaction handles CRUD

>> Catalyst as it stands somewhat encourages a confusion between
>> nouns (URIs) and verbs (methods), with URIs like
>> `/entry/1234/comments/add`, where the `/entry/1234/comments` part
>> identifies a resource, but the `/add` bit at the end is really an
>> verb. That should simply be a POST to `/entry/1234/comments`.
> 
> I ended up disagreeing with the RoR folks and Audrey's REST work in 
> Jifty. I think leaving the key unspecified is a bad idea. Sometimes a 
> table has more than one primary key candidate. Or you may want to limit 
> the result set based on field that isn't a primary key. What I 
> implemented back around the same time as John Napiorkowski was working 
> on the same thing was:
> 
> /entry/id/1234/comments
> 
> vs.
> 
> /entry/surname,given_name/Pagaltzis,Aristotle/comments
> 
> vs.
> 
> /entry/status/open

Again, pretty much exactly how Reaction handles it, except that I've shied 
away from dealing with multi-col PKs -just- yet :)

>> And
>> most of the time, if the design is RESTful from the start, you
>> can implement a web app as pure CRUD using the HTTP methods; eg.
>> the methods in a controller should simply correspond 1:1 to HTTP
>> verbs. That was as far as I had gotten my own thoughts.
>>
>> Of course, there are going to be some cases where that isn’t
>> quite enough; for these, you could have the option of adding
>> methods that work as if they were custom invented HTTP methods.
>> The framework could permit tunneling unsupported methods inside
>> POST requests, which it would automatically unravel before
>> dispatching. The Rails guys were doing something along these
>> lines; I have to take another look sometime.
> 
> I tunneled PUT and DELETE inside a POST via a "_method" body parameter, 
> which was automatically unravelled before dispatch within an overridden 
> Catalyst::prepare_body_parameters method.

Clever. Would you be willing to separate that out as a plugin with a 
configurable parameter name? I think that might be well-received (and actually 
something that *should* be a plugin for once :)

>> What I don’t like about the current proposals in Catalyst-land
>> is that they make the RESTful approach a wordy non-default
>> option. It takes quite a bit of extra annotation with any of
>> them, even if each annotation is concise. It ends easier to just
>> put verbs in your URIs, where it should be just as easy to do it
>> properly.
> 
> I'm not sure I follow you. RESTful uri's end up being shorter. I suppose 
> you're talking about the pain associated with implementing a RESTful 
> interface within a catalyst app.
> 
> The annoying problem that I ran up against was:
> 
> 1) Catalyst is designed with the assumption of dispatch based on first 
> match instead of best match. I.e. Catalyst::Dispatcher short circuits on 
> the first dispatch type attribute which matches. So side effects in the 
> dispatch type's ->match method aren't guaranteed if multiple dispatch 
> type parameters were specified.

It's not -exactly- first match, more an implicit assumption that the match of 
the most path parts is the best match - I effectively consider the current 
dispatcher to be half refactored from the 5.0 implementation, but haven't yet 
found a design for the second half I consider satisfactory.

> 2) All dispatch types are based on the request's path. If you want to 
> dispatch based on additional criterion, you're supposed to bury the 
> dispatch match logic in action classes.

You seem to consider dispatching on the path above all to be a bad thing; I 
don't quite understand this since the nature of HTTP/REST is that the URI 
identifies the entity and -then- the query parameters are applied, if present, 
representing a search on that entity, and only then is the method (and body if 
any) applied to the entity resulting.

> Fast is good and well, but I'd rather be correct than fast.

That's nice, but mapping path -> entity first *is* correct, at least for 
implementing REST :)

> I ended up subclassing the Catalyst::Dispatcher to make prepare_action 
> match all the dispatch types attributes specified for an action. This 
> allowed me to move my dispatch logic out of action classes and into 
> dispatch types where IMHO they belong.


I think what we really need to do is re-think the dispatch logic somewhat - 
entity, then search, then apply method+body is definitely correct, but 
matching the action first (and having the actions be singleton-per-app) is 
almost certainly wrong.

What I think we really want to be doing is something like Chained, but where 
the controllers rather than being a singleton-per-app are instantiated 
per-request, so

POST /foo/id/1/bar/id/3?baz=quux would do something like

my $foo_c = Controller::Foo->new(id => 1, parent => $app);

my $bar_c = Controller::Bar->new(id => 3, parent => $foo_c);

$bar_c->apply_search(baz => 'quux');

$bar_c->POST($body);

or similar. I don't think the above is -quite- right but I think it -is- 
closer than how things currently work.



More information about the Catalyst mailing list