[{"date":"2009-04-26T08:13:18Z","categories":["Perl"],"author":{"name":"Florian Ragwitz","email":"rafl@fsfe.org","keyid":"742f2a428e635a5e"},"tags":[{"perl":0},{"syntax":0},{"catalyst":0}],"modified":"2009-04-26T09:14:50Z","uri":"http://perldition.org/articles/Declaring%20Catalyst%20Actions.pod","signed":1,"summary":" For a long time the Catalyst Framework has been …","xhtml":"
For a long time the Catalyst Framework has been using code attributes\nto allow users to declare actions that certain URLs get dispatched to.\nThat looks something like this:
\nsub base : Chained('/') PathPart('') CaptureArgs(0) { ... }\nsub index : Chained('base') PathPart('') Args(0) { ... }\nsub default : Chained('base') PathPart('') Args { ... }\n\n
It's a nice and clean syntax that keeps all important information\nright next to the method it belongs to.
\nHowever, attributes in perl have a couple of limitations. For one, the\ninterface the perl core provides to use them is horrible and doesn't\nprovide nearly enough information to do a lot of things, but most\nimportantly attributes are just plain strings. That means you will\nneed to parse something like "Chained('base')" into\n(Chained => 'base') yourself to make proper use of them.
While that's easy for the above example, it can be very hard in the\ngeneral case because only perl can parse Perl. It's one of the reasons\nyou can't use Catalyst::Controller::ActionRole to apply\nparameterized roles to your action instances, because parsing\nparameters out of things like\nDoes(SomeRole => { names => [qw/affe tiger/], answer_re => qr/42/ })\nwould be awful and wrong.
With Catalyst 5.8 most of the attribute related code has been removed\nfrom the internals. It's now using MooseX::MethodAttributes to do\nall the heavy lifting. Also the internals of how actions are\nregistered have been refactored to make it easier to implement\nalternate ways without changing the Catalyst core.
\nAs a proof of concept for this I implemented a new way of declaring\nactions that's very similar to how Moose provides it's sugar\nfunctions. You can get it from\ngithub.
\nWith that, the above example looks like this:
\naction base => (Chained => '/', PathPart => '', CaptureArgs => 0) => sub { ... };\naction index => (Chained => 'base', PathPart => '', Args => 0 ) => sub { ... };\naction default => (Chained => 'base', PathPart => '', Args => undef) => sub { ... };\n\n\n\n
It also moves method declaration from compiletime to runtime, making\nthis possible:
\nfor my $action (qw/foo bar baz/) {\n action $action => (Chained => 'somewhere', Args => 0) => sub {\n my ($self, $ctx) = @_;\n $ctx->stash->{ $action } = $ctx->model('Foo')->get_stuff($action);\n };\n}\n\n
Admittedly, that's all very ugly, but illustrates well what kind of\nthings we're able to do now. But it doesn't need to be ugly. With\nDevel::Declare we have a great tool to add our own awesome syntax\nto perl, similar to how things like MooseX::Method::Signatures,\nMooseX::MultiMethods and MooseX::Declare do.
\nSo how would a declarative syntax for Catalyst controllers look like?\nI don't know. Ideas include something like this:
\nunder /some/where, action foo ('foo', $id) { ... }\n\n
to mean:
\nsub foo : Chained('/some/where') PathPart('foo') CaptureArgs(1) { ... }\n\n
Adding Moose type constraints to this would be interesting, too, and\nmake validation of captures and arguments a lot easier. Multi dispatch\nsimilar to MooseX::MultiMethods could be handy as well:
\nunder /some/where {\n action ('foo', Int $id) {\n # find and stash an item by id\n }\n action ('foo', Str $name) {\n # search items using $name\n }\n action ('foo', Any $thing) {\n # display error page\n }\n}\n\n
So you see there are a lot of possibilities that should be\nexplored. Unfortunately I have no idea what kind of syntax and\nfeatures people would like to have, so your feedback on this would be\nmuch appreciated. :-)
\n\n\n