With Catalyst you register your actions and address them directly. For example:
- MyApp->action( 'hello' => sub {
+ sub hello : Global {
my ( $self, $context ) = @_;
$context->response->output('Hello World!');
- });
+ }
Now http://localhost:3000/hello prints "Hello World!".
The best part is that Catalyst implements all this flexibility in a very simple way.
+=over 4
+
=item * B<Building Block Interface>
Components interoperate very smoothly. For example, Catalyst automatically makes a L<Context> object available in every component. Via the context, you can access the request object, share data between components, and control the flow of your application. Building a Catalyst application feels a lot like snapping together toy building blocks, and everything just works.
Catalyst provides helper scripts to quickly generate running starter code for components and unit tests.
+=back
+
=head2 Quickstart
Here's how to install Catalyst and get a simple application up and running, using the helper scripts described above.
# my_param_name => $my_param_value,
);
- MyApp->action( '!default' => sub {
+ sub default : Private {
my ( $self, $context ) = @_;
$context->response->output('Catalyst rockz!');
- });
+ }
1;
As illustrated earlier in our URL-to-Action dispatching example, the Context is always the second method parameter, behind the Component object reference or class name itself. Previously we called it C<$context> for clarity, but most Catalyst developers just call it C<$c>:
- MyApp->action( 'hello' => sub {
+ sub hello : Global {
my ( $self, $c ) = @_;
$c->res->output('Hello World!');
- });
+ }
The Context contains several important objects:
The last of these, the stash, is a universal hash for sharing data among application components. For an example, we return to our 'hello' action:
- MyApp->action(
-
- 'hello' => sub {
- my ( $self, $c ) = @_;
- $c->stash->{message} = 'Hello World!';
- $c->forward('!show-message');
- },
-
- '!show-message' => sub {
- my ( $self, $c ) = @_;
- $c->res->output( $c->stash->{message} );
- },
+ sub hello : Global {
+ my ( $self, $c ) = @_;
+ $c->stash->{message} = 'Hello World!';
+ $c->forward('show-message');
+ }
- );
+ show-message : Private {
+ my ( $self, $c ) = @_;
+ $c->res->output( $c->stash->{message} );
+ }
=head3 Actions
=item * B<Literal>
- MyApp->action( 'foo/bar' => sub { } );
+ sub bar : Path('/foo/bar') { }
Matches only http://localhost:3000/foo/bar.
=item * B<Regex>
- MyApp->action( '/^foo(\d+)/bar(\d+)$/' => sub { } );
+ sub bar : Regex('/^foo(\d+)/bar(\d+)$/') { }
Matches any URL that matches the pattern in the action key, e.g. http://localhost:3000/foo23/bar42. The pattern must be enclosed with forward slashes, i.e. '/$pattern/'.
=item * B<Namespace-Prefixed>
package MyApp::Controller::My::Controller;
- MyApp->action( '?foo' => sub { } );
+ sub foo : Local { }
-Matches http://localhost:3000/my_controller/foo. The action key must be prefixed with '?'.
+Matches http://localhost:3000/my/controller/foo. The action key must be prefixed with '?'.
Prefixing the action key with '?' indicates that the matching URL must be prefixed with a modified form of the component's class (package) name. This modified class name excludes the parts that have a pre-defined meaning in Catalyst ("MyApp::Controller" in the above example), replaces "::" with "_" and converts the name to lower case. See L</Components> for a full explanation of the pre-defined meaning of Catalyst component class names.
=item * B<Private>
- MyApp->action( '!foo' => sub { } );
+ sub foo : Private { }
Matches no URL, and cannot be executed by requesting a URL that corresponds to the action key. Private actions can be executed only inside a Catalyst application, by calling the C<forward> method:
- $c->forward('!foo');
+ $c->forward('foo');
See L</Flow Control> for a full explanation of C<forward>.
=head4 B<Namespace-Prefixed Private Actions>
- MyApp->action( '!?foo' => sub { } );
- MyApp->action( '!?default' => sub { } );
+ sub foo : Private { }
+ sub default : Private { }
The leading '!?' indicates that these are namespace-prefixed private actions. These override any application-wide private actions with the same names, and can be called only from within the namespace in which they are defined. Any private action can be namespace-prefixed, including the builtins. One use for this might be to give a Controller its own !?default, !?begin and !?end.
If you want to pass variable arguments at the end of a URL, you must use regex actions keys with '^' and '$' anchors, and the arguments must be separated with forward slashes (/) in the URL. For example, suppose you want to handle /foo/$bar/$baz, where $bar and $baz may vary:
- MyApp->action( '/^foo$/' => sub { my ($self, $context, $bar, $baz) = @_; } );
+ sub foo : Regex('/^foo$/') { my ($self, $context, $bar, $baz) = @_; }
But what if you also defined actions for /foo/boo and /foo/boo/hoo ?
- MyApp->action( '/foo/boo' => sub { .. } );
- MyApp->action( '/foo/boo/hoo' => sub { .. } );
+ sub boo : Path('/foo/boo') { .. }
+ sub hoo : Path('/foo/boo/hoo') { .. }
Catalyst matches actions in most specific to least specific order:
Control the application flow with the C<forward> method, which accepts the key of an action to execute.
- MyApp->action(
-
- 'hello' => sub {
- my ( $self, $c ) = @_;
- $c->stash->{message} = 'Hello World!';
- $c->forward('!check-message');
- },
-
- '!check-message' => sub {
- my ( $self, $c ) = @_;
- return unless $c->stash->{message};
- $c->forward('!show-message');
- },
+ sub hello : Global {
+ my ( $self, $c ) = @_;
+ $c->stash->{message} = 'Hello World!';
+ $c->forward('check-message');
+ }
- '!show-message' => sub {
- my ( $self, $c ) = @_;
- $c->res->output( $c->stash->{message} );
- },
+ sub check-message : Private {
+ my ( $self, $c ) = @_;
+ return unless $c->stash->{message};
+ $c->forward('show-message');
+ }
- );
+ sub show-message : Private {
+ my ( $self, $c ) = @_;
+ $c->res->output( $c->stash->{message} );
+ }
You can also forward to classes and methods.
- MyApp->action(
-
- 'hello' => sub {
- my ( $self, $c ) = @_;
- $c->forward(qw/MyApp::M::Hello say_hello/);
- },
-
- 'bye' => sub {
- my ( $self, $c ) = @_;
- $c->forward('MyApp::M::Hello');
- },
+ sub hello : Global {
+ my ( $self, $c ) = @_;
+ $c->forward(qw/MyApp::M::Hello say_hello/);
+ }
- );
+ sub bye : Global {
+ my ( $self, $c ) = @_;
+ $c->forward('MyApp::M::Hello');
+ }
package MyApp::M::Hello;
This gives us a process() method and we can now just do $c->forward('MyApp::V::TT') to render our templates. The base class makes process() implicit, so we don't have to say C<$c-E<gt>forward(qw/MyApp::V::TT process/)>.
- MyApp->action(
-
- 'hello' => sub {
- my ( $self, $c ) = @_;
- $c->stash->{template} = 'hello.tt';
- },
-
- '!end' => sub {
- my ( $self, $c ) = @_;
- $c->forward('MyApp::View::TT');
- },
+ sub hello : Global {
+ my ( $self, $c ) = @_;
+ $c->stash->{template} = 'hello.tt';
+ }
- );
+ sub end : Private {
+ my ( $self, $c ) = @_;
+ $c->forward('MyApp::View::TT');
+ }
You normally render templates at the end of a request, so it's a perfect use for the !end action.
root => '/home/joeuser/myapp/root'
);
- __PACKAGE__->action(
-
- '!end' => sub {
- my ( $self, $c ) = @_;
- $c->stash->{template} ||= 'index.tt';
- $c->forward('MyApp::V::TT');
- },
-
- 'view' => sub {
- my ( $self, $c, $id ) = @_;
- $c->stash->{item} = MyApp::M::CDBI::Foo->retrieve($id);
- }
+ sub end : Private {
+ my ( $self, $c ) = @_;
+ $c->stash->{template} ||= 'index.tt';
+ $c->forward('MyApp::V::TT');
+ }
- );
+ sub view : Global {
+ my ( $self, $c, $id ) = @_;
+ $c->stash->{item} = MyApp::M::CDBI::Foo->retrieve($id);
+ }
1;
package MyApp::C::Login;
- MyApp->action(
- '?sign-in' => sub { },
- '?new-password' => sub { },
- '?sign-out' => sub { },
- );
+ sign-in : Relative { }
+ new-password :Relative { }
+ sign-out : Relative { }
package MyApp::C::Catalog;
- MyApp->action(
- '?view' => sub { },
- '?list' => sub { },
- );
+ sub view : Local { }
+ sub list : Local { }
package MyApp::C::Cart;
- MyApp->action(
- '?add' => sub { },
- '?update' => sub { },
- '?order' => sub { },
- );
+ sub add : Local { }
+ sub update : Local { }
+ sub order : Local { }
=head3 Testing