Catalyst::Manual::Tutorial - Getting started with Catalyst


NAME

Catalyst::Manual::Tutorial - Getting started with Catalyst


DESCRIPTION

This document aims to get you up and running with Catalyst.

Installation

The first step is to install Catalyst, and the simplest way to do this is to install the Catalyst bundle from CPAN:

    $ perl -MCPAN -e 'install Task::Catalyst'

This will retrieve Catalyst and a number of useful extensions and install them for you. This process might not be totally painless though, and you might want to look at CatInABox at http://use.perl.org/~jk2addict/journal/28071, especially if you are on a system that lacks a compiler.

The very basics - Setting up the skeleton application.

Catalyst includes a helper script, catalyst.pl, that will set up a skeleton application for you:

    $ catalyst.pl tutorial
    created "tutorial"
    created "tutorial/script"
    ... output snipped
    created "tutorial/script/tutorial_create.pl"

This creates the directory structure, populated with skeleton files.

Testing out the skeleton application

You can test out your new application by running the server script that Catalyst provides:

    $ cd tutorial
    $ script/tutorial_server.pl
    [...] [catalyst] [debug] Debug messages enabled
    [...] [catalyst] [debug] Loaded plugins:
    .------------------------------------------------------------------------------.
    | Catalyst::Plugin::Static::Simple                                             |
    '------------------------------------------------------------------------------'
    [...] [catalyst] [debug] Loaded dispatcher "Catalyst::Dispatcher"
    [...] [catalyst] [debug] Loaded engine "Catalyst::Engine::HTTP"
    [...] [catalyst] [debug] Found home "/home/users/me/tutorial"
    [...] [catalyst] [debug] Loaded Private actions:
    .--------------------------------------+---------------------------------------.
    | Private                              | Class                                 |
    +--------------------------------------+---------------------------------------+
    | /default                             | tutorial                              |
    '--------------------------------------+---------------------------------------'
    [...] [catalyst] [info] tutorial powered by Catalyst 5.67
    You can connect to your server at http://localhost:3000

(Note that each line logged by Catalyst begins with a timestamp, which has been replaced here with ``...'' so that the text fits onto the lines.)

The server is now waiting for you to make requests of it. Try using telnet to manually make a simple GET request of the server (when telnet responds with ``Escape character is '^]'.'', type ``GET / HTTP/1.0'' and hit return twice):

    $ telnet localhost 3000
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
    GET / HTTP/1.0
    HTTP/1.0 200 OK
    Date: Mon, 07 Nov 2005 14:57:39 GMT
    Content-Length: 5525
    Content-Type: text/html; charset=utf-8
    Status: 200
    X-Catalyst: 5.67
    [...]
    Connection closed by foreign host.
    $

You can see the full welcome message by visiting http://localhost:3000/ with your browser.

More trace messages will appear in the original terminal window:

    [...] [catalyst] [debug] **********************************
    [...] [catalyst] [debug] * Request 1 (0.063/s) [2148]
    [...] [catalyst] [debug] **********************************
    [...] [catalyst] [debug] Arguments are ""
    [...] [catalyst] [debug] "GET" request for "" from localhost
    [...] [catalyst] [info] Request took 0.046883s (21.330/s)
    .------------------------------------------------------------------+-----------.
    | Action                                                           | Time      |
    +------------------------------------------------------------------+-----------+
    | /default                                                         | 0.000000s |
    '------------------------------------------------------------------+-----------'

The server will continue running until you interrupt it.

The application can also be tested from the command line using the generated helper script, script/tutorial_test.pl.

Getting started

So you picked Catalyst. Good choice. We assume you have installed it as well. For this tutorial you will also need the following modules:

the Catalyst::Plugin::Session manpage

the Catalyst::Plugin::Session::Store::File manpage

the Catalyst::Plugin::Session::State::Cookie manpage

the Catalyst::Plugin::Authentication manpage

the Catalyst::Plugin::Authentication::Store::Minimal manpage

the Catalyst::Plugin::Authentication::::Minimal manpage

the Catalyst::Plugin::Authentication::Credential::Password manpage

the Catalyst::Plugin::Authorization::Roles manpage

the DBD::SQLite manpage

...

If you have not already done this following the example above, then to get started all you need to do is type:

    catalyst.pl tutorial

Debugging

The simplest way to debug your Catalyst application is to run it using the built-in mini-server as described in Getting started.

If you want to output any debugging information to the console, then call $c->log->debug(), passing it a string to output. For data structures, use the Data::Dumper manpage and call < $c-log->debug(Dumper($structure)) >>

Model/View/Controller

The recommended method for code organization in a Catalyst application is known as the ``Model View Controller'' design pattern (also referred to ``MVC''. See http://en.wikipedia.org/wiki/Model-view-controller).

The point of the MVC pattern is to separate the dependencies of parts of the application from each other, and give them standard interfaces. Following this theory of organization should give your code all the benefits of modularity. The main benefits are interchangeability of parts and reusability of code.

Thus you could replace your flat-file data storage with a relational database, or your MySQL database with an Oracle database, and not have to change any of your Controller or View logic. Or you could later decide to output information from your application as RSS instead of HTML just by adding a new View module.

Model

Models deal with the storage of data. For a complex website, you may need multiple data sources, each having its own model class that provides an abstracted interface to it. In this tutorial we are going to be using a simple database.

View

Views are used to display information to the user. In a web framework, the View is generally used to output HTML to the browser. As mentioned previously, Views can also be used to output RSS or any other kind of data. One easy way to do this with Catalyst is to use a templating system such as Template Toolkit. If outputting HTML is all you are going to do, then you will probably only need one View.

Controller

A controller is responsible for responding to user choices, and thus controls what the application does. Since this is a web framework, Catalyst controllers are frequently used to react directly to URLs requested by the user. This tutorial will describe the simplest way of using controllers, where each path or part of a path is assigned its own action (or subroutine). More complex controlling mechanisms will be mentioned briefly, and can be read about in detail elsewhere in the manual.

Controlling

Now let's write our first bit of application code. First, we would like our application to greet our users. We'll assume for now that our users will be sent to the users/greet URL. To create a controller that serves the users namespace, we run the following command in our tutorial directory:

    script/tutorial_create.pl controller Users

This will create a Users.pm in lib/tutorial/Controller. Open this file in an editor and take a look. You will notice there is some commented-out code which we will ignore for now. To make something happen when our URL is visited, we will write a ``greet'' action which looks like this:

  sub greet : Local {
    my ($self, $c) = @_;
    my $name = $c->req->param('name');
    $c->log->debug("Got name: $name\n");
    if ($c->req->method eq 'POST') {
       if(!$name) {
            $c->stash->{message} = 'Please fill in a name!';
        }
        else {
            $c->stash->{message} = "Hello $name!";     
        }
    }
    $c->stash->{template} = 'greet.tt';
  }

Whew! So, what does all this do? Let's take it one step at a time. The subroutine declaration gives the action a name. To the right of the name there is an attribute type that looks like this:

    : Local 
    
That defines which URIs will translate to this action. "Local" matches
exactly one URI:
    /users/greet
    
The URI matched by "Local" is composed from the namespace minus the 
tutorial::controller portion, that is common to all controllers, 
and the action name itself.  Because it is a URI, we use forward slashes 
instead of double colons.  So, in summary, when a user requests:
 
    http://localhost:3000/users/greet
    
the "greet" action defined above in the Users controller will be executed.

The second line retrieves the parameters Catalyst gives us when it calls our method. The first is the instance of our Users class, and the second is commonly called the ``context'', held in $context, or abbreviated to $c. From now on, whenever we are talking about the context object, it will be represented as $c in the code.

The context is the magical object containing any information you need from catalyst, or want to send to it, and is passed from action to action. You will see it used frequently in Catalyst applications, and a list of all its methods is available in the Catalyst POD.

On the third line we use the ->param method of the context's request object to retrieve one of the query parameters, just like in the CGI manpage.

On the fourth, we make a debug output of this object on the server console, or the error log if running under CGI or mod_perl.

Next, if we have a post request, we check if the name field contains anything (or is ``true''). If it isn't, we assign an error message to a ``message'' field in the stash. The stash is a special hash of the context object, which allows us to pass data on to other methods we call later, typically in the View.

If the username did contain a value, then we just set our message to greet the user by name.

Finally, we set the special ``template'' variable in the stash to the name of the template we want our View to use to display this page.

Viewing

OK, so reacting and checking the users data is all fine, but how do we actually display the page/form in the first place, and our results? In this tutorial, we'll use Template Toolkit for our viewing. To create our TT-based view, just run the following command:

    script/tutorial_create.pl view TToolkit TT

Notice that this time we not only gave it the type of module we wanted to create (a view), and a name, but also a third argument, ``TT''. This is a Catalyst helper module, which will make a standard Template Toolkit module for you. And that's all you need to do there.

To use the view, the easiest way is to set up a standard ``end'' action. This is a special private action which will not be matched to a path like our ``greet'' action, but instead will be called after all other processing is done. Only one end action will be called in any request cycle. If there is one in a controller, it will be preferred over one in the application module, and so on.

Since we're writing a simple application, just add an end action like this to tutorial.pm:

    sub end : Private {
        my ($self, $c) = @_;
        $c->forward('tutorial::View::TToolkit') unless $c->res->body();
    }

The first line declares the end sub, and marks it as a Private action. (This is the second and last attribute type we'll be using in this tutorial.) The second line collects our standard parameters as shown in the controller's greet action.

The third line directs Catalyst to pass processing on to our TToolkit view. The forward method, when just passed a class name, calls process on that class. The standard TT view's process method renders the template named in the template variable in the stash, using all the other variables in the stash as values to fill it in.

NB: This is such a common way to end your processing that there is a plugin which does it for you: the Catalyst::Plugin::DefaultEnd manpage.

Template Toolkit also has access to the entire context object via the special variable ``c''. For example, using [% c.config.name %] in our template will output ``tutorial'', our project name.

All that remains is to create a simple template called ``greet.tt'', containing a form with a text field called ``name'' like below.

    <html><head><title> [% c.name %]</head><body>
    <p>[% message %]</p>
    <form action="[% c.req.uri %]" method="post">
    <input type="text" name="name"/>
    <input type="submit" value="Submit" name="submit"/>
    </form>
    </body></html>

In the example above, we use [% c.req.uri %], since we're posting to ourself. If we post to another action, we commonly use the uri_for method, like this:

    [% c.uri_for('/users/greet')%]

Place this file in the root directory. By default, templates are searched for here, but we can change that, which brings us to...

Configuring

As previously mentioned, the configuration of modules, plugins, and so on is done in the main application file. This is especially true for bits which need to be done before an instance of them is created, for example Template Toolkit.

The TT View looks for its templates in the root directory by default. Since this is also the directory that static files go in, we'd rather have a separate templates directory. To do this, change the config call in tutorial.pm like this:

 __PACKAGE__->config( name => 'tutorial',
                      'View::TToolkit' => {
                          'INCLUDE_PATH' => __PACKAGE__->path_to('templates')
     }
  );

And move the greet.tt file from root to the templates directory (after creating it).

Now we can run our application again by killing (ctrl-c) and restarting script/tutorial_server.pl. Try connecting to localhost:3000/users/greet with a browser and see what happens. What happens if you try to visit localhost:3000/users ?

Users and Authenticating

One of the many reasons to write dynamic websites instead of just using static HTML is to allow us to produce different content for different users, as well as restricting access to pages (which we could do with just Apache's htpasswd system).

In this tutorial, we will just be using basic authentication. When writing a real application, you'll want to use a database or other secure store to contain your user data.

To add authentication, all we need to do is add the the Catalyst::Plugin::Authentication manpage module to our main application file. Then we need to pick a storage method (one of the the Catalyst::Plugin::Authentication::Store manpage modules), and a method of verifying the user's credentials (one of the the Catalyst::Plugin::Authentication::Credential manpage modules). Edit tutorial.pm to look like this:

 use Catalyst qw/-Debug Static::Simple Authentication 
                Authentication::Store::Minimal 
                Authentication::Credential::Password/;

To configure, add some users to the config call. For example:

 authentication => { 'users' =>
                       { 'fred' =>
                           { 'password' => 'fred1234',
                           }
                       }
                   }

Generally, setting up configuration data for plugins is done based on the type of plugin. Check the documentation of the plugin for exact details; in this example we should look in the Catalyst::Plugin::Authentication::Store::Minimal manpage.

Since our user data is in the config, we can update it at runtime, and thus add users dynamically. (Of course, to keep them permanently we'd need to save our data to disk and read it back into the config on startup.)

To allow creation of new users we'll add a create action to our Users controller.

 sub create : Local {
    my ($self, $c) = @_;
    my ($username, $passwd1, $passwd2) = map { $c->req->param($_)} 
       ('username', 'password', 'passwordverify');
    if($username && $passwd1 && $passwd2) {
       if($c->config->{authentication}{users}{$username}) {
          $c->stash->{message} = 'Sorry, that user already exists';
          $c->stash->{username} = $username;
       }
       elsif($passwd1 eq $passwd2) {
            $c->config->{authentication}->{users}->{$username} =
                {password => $passwd1};
          $c->stash->{message} = 'User created!';
       }
       else {
          $c->stash->{username} = $username;
          $c->stash->{message} = 'Passwords do not match!';
       }
    }
    $c->stash->{template} = 'usercreate.tt';
 }

All this is doing is checking that all the appropriate fields are filled, and that the password fields contain the same data, and then adding the user to the config hash. All the checks produce a message which can be displayed to the user via the View.

The usercreate.tt template looks like this:

 <html><head><title>[% c.config.name %]</title></head><body>
 <h1>Create a new user</h1>
 <h2>Current users are:</h2>
 <p>
    [% FOREACH key = c.config.authentication.users.keys %]
        [% key %]<br/>
    [% END %]
 </p>
 <p> [% message %] </p>
 <form action="/users/create" method="post">
 <p>User Name: <input type="text" name="username"/></p>
 <p>Password: <input type="password" name="password"/></p>
 <p>Confirm Password: <input type="password" name="passwordverify"/></p>
 <p><input type="submit" name="submit" value="submit"></p>
 </form>
 </body></html>

In order for our users to be able to login, we need a login action which we put in the Users controller:

 sub login : Local {
     my ($self, $c) = @_;
     $c->stash->{template} = 'userlogin.tt';
     if(!$c->login()) {
             $c->stash->{message} = 'Please login.';
     }
     else {
         $c->stash->{message} = "Welcome " . $c->user->id;
     }
 }

And the userlogin.tt template:

 <html><head><title>[% c.config.name %]</title></head><body>
 <p> [% message %] </p>
 <form name='login' action='/users/login' method='post'>
 <p>Username: <input type='text' name='user' /></p>
 <p>Password: <input type='password' name='password' /></p>
 <p><input type="submit" /></form>
 </body></html>

Very simple. Since Credential::Password's ``login'' call extracts the username/password data from the query itself (assuming we use a standard name for our form fields), we don't have to do anything but call it.

To keep the user logged in, all we need to do is add the Session modules to our collection, and the Auth modules will automatically use them:

 use Catalyst qw/-Debug Static::Simple Authentication 
                 Authentication::Store::Minimal 
                 Authentication::Credential::Password
                 Session Session::Store::File Session::State::Cookie/;

Magic!

Exercise

As an exercise for the reader, do the following:

Change users/greet and greet.tt so that the welcome message greets the user by name.

Enforce user logging in by adding an auto action in tutorial.pm (see the Catalyst documentation to find out about the auto action).

Authorizing

Authentication is about verifying users, and authorization is about allowing them to do things. Catalyst currently has two Authorization modules, Roles and ACL. The Roles module allows you to define groups which you can assign your users to, and then allow access to areas of your website to the groups. The ACL module lets you do more fine grained access/restriction by allowing of denying access however you like. (It also supports Roles as done by the roles module.) This example uses the Catalyst::Plugin::Authorization::Roles manpage. To use this add ``Authorization::Roles'' into the ``use Catalyst'' statement in tutorial.pm.

Adding Roles via the Minimal store we are already using is quite simple, we just add a roles key to each user, defining the names of the roles they belong to.

 authentication => { 'users' =>
                            { 'fred' => 
                               { 'password' => 'fred1234',
                                 'roles'       => ['admin']
                               }
                            }
                         }

We need an interface for our admins to administer the roles, i.e. assign the users to groups. To restrict access to certain actions, we just need to call $c->check_user_roles() in each action. So we can make a restricted http://localhost:3000/users/groups page like this:

 sub groups : Local {
    my ($self, $c) = @_;
    if($c->check_user_roles('admin')) {
       # Now we can do things only an admin will see
       if (my $params = $c->req->params) {
          my $users = $c->config->{authentication}{users};
          foreach my $u (keys %$params) {
             $users->{$u}{roles} = $params->{$u} if($users->{$u});
          }
          $c->stash->{message} = 'Updated user roles!';
       }
       else {
           $c->stash->{users} = $c->config->{authentication};
       }
       $c->stash->{template} = 'usersgroups.tt';
    }
    else {
        $c->stash->{message} = 'Admins Only!';
        $c->stash->{template} = 'error.tt';
    }
 }

What we are doing here is checking whether the logged-in user (used by default in the check_user_roles method) is a member of the admin group. If it is, then we display the usergroups template, and update the users hash as required. Otherwise, we just show the user an error page.

For this simple example, the usersgroups.tt and error.tt templates could both look like this:

 <html><head><title>[% c.config.name %]</title></head><body>
 <p>[% message %]</p>
 <p>[% c.stash.users %]</p>
 </body></html>

And that's all there is to it.

Data Storage (Modelling)

Whether we want our users to be able to contribute to our website, or just create it from changeable data, we need to store the data somewhere. Generally this is done using a database, but models can also use other data sources, for example another website, or an RSS feed.

If you have or want a database, there are still choices to be made. There are several modules for accessing databases using an object-oriented wrapper. The best known are probably the DBIx::Class manpage and the Class::DBI manpage. Catalyst supports making models using either of these.

For a simple example, we will allow our users to store their favorite greeting in our database. Create a table called ``greetings'' in a database, that contains a ``user'' field and a ``greeting'' field. The simplest way to create a model of your database is to use these helper modules, for example with the DBIx::Class manpage:

    script/tutorial_create.pl model UserData DBIC dbi:SQLite:/path/to/mydb.db

This will cause the DBIx::Class Loader to inspect your database, and create a module in the Model::UserData namespace for each table in your database.

Now we need a form for our users to enter/edit their personal greetings in, so we'll make a http://localhost:3000/users/editgreeting page:

 sub editgreeting : Local {
    my ($self, $c) = @_;
    if($c->req->params->{greeting}) {
       if(!$c->user_exists) {
          $c->stash->{message} = "You're not logged in!";
       }
       else {
          my $grtable = $c->model('UserData::Greetings');
          my $record = $grtable->find_or_create(user => $c->user->id);
          $record->greeting($c->req->params->{greeting});
          $record->update;
          $c->stash->{message} = 'Greeting updated';
       }
    }
    $c->stash->{template} = 'usersgreeting.tt';
 }

Using $c->user_exists from the Authentication plugin, this checks whether the user is logged in already. If they are, and they have entered a new greeting, we use DBIx::Class' find_or_create method to fetch or create a new record in the greetings table for the user. Once we have the record, we change the value of the greeting field, and call update to store the new value in the database.

Engines (Apache and FastCGI)

Now that we have the basics together, we can try running our application on a ``real'' server instead of just using the test server that Catalyst comes with. the Catalyst::Engine manpage is the module used to implement various types of servers to run it on. The current popular ones are Apache and FastCGI.

Apache

Apache needs to be configured: we need to tell it to load your application. You can either use Catalyst for your entire website, or subsections. Use the Location directive to choose a path to run your application under:

 <Location />
   SetHandler           perl-script
   PerlResponseHandler  tutorial
 </Location>

You will need to install the perl modules of your application into one of perl's library directories, as listed by perl -V, so that Apache can find them. Alternatively you can use the PerlSwitches directive to tell Apache where to look:

 PerlSwitches -I/path/to/tutorial/

These instructions are for using Apache2 and mod_perl 2.0. If you are using mod_perl 1.3 or 1.99, please refer to either the Catalyst::Engine::Apache::MP13 manpage or the Catalyst::Engine::Apache2::MP19 manpage for details of the slightly different ways to do it.

If you wish to ensure that Apache pre-loads your application, use the PerlModule directive. This means that there will be less of a delay when your application is accessed.

 PerlModule tutorial

FastCGI

These instructions apply to the use of mod_fastcgi under Apache (either 1 or 2 series).

There are 3 ways to attach a program to a URL with mod_fastcgi; we'll examine all of them, and explain how to avoid having the tutorial_fastcgi.pl substring in the user-visible URLs.

In all of these examples, we assume that the DocumentRoot is /var, that our app is called tutorial and is kept in /usr, that you want the users to access the app either from the root of the server-uri-space, or from /theapp. We also assume that the general FastCGI settings (FastCgiIpcDir, loading the module) are already correct (they don't depend on Catalyst or your application layout).

static application

In this setup, you tell mod_fastcgi that a particular file is to be run as a FastCGI handler. Put this somewhere in Apache's configuration:

  FastCgiServer /usr/apps/tutorial/script/tutorial_fastcgi.pl
  Alias / /usr/apps/tutorial/script/tutorial_fastcgi.pl/

If you want your app under /theapp, change the Alias line to:

  Alias /theapp /usr/apps/tutorial/script/tutorial_fastcgi.pl

Note the detail of the trailing / : this is a general rule of the Alias directive, both sides must end with / , or both must not; you can't have one with / and the other without, or strange things happen.

dynamic application

In this setup, you tell mod_fastcgi that certain files are to be treated as FastCGI handlers, in the same way you have to tell mod_cgi. Put this in the configuration:

  FastCgiConfig -autoUpdate
  <Directory /usr/apps/tutorial/script>
   Options +ExecCGI
   <Files *_fastcgi.pl>
    SetHandler fastcgi-script
   </Files>
  </Directory>
  Alias / /usr/apps/tutorial/script/tutorial_fastcgi.pl/

Again, if you want your app under /theapp, change the Alias line to:

  Alias /theapp /usr/apps/tutorial/script/tutorial_fastcgi.pl

external server

In this setup, the application is started separately from Apache, and communicates via a socket with mod_fastcgi. This can be useful if you need to have a particular environment for your application (maybe different between applications), or you want to run them on different machines, or under different users for security reasons.

If you want to use a UNIX socket (on the filesystem), put this in Apache's configuration:

  FastCgiExternalServer /tmp/somewhere -socket /tmp/tutorial-socket
  Alias / /tmp/somewhere/

Note that /tmp should not exist: it's just a name to connect the two parts.

Again, if you want your app under /theapp, change the Alias line to:

  Alias /theapp /tmp/somewhere

Then start your Catalyst application:

  $ cd /usr/apps/tutorial
  $ ./script/tutorial_fastcgi -l /tmp/tutorial-socket

If you want to use a TCP socket, simply change the /tmp to a host:port pair, both in Apache's configuration and on the command line of your application.

Upgrading

Upgrading your application to newer Catalyst versions is quite simple. After installing the new Catalyst package, just run:

    catalyst.pl -scripts

One level above your application directory. This will update the scripts directory only, and leave the rest of your app alone. If you wish to make use of other parts of Catalyst that have been updated, leave off the -scripts argument, this will cause .new files to appear, for each module that has either been updated, or is different to the original because you have changed it. To find out what these changes are, type:

    diff tutorial/lib/tutorial/View/TT.pm tutorial/lib/tutorial/View/TT.pm.new

for each of the changed files. (This is a Unix command; Windows users will need to find some equivalent.) Copy any changes you need into your original file, then remove the .new files. (This makes life less complicated when the next upgrade comes around.)


AUTHORS

Jess Robinson, jrobinson@cpan.org Andrew Ford, A.Ford@ford-mason.co.uk Marcus Ramberg, mramberg@cpan.org Kieren Diment, kd@totaldatasolution.com Gavin Henry, ghenry@cpan.org

Please send comments, corrections and suggestions for improvements to jrobinson@cpan.org, ghenry@cpan.org


TODO

Finish DBIC examples with templates and tested code. Make /users/groups do something ``useful''.

Many other things....


COPYRIGHT

This program is free software, you can redistribute it and/or modify it under the same terms as Perl itself.

 Catalyst::Manual::Tutorial - Getting started with Catalyst