An Introduction to Application Development with Catalyst and Perl

Dispatch

Catalyst provides a particularly flexible and powerful mechanism for configuring dispatch rules. Rather than having a separate configuration to assign URLs to specific actions, Catalyst uses the actions themselves to determine URL mappings dynamically.

Each action definition (which is just a Perl subroutine) represents not only a block of code, but also what URL paths apply to it. This is specified in subroutine attributes—a lesser-known Perl feature that provides arbitrary labels that can be used for introspection.

Catalyst supports a handful of parameterized attributes to determine the URL path to action mappings in a variety ways. For example, the following action has an absolute path set using the :Path attribute:


sub myaction :Path('/some/place') {
        my ( $self, $c, @args ) = @_;
        # do stuff...
}

Regardless of what controller you put it in, the above action would map to all URLs starting with /some/place (http://localhost:3000/some/place with the development server).

If you omitted the starting slash and used :Path('some/place'), the action would map to a path relative to the namespace of the controller. For example, if it were in KillerApp::Controller::Foobar, it would be mapped to URL paths starting with /foobar/some/place.

Instead of using :Path to set the path explicitly, you can set :Local to use the name of the controller and method. For instance, the following action, if contained in the controller KillerApp::Controller::Some, would also map to /some/place:


sub place :Local {
        my ( $self, $c, @args ) = @_;
        # do stuff...
}

If it were contained in the controller KillerApp::Controller::Some::Other, it would map to /some/other/place.

Actions include subpaths by default, so the above also would match /some/other/place/blah/foo/1. When this happens, the leftover parts of the path are supplied as arguments to the action method ('blah','foo','1'). You can use the :Args attribute to limit how deep the action will match subpaths, if at all. With an Args value of 0, this action would match only /some/place, but nothing below it:


sub myaction :Path('/some/place') :Args(0) {
        my ( $self, $c ) = @_;
        # do stuff...
}

Other attributes are available too. :Global works like :Local but ignores the controller name, and path pattern matching can be done with :Regex and :LocalRegex.

When a URL matches more than one action, Catalyst picks the one that matches best. However, there are a few built-in actions (method names "begin", "end" and "auto") that, if defined, are called at various stages of every request in addition to the matched action. Using the advanced :Chained attribute type, you can configure additional/multiple actions to be called with single requests in any order you like.

You also can programmatically dispatch to other action/paths from within the action code itself:


sub myaction :Path('/some/place') {
        my ( $self, $c, @args ) = @_;
        $c->forward('/some/other/place');
}

The Context Object ($c)

Controller actions serve as entry points to application code. A special per-request object called the "context" is supplied as an argument to every action when it is called by the dispatcher. The context object typically is read into a variable named $c, but it could be called anything.

The context provides interfaces and information about the application and its current state. It contains the details of the request currently being processed ($c->request) and access to what will become the response ($c->response).

At the beginning of the request, before any actions are called, the response object is created with empty/default data. Each of the actions that are called then has an opportunity to manipulate the response. At the end of the request, its final state is sent back to the client. This iterative approach to generating the response lends itself to a modular and dynamic structure.

The following action illustrates a few of the simple APIs that are available, such as inspecting the User-Agent and query/post parameters in the request, and setting the body and headers of the response:


sub myaction :Path('/some/place')  {
     my ( $self, $c, @args ) = @_;
	
     my $myparam = $c->request->params->{myparam};
	
     if(defined $myparam) {
          $c->response->body("myparam is $myparam");
     }
     else {
          $c->response->body("myparam was not supplied!!");
     }
	
     $c->response->body( 
          $c->response->body . 
          "\n\nExtra path args: " . join('/',@args)
     ) if (@args > 0);
	
     $c->response->headers->header( 'Content-Type' => 'text/plain' );
	
     $c->response->body("Bad command or file name") 
          if ($c->request->user_agent =~ /MSIE/);
}

Accessing the URL http://localhost:3000/some/place/boo/baz?myparam=foo would display the text that follows (except when using IE, in which case "Bad command or file name" is displayed instead):


myparam is foo

Extra path args: boo/baz

Within the action code, you can write any logic you like to build your application. Because the context object is just a variable, you can pass it as an argument into other functions. Following normal Perl programming rules, you can use other classes and libraries, instantiate objects and so on.

This is the extent of what you have to do—write controller actions and use the context object—but it's only the beginning of what you can do.

Catalyst Components

Over and above the core functionality, Catalyst provides a robust MVC structure to build your application. This includes useful base classes, sensible default behaviors and helpful sugar functions. You can leverage a tremendous amount of turnkey functionality by creating your classes within the supplied framework as models, views and controllers.

All these are considered "components" within Catalyst, and there aren't actually many functional differences between them. The Model-View-Controller monikers primarily are used for the purpose of categorization. Models are meant to contain data and business logic; views are supposed to handle rendering and display; and controllers tie everything together.

Operationally, components are essentially application classes with some extra, Catalyst-specific functionally. They are loaded automatically at startup as static object instances. Any component can be accessed throughout the application via the context object:


sub myaction :Path('/some/place') {
        my ( $self, $c, @args ) = @_;
        $c->model('MyModel')->do_something;
        $c->forward( $c->view('MyView') );
}

In the above example action, you simply are calling the method do_something in a model named MyModel (KillerApp::Model::MyModel), and then forward to the view named MyView (KillerApp::View::MyView).

Earlier, I showed how to use forward to dispatch to another action by supplying a path. When you pass a component to forward, the process method of the supplied component is called as if it were a controller action, which is roughly equivalent to this:


$c->view('MyView')->process($c,@args);

These are just a few examples of the available conventions and shortcuts. The important thing to understand is that all these sugar functions just boil down to calling methods and normal program flow.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Error in template?

j0e's picture

In page 3 of this article:

$c->stash->{data}->{title} = 'TT rendered page';

Shouldn't the template have this:

[% data.title %]

Instead of what is in the article:

[% title %]

This was an effective review for me, thank you!

[% data.message %] too

j0e's picture

I should have also mentioned that given:

$c->stash->{data}->{message} = 'A cool message!';

The following in the template:

[% message %]

needs to be:

[% data.message %]

Good work

Mas BlackBerry's picture

I like post a complete catalyst tutorial, i want to know if reference for it article, tks

Really nice article! Catalyst

Anonymous's picture

Really nice article! Catalyst is great framework with great community behind it

typo

Jon Bjornstad's picture

In this text:

The alias for /static/ above tells Apache to serve the files directly in this directory (images, CSS, JavaScript files and so on). This is more efficient than serving these files through the application, which isn't necessary.

The <code> and </code> HTML tags surrounding /static/ somehow got through.

Good Job!

daniel.cristian's picture

Nice work! I hope you'll continue with another MVC framework from Perl, a more lighter one, Perl Dancer.

Daniel

Excellent!

Dominic's picture

With so many people still thinking of perl as "that legacy language that you run from the cgi-bin directory", it's good to see the modern stuff get some coverage. Nice one!

Good work, looking for more!

Chankey Pathak's picture

Only LinuxJournal could have come up with such article. Good work!

Try to post a complete catalyst tutorial, there's only one single tutorial available (CPAN's tutorial for catalyst, the best one).

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState