=head1 Writing Your Application Declaratively =head2 Some History There have been many shiny new developments in the Perl community of late. With Catalyst's switch to L, a whole new world of possibilities was introduced and suddenly available to all Catalyst developers. Another exciting new idea came along with L: the ability to provide a declarative syntax for things that used to be more verbose or repetitious, or just simply to provide an API that goes beyond what the Perl parser itself provides. One of the neater examples that came with the availability of those modules is L, which allows you to write your classes like this: class SomeClass extends SomeParentClass { has foo => (is => 'rw'); method bar (Int $baz) { $self->foo + $baz } } While it might look like it, this is not a source filter in the sense that we all (hopefully) learned to avoid. The module is still parsed by perl. When for example the C keyword is hit, L takes over and parses the special syntax. When the C<{> opening brace is found, it gives control back to perl. This has the big advantage that extensions can be safely combined and also be written in Perl itself. In fact, the C and C keywords are handled by separate parts of L. =head2 What's this got to do with Catalyst? A very good question! The module L is an extension of L that allows you to write your Catalyst applications declaratively. An example L controller would look like this: controller MyApp::Controller::Foo { action base as '' under '/'; final action bar (Int $id) under base { $ctx->stash(object => $ctx->model('Baz')->find($id)); } } The C keyword is an extension of the C handler from L. While C (like L) will always automatically inherit from L when no C option is provided, Cs will have a default superclass of L. The first thing you might notice about the actions is that there are no attributes. Everything is specified with a simple declarative syntax. The first action you see is this: action base as '' under '/'; This is roughly equivalent to the following: sub base: Chained('/') PathPart('') CaptureArgs(0) { } Yes, all actions in L are chained actions, since L is the most flexible way of designing Catalyst applications (and one that lends itself especially well to declarative syntax). The next action is where it becomes really interesting: final action bar (Int $id) under base { $ctx->stash(object => $ctx->model('Baz')->find($id)); } The C creates an endpoint chained to the C we declared above. The actions all automatically receive a C<$ctx> context variable in addition to the C<$self> that L already provides for methods. The interesting part is the C<(Int $id)> signature for the action. The number of positional arguments is used to determine the number of arguments in the public URL. The above chain would run if the resource C is hit, and it will only match if its argument is an L. To expand a bit on the last comment, let me show you an example: controller MyApp::Controller::Foo { # see MooseX::Types use MyApp::Types qw( PageName SequentialID Language ); action base (Language $lang) as '' under '/' { $ctx->stash(language => $lang); } final action show_page (PageName $name) as show under base { $ctx->stash(page => $ctx->model('DB::Page')->find($name)); } final action show_item (SequentialID $id) as show under base { $ctx->stash(item => $ctx->model('DB::Item')->find($id)); } } We assume that C must be a valid language string (such as C); C must be an identifier in the Perl sense (starts with a letter or underscore, not a digit); and C is a positive integer. Then, you can hit the above controller with the following: =over =item * /en/show/foo This will load C with a C<$lang> of C and then dispatch to C with a C<$name> of C. =item * /de/show/23 This will first run C like before, but this time with C as C<$lang> argument and then execute C with an C<$id> of C<23>. =back Of course you can use any number of arguments, and for endpoints even slurpy arguments are possible: final action show_page (Str @path) as page under base { ... } =head2 Grouping actions by their base You often have a single action as a base and many actions that chain off of it. If that is the case for you, you can use C as a keyword to group the actions together and save yourself the repetition of the C option: action base as '' under '/'; under base { final action foo { ... } final action bar { ... } } Both C and C in the above example will implicitly chain off C. =head2 Declaring other parts of the application Of course controllers and actions are the most important components for which you'd want to have a declarative syntax. But they are not all that L provides you with. You can also declare your application in a shinier syntax: application MyApp with ConfigLoader with Static::Simple { $CLASS->config(name => 'My App'); } There you go; no need to inherit from L, no need to call C. And the plugins are nicely specified as roles (which is what plugins are likely to become in the future). So now we have the application, and we already had the C in C, but L also gives you keywords for the two other letters: model MyApp::Model::DBIC extends Catalyst::Model::DBIC::Schema { method foo (Int $x) { ... } } for models, and view MyApp::View::HTML extends Catalyst::View::TT { after process { ... } } for views. Additionally you can also define controller roles like this: controller_role MyApp::ControllerRole::DefaultBase { action base as '' under '/'; } and you can consume them on the other side like usual in L based modules: controller MyApp::Controller::Qux with MyApp::ControllerRole::DefaultBase { action view under base { ... } } =head2 Methods and attributes Since L is just an extension of L you can use its full power to declare methods and attributes as well. Here is an example of a complete root controller with methods and attributes: controller MyApp::Controller::Root { use MooseX::Types::Moose qw( Str ); has home_action => ( is => 'ro', isa => Str, required => 1, default => '/somewhere/else', ); method home_uri (Object $ctx) { return $ctx->uri_for_action($self->home_action); } action app_base as '' under '/'; action end (@) isa RenderView; under base { # matches / final action app_root as '' { $ctx->response->redirect($self->home_uri($ctx)); } # matches /... final action fallback (@) as '' { $ctx->response->status(404); $ctx->response->body('File not found'); } } } =head2 Conclusion The real hard work for all this shinyness is done by L. Take a look at L on CPAN for a full description of the available syntax, since the above is only a quick tour. I also casually skipped over the use of method modifiers that are applied to actions, which adds even more possibilities. If you want more samples, there is an example application shipped with the distribution at L. =head2 Author and Copyright (c) 2009, Robert 'phaylon' Sedlacek (C). =cut