Catalyst
by Jesse Sheidlower
|
Pages: 1, 2
Sample Application: MiniMojo, an Ajax-Based Wiki in 30 Lines of Written Code
Now that you have a sense of what Catalyst is, it's time to look at what it
can do. The example application is MiniMojo, a wiki based on Ajax,
which is a JavaScript framework that uses the XMLHttpRequest object to create
highly dynamic web pages without needing to send full pages back and forth
between the server and client.
Remember that from the Catalyst perspective, Ajax is just a case of sending more text to the browser, except that this text is in the form of client-side JavaScript that talks to the server, rather than a boilerplate copyright notice or a navigation sidebar. It makes no difference to Catalyst.
Installation
Catalyst has a relatively large number of requirements; most, however, are easy to install, along with their dependencies, from CPAN. The following list should take care of everything you need for this project:
- Catalyst
- Catalyst::Model::CDBI
- Class::DBI::SQLite
- Catalyst::View::TT
- Catalyst::Plugin::Textile
- Catalyst::Plugin::Prototype
- SQLite (the binary, not the Perl module)
Generate the Application Skeleton
Run this command:
$ catalyst.pl MiniMojo
$ cd MiniMojo
You've just created the skeleton for your entire application, complete with a helper script keyed to MiniMojo to generate individual classes, basic test scripts, and more.
Run the built-in server:
$ script/minimojo_server.pl
MiniMojo is already running, though it isn't doing much
just yet. (You should have received a web page consisting solely
of the text "Congratulations, MiniMojo is on Catalyst!") Press
Ctrl-C to stop the server.
Add Basic Methods to Your Application Class
Add a private end action to your application class,
lib/MiniMojo.pm, by editing the new file:
sub end : Private {
my ( $self, $c ) = @_;
$c->forward('MiniMojo::V::TT') unless $c->res->output;
}
Catalyst automatically calls the end action at the end of a
request cycle. It's one of four built-in Private actions. It's a typical
pattern in Catalyst to use end to forward the application to the
View component for rendering, though if necessary you could do it yourself (for
example, if you want to use different Views in the same application--perhaps
one to generate web pages with Template Toolkit and another to generate PDFs
with PDF::Template).
Replace the existing, helper-generated default
action in the same class with:
sub default : Private {
my ( $self, $c ) = @_;
$c->forward('/page/show');
}
In case the client has specified no other appropriate action, this will
forward on to the page controller's show method. As Private
actions, nothing can call these from outside the application. Any method from
within the application can call them. The default action is
another built-in Private action, along with begin,
auto, and end. Again, Catalyst calls them
automatically at relevant points in the request cycle.
Set Up the Model (SQLite Database) and Use the Helper to Create Model Classes
Next, create a file, minimojo.sql, that contains the SQL for setting
up your page table in SQLite.
-- minimojo.sql
CREATE TABLE page (
id INTEGER PRIMARY KEY,
title TEXT,
body TEXT
);
Create a database from it, using the sqlite command-line
program:
$ sqlite minimojo.db < minimojo.sql
Depending on your setup, it might be necessary to call this as sqlite3.
Use the helper to create model classes and basic unit tests (Figure 3 shows the results):
$ script/minimojo_create.pl model CDBI CDBI dbi:SQLite:/path/to/minimojo.db

Figure 3. Creating the model
The minimojo_create.pl script is a helper that uses Template
Toolkit to automate the creation of particular modules. The previous command
creates a model (in contrast to a controller or a view) called
CDBI.pm, using the CDBI helper, setting the connection string to
dbi:SQLite:/path/to/minimojo.db, the database you just created. (Use the appropriate path for
your system.) The helper will write the models into lib/MiniMojo/M/.
There are various options for the helper scripts; the only requirement is the
type and the name. (You can create your own modules from scratch, without using
the helper.)
Set Up the View (Template::Toolkit) and Use the Helper to
Create View Classes
Use the helper to create a view class:
$ script/minimojo_create.pl view TT TT
View classes go into lib/MiniMojo/V/.
Set Up a Controller Class Using the Helper
Create a controller class called Page with the helper:
$ script/minimojo_create.pl controller Page
Controller classes live in lib/MiniMojo/C/.
Add a show action to lib/MiniMojo/C/Page.pm:
sub show : Regex('^(\w+)\.html$') {
my ( $self, $c ) = @_;
$c->stash->{template} = 'view.tt';
# $c->forward('page');
}
The Regex dispatch matches a page in foo.html, where
foo is any sequence of word characters. This sequence is available
in the $context->request->snippets array, where the page
action uses it to display an existing page or to create a new one. The rest of
this action sets the appropriate template and sends the application to the
page action. (Leave the forward command commented out until you have written the page action.)
Restart the server with $ script/minimojo_server.pl and point a web browser to http://localhost:3000/show/ to see
the debug screen (you don't yet have the template that show is
trying to send people to).
Create root/view.tt:
<html>
<head><title>MiniMojo</title></head>
<body>
<h1>MiniMojo is set up!</h1>
</body>
</html>
Test again by killing the server with Ctrl-C and restarting it, and go to
http://localhost:3000/show/. You should see the page you just
defined.
Add the Display and Edit Code
Modify the application class lib/MiniMojo.pm to include the
Prototype and Textile plugins:
use Catalyst qw/-Debug Prototype Textile/;
Note that you can use the plugins by specifying their base names; Catalyst
figures out what you mean without making you use
Catalyst::Plugin::Prototype.
Modify the page controller, lib/MiniMojo/C/Page.pm, to
add page-view and editing code:
sub page : Private {
my ( $self, $c, $title ) = @_;
$title ||= $c->req->snippets->[0] || 'Frontpage';
my $query = { title => $title };
$c->stash->{page} = MiniMojo::M::CDBI::Page->find_or_create($query);
}
The private page method sets a title--whether passed in to it,
taken from the snippets array (that matches the regex in
show), or defaulting to "Frontpage." The $query
variable holds a hashref used for Class::DBI's
find_or_create method, seeding the stash for the page
variable with the result of this CDBI query. At the end of the method, control
flow returns to the calling method.
Now uncomment the $c->forward('page'); line in the show action.
sub edit : Local {
my ( $self, $c, $title ) = @_;
$c->forward('page');
$c->stash->{page}->body( $c->req->params->{body} )
if $c->req->params->{body};
my $body = $c->stash->{page}->body || 'Just type something...';
my $html = $c->textile->process($body);
my $base = $c->req->base;
$html =~ s{(?<![\?\\\/\[])(\b[A-Z][a-z]+[A-Z]\w*)}
{<a href="$base$1.html">$1</a>}g;
$c->res->output($html);
}
The edit method first forwards the action off to
page, so that the stash's page object contains the
result of the CDBI query. If there is a value for body, it will
use this; otherwise "Just type something..." is the default. The code then
processes the body with Textile, which converts plain text to HTML, and then runs
the body through a regex to convert camel-case text into links, with the URL
base taken from the Catalyst request object. Finally, it outputs the HTML.
Set Up the Wiki with Ajax
Modify root/view.tt to include Ajax code:
<html>
<head><title>MiniMojo</title></head>
[% c.prototype.define_javascript_functions %]
[% url = base _ 'page/edit/' _ page.title %]
<body Onload="new Ajax.Updater( 'view', '[% url %]' )">
<h1>[% page.title %]</h1>
<div id="view"></div>
<textarea id="editor" rows="24" cols="80">[% page.body %]</textarea>
[% c.prototype.observe_field( 'editor', {
url => url,
with => "'body='+value",
update => 'view' }
) %]
</body>
</html>
The line:
[% c.prototype.define_javascript_functions %]
includes the whole prototype.js library in a script
block. Note that the prototype plugin is available in the context
object.
The section
[% url = base _ 'page/edit/' _ page.title %]
<body Onload="new Ajax.Updater( 'view', '[% url %]' )">
<h1>[% page.title %]</h1>
<div id="view"></div>
constructs the Ajax URL and updates the view div when loading
the page.
Finally:
<textarea id="editor" rows="24" cols="80">[% page.body %]</textarea>
[% c.prototype.observe_field( 'editor', {
url => url,
with => "'body='+value",
update => 'view' }
) %]
periodically checks the textarea for changes and makes an Ajax
request on demand.
That's it! Now you can re-run the server and your wiki is up and running
(Figure 4). To use the wiki, simply start typing in the textarea.
As you type, the wiki will regularly echo your entry above, passing it through
the formatter. When you type something in camel case, it will automatically
create a link you can click to go to the new page.

Figure 4. The running wiki
Enjoy your new Catalyst-powered Ajax wiki!
Resources
For more information, see the Catalyst documentation, in particular the Catalyst::Manual::Intro module, which gives a thorough introduction to the framework. There are two Catalyst mailing lists, a general list and a developer list. The best place to discuss Catalyst, though, is the #catalyst IRC channel at irc.perl.org. The Catalyst home page is currently just a collection of a few links, but we will extend it in the near future.
Thanks to Catalyst lead developer Sebastian Riedel for help with this article and, of course, for Catalyst itself.
|
Related Reading Advanced Perl Programming |
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 17 of 17.
- 2005... wow
2009-07-28 14:05:20 dandv [Reply]
I'm surprised at how much coverage this article has received since 2005.
Anyway, it's 2009 now - please look at the full-blows Catalyst-based AJAX wiki, MojoMojo. It's open-sourced on http://github.com/marcusramberg/mojomojo/ and can be seen live at http://mojomojo.org/advocacy.
It also powers the Catalyst wiki, http://dev.catalyst.perl.org/wiki/
HTH,
Dan Dascalescu
- Clarity in instruction needed...
2009-07-12 18:26:22 abrooks [Reply]
One of the instructions is rather vague to me because it says,
"Replace the existing, helper-generated default action in the same class with:
sub default : Private {
my ( $self, $c ) = @_;
$c->forward('/page/show');
}"
Yet if it is referring to the Minimojo.pm file, then this file does not contain a helper-generated "default" action. Therefore, this leads me to wonder if the instruction was meant for a different file. Possibly the Root.pm in the lib/Minimojo/Controller directory instead.- Clarity in instruction needed...
2009-07-13 09:05:44 the_jester [Reply]
The instructions in the published version of the article refer to (much) earlier versions of Catalyst; in current versions the default sub would indeed be generated in the Root controller.
Unfortunately it's not possible to modify the article to reflect this.
Thanks.
- Clarity in instruction needed...
- Best Practices Update updated
2006-06-17 20:21:33 newuser007 [Reply]
MiniMojo has been refactored to fit with current catalyst best practices. You can obtain it from the prokect svn repository from:
http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/MiniMojoDBIC/
or from the project wiki:
http://dev.catalyst.perl.org/attachment/wiki/WikiStart/MiniMojoDBIC.tar.gz
- Best-practice updates (from the author)
2006-05-21 18:53:26 the_jester [Reply]
This example should still work properly under current versions of Catalyst.
However, there are certain aspects of the example that should change, either because Catalyst has changed to allow better functionality, or because the Catalyst community has changed its mind about development practices. The Catalyst documentation, in particular Catalyst::Manual::Tutorial in the core distribution, provides more up-to-date discussion of current best practices. (And at the time of the writing of this note, we are thoroughly revising the Tutorial and other parts of the core docs to make them more extensive and more clear.)
Some of the issues include:
The use of M|V|C instead of Model|View|Controller in lib/MiniMojo/ . We currently recommend Model|View|Controller (the M|V|C directories work, but are deprecated, and will no longer correspond to current docs).
Placing global methods into the application class (in this example, MiniMojo.pm) can cause namespace collisions in certain conditions. In current versions of Catalyst (5.66 or later), we recommend that only application configuration should be placed in MiniMojo.pm; global Controller actions should go in MiniMojo/Controller/Root.pm.
Current Catalyst development normally uses DBIx::Class as its database interface layer, instead of Class::DBI.
We hope to release a tarball containing a best-practices version of MiniMojo shortly. We'll post here when this happens.
Thanks for your interest in Catalyst.
Jesse Sheidlower
- Content Escaping
2006-03-16 19:56:22 claco [Reply]
Posting of updates will truncate content if there are single/double quotes/equals in the textarea. To fix this, change the view.tt template to escape the 'with' option of observe_field:
with => "'body='+value",
to
with => "'body='+escape(value)",
- needs sqlite 3
2005-12-13 22:47:28 codyk [Reply]
just in case someone else runs into the "DBD::SQLite::db prepare failed: file is encrypted or is not a database(26)" error, its because DBD:SQLite requires sqlite3, not 2.
- MiniMojo::M::CDBI::Page empty?
2005-08-24 08:51:01 nbest [Reply]
Thanks for this article. I learn the most by looking at examples. I am stuck on something at the moment, the Page model. What is it? I see in the eterm screen shot where it got created, but when I look at the source in the Catalyst Trac trunk it appears to be an empty class. Am I missing something? Maybe I should just install it on my system and see what it does, but I am more interested in writing my own app once I get the hang of it. Sorry if this should be obvious, but I have been staring at it for a while.
- MiniMojo::M::CDBI::Page empty?
2005-08-24 09:08:06 nbest [Reply]
I presume that it inherits what it needs, but why is it necessary?- MiniMojo::M::CDBI::Page empty?
2005-08-24 10:38:07 nbest [Reply]
I guess it's how the method knows which table to look at. How's that for answering my own question? There may be further subtleties here that escape me, such as how Catalyst knew to create it.
- MiniMojo::M::CDBI::Page empty?
- MiniMojo::M::CDBI::Page empty?
- Trouble with the tutorial
2005-07-08 06:33:54 MattKing [Reply]
Hi hate to sound like a n3wb here, but I am really excited to start learning and using the Catalyst framework...so here goes:
I am "succesfull" at doing all the steps in the tutorial, however I never get any web pages except the browser saying:
"Congratulations, MiniMojo is on Catalyst!"
Even on the step after creating view.tt, where you say I should see the debug information on the screen.
After finishing all the steps, I still see the same thing. Am I missing something?
Thanks,
Matt- Trouble with the tutorial
2008-06-11 17:19:37 fapestniegd [Reply]
It tells me
.----------------------+--------------------------------------+--------------.
| Private | Class | Method |
+----------------------+--------------------------------------+--------------+
| /show | MiniMojo::Controller::Root | show |
| /default | MiniMojo::Controller::Root | default |
| /end | MiniMojo::Controller::Root | end |
| /page/index | MiniMojo::Controller::Page | index |
| /page/show | MiniMojo::Controller::Page | show |
'----------------------+--------------------------------------+--------------'
but when I go to any of the above it just states:
.----------------------------------------------------------------+-----------.
| Action | Time |
+----------------------------------------------------------------+-----------+
| /default | 0.000540s |
| /end | 0.000160s |
'----------------------------------------------------------------+-----------'
not sure why
- Trouble with the tutorial
2008-06-11 17:15:15 fapestniegd [Reply]
Same here, nothing seems to change the page.
- Trouble with the tutorial
- Very Impressive
2005-06-08 07:53:08 JoeCumming [Reply]
This is a nice article and a very impressive framework.
I was a fan of Maypole when it came out - what's that comment about standing of the shoulders of giants? - but was frustrated by the inflexibility of the model.
Catalyst appears to address most of the issues I had with Maypole and has expanded well beyond the maypole model. I particularly like the modular plugin approach which, theoretically, makes building extra functionality plug & play
I suspect that this is what Perl web applications will be built on in the future. I'll be watching future developments with interest.
Thanks for an excellent framework.
- Thanks for the article
2005-06-08 00:38:00 pmccann [Reply]
Most enjoyable: I wonder whether the lack of comments reflects the fact that perl.com articles seldom have comments enabled?
In any case: following the steps provided had Minimojo up and running in no time flat. Where I got a bit stuck was in trying to "upgrade" the application a little bit. In particular I was trying to stick in a simple stylesheet link in the view.tt page. Say something like
<link rel="stylesheet" type="text/css" href="help.css">
[fingers crossed on the fact that is is not going to turn that line into a disaster!]
Given the above, and given that I had set the root configuration variable to be MiniMojo/root, where should "help.css" live so as to be seen by the included web server? I tried in MiniMojo/root, but still got my unstyled pages (the html includes the link, it's just that the server doesn't seem to find the file). The description of the root variable makes it *sound* like this should be the place, but maybe the "default" action is preventing such a thing? Anyway, hope this makes some sort of sense: thanks for any help,
Paul
- Thanks for the article
2005-06-09 16:11:28 sriatoookde [Reply]
You can use Catalyst::Plugin::Static to serve static files. ;)- Thanks for the article
2005-06-09 18:13:01 pmccann [Reply]
Beautiful, thanks...
- Thanks for the article
- Thanks for the article








