Expand roles section to contain specific docs on caveats, and version numbers when...
[catagits/Catalyst-Manual.git] / lib / Catalyst / Manual / Cookbook.pod
CommitLineData
cb93c9d7 1=head1 NAME
2
3Catalyst::Manual::Cookbook - Cooking with Catalyst
4
5=head1 DESCRIPTION
6
7Yummy code like your mum used to bake!
8
9=head1 RECIPES
10
11=head1 Basics
12
c718cfb6 13These recipes cover some basic stuff that is worth knowing for
14catalyst developers.
cb93c9d7 15
16=head2 Delivering a Custom Error Page
17
18By default, Catalyst will display its own error page whenever it
19encounters an error in your application. When running under C<-Debug>
c718cfb6 20mode, the error page is a useful screen including the error message
21and L<Data::Dump> output of the relevant parts of the C<$c> context
22object. When not in C<-Debug>, users see a simple "Please come back
23later" screen.
cb93c9d7 24
c718cfb6 25To use a custom error page, use a special C<end> method to
26short-circuit the error processing. The following is an example; you
27might want to adjust it further depending on the needs of your
28application (for example, any calls to C<fillform> will probably need
29to go into this C<end> method; see L<Catalyst::Plugin::FillInForm>).
cb93c9d7 30
31 sub end : Private {
32 my ( $self, $c ) = @_;
33
34 if ( scalar @{ $c->error } ) {
35 $c->stash->{errors} = $c->error;
36 $c->stash->{template} = 'errors.tt';
37 $c->forward('MyApp::View::TT');
38 $c->error(0);
39 }
40
41 return 1 if $c->response->status =~ /^3\d\d$/;
42 return 1 if $c->response->body;
43
44 unless ( $c->response->content_type ) {
45 $c->response->content_type('text/html; charset=utf-8');
46 }
47
48 $c->forward('MyApp::View::TT');
49 }
50
51You can manually set errors in your code to trigger this page by calling
52
53 $c->error( 'You broke me!' );
54
55=head2 Disable statistics
56
c718cfb6 57Just add this line to your application class if you don't want those
58nifty statistics in your debug messages.
cb93c9d7 59
60 sub Catalyst::Log::info { }
61
62=head2 Enable debug status in the environment
63
64Normally you enable the debugging info by adding the C<-Debug> flag to
b411df01 65your C<use Catalyst> statement . However, you can also enable it using
66environment variable, so you can (for example) get debug info without
67modifying your application scripts. Just set C<CATALYST_DEBUG> or
68C<E<lt>MYAPPE<gt>_DEBUG> to a true value.
cb93c9d7 69
70=head2 Sessions
71
c718cfb6 72When you have your users identified, you will want to somehow remember
73that fact, to save them from having to identify themselves for every
74single page. One way to do this is to send the username and password
75parameters in every single page, but that's ugly, and won't work for
76static pages.
cb93c9d7 77
c718cfb6 78Sessions are a method of saving data related to some transaction, and
79giving the whole collection a single ID. This ID is then given to the
80user to return to us on every page they visit while logged in. The
81usual way to do this is using a browser cookie.
cb93c9d7 82
83Catalyst uses two types of plugins to represent sessions:
84
85=head3 State
86
c718cfb6 87A State module is used to keep track of the state of the session
88between the users browser, and your application.
cb93c9d7 89
c718cfb6 90A common example is the Cookie state module, which sends the browser a
91cookie containing the session ID. It will use default value for the
92cookie name and domain, so will "just work" when used.
cb93c9d7 93
94=head3 Store
95
c718cfb6 96A Store module is used to hold all the data relating to your session,
97for example the users ID, or the items for their shopping cart. You
98can store data in memory (FastMmap), in a file (File) or in a database
99(DBI).
cb93c9d7 100
101=head3 Authentication magic
102
103If you have included the session modules in your application, the
104Authentication modules will automagically use your session to save and
105retrieve the user data for you.
106
107=head3 Using a session
108
109Once the session modules are loaded, the session is available as C<<
c718cfb6 110$c->session >>, and can be writen to and read from as a simple hash
111reference.
cb93c9d7 112
113=head3 EXAMPLE
114
ca7528df 115 use parent qw/Catalyst/;
b411df01 116 use Catalyst qw/
ca7528df 117 Session
118 Session::Store::FastMmap
119 Session::State::Cookie
b411df01 120 /;
cb93c9d7 121
122
123 ## Write data into the session
124
125 sub add_item : Local {
126 my ( $self, $c ) = @_;
127
128 my $item_id = $c->req->param("item");
129
130 push @{ $c->session->{items} }, $item_id;
131
132 }
133
134 ## A page later we retrieve the data from the session:
135
136 sub get_items : Local {
137 my ( $self, $c ) = @_;
138
139 $c->stash->{items_to_display} = $c->session->{items};
140
141 }
142
143
144=head3 More information
145
146L<http://search.cpan.org/dist/Catalyst-Plugin-Session>
147
148L<http://search.cpan.org/dist/Catalyst-Plugin-Session-State-Cookie>
149
150L<http://search.cpan.org/dist/Catalyst-Plugin-Session-State-URI>
151
152L<http://search.cpan.org/dist/Catalyst-Plugin-Session-Store-FastMmap>
153
154L<http://search.cpan.org/dist/Catalyst-Plugin-Session-Store-File>
155
156L<http://search.cpan.org/dist/Catalyst-Plugin-Session-Store-DBI>
157
158=head2 Configure your application
159
160You configure your application with the C<config> method in your
161application class. This can be hard-coded, or brought in from a
162separate configuration file.
163
c010ae0d 164=head3 Using Config::General
cb93c9d7 165
c010ae0d 166L<Config::General|Config::General> is a method for creating flexible
167and readable configuration files. It's a great way to keep your
168Catalyst application configuration in one easy-to-understand location.
cb93c9d7 169
c010ae0d 170Now create C<myapp.conf> in your application home:
cb93c9d7 171
c010ae0d 172 name MyApp
cb93c9d7 173
174 # session; perldoc Catalyst::Plugin::Session::FastMmap
c010ae0d 175 <Session>
176 expires 3600
177 rewrite 0
178 storage /tmp/myapp.session
179 </Session>
cb93c9d7 180
181 # emails; perldoc Catalyst::Plugin::Email
182 # this passes options as an array :(
c010ae0d 183 Mail SMTP
184 Mail localhost
cb93c9d7 185
186This is equivalent to:
187
188 # configure base package
189 __PACKAGE__->config( name => MyApp );
190 # configure authentication
191 __PACKAGE__->config->{authentication} = {
192 user_class => 'MyApp::Model::MyDB::Customer',
193 ...
194 };
195 # configure sessions
196 __PACKAGE__->config->{session} = {
197 expires => 3600,
198 ...
199 };
200 # configure email sending
201 __PACKAGE__->config->{email} = [qw/SMTP localhost/];
202
c010ae0d 203See also L<Config::General|Config::General>.
cb93c9d7 204
205=head1 Skipping your VCS's directories
206
207Catalyst uses Module::Pluggable to load Models, Views and Controllers.
208Module::Pluggable will scan through all directories and load modules
209it finds. Sometimes you might want to skip some of these directories,
210for example when your version control system makes a subdirectory with
211meta-information in every version-controlled directory. While
212Catalyst skips subversion and CVS directories already, there are other
213source control systems. Here is the configuration you need to add
214their directories to the list to skip.
215
216You can make catalyst skip these directories using the Catalyst config:
217
218 # Configure the application
219 __PACKAGE__->config(
220 name => 'MyApp',
221 setup_components => { except => qr/SCCS/ },
222 );
223
224See the Module::Pluggable manual page for more information on B<except>
225and other options.
226
227=head1 Users and Access Control
228
229Most multiuser, and some single user web applications require that
230users identify themselves, and the application is often required to
231define those roles. The recipes below describe some ways of doing
232this.
233
234=head2 Authentication (logging in)
235
236This is extensively covered in other documentation; see in particular
237L<Catalyst::Plugin::Authentication> and the Authentication chapter
238of the Tutorial at L<Catalyst::Manual::Tutorial::Authorization>.
239
240=head2 Pass-through login (and other actions)
241
242An easy way of having assorted actions that occur during the processing
243of a request that are orthogonal to its actual purpose - logins, silent
244commands etc. Provide actions for these, but when they're required for
245something else fill e.g. a form variable __login and have a sub begin
246like so:
247
248 sub begin : Private {
249 my ($self, $c) = @_;
250 foreach my $action (qw/login docommand foo bar whatever/) {
251 if ($c->req->params->{"__${action}"}) {
252 $c->forward($action);
253 }
254 }
255 }
256
257
258=head2 Role-based Authorization
259
260For more advanced access control, you may want to consider using role-based
261authorization. This means you can assign different roles to each user, e.g.
262"user", "admin", etc.
263
264The C<login> and C<logout> methods and view template are exactly the same as
265in the previous example.
266
267The L<Catalyst::Plugin::Authorization::Roles> plugin is required when
268implementing roles:
269
ca7528df 270 use parent qw/Catalyst/;
b411df01 271 use Catalyst qw/
ca7528df 272 Authentication
273 Authentication::Credential::Password
274 Authentication::Store::Htpasswd
b411df01 275 Authorization::Roles/;
cb93c9d7 276
277Roles are implemented automatically when using
278L<Catalyst::Authentication::Store::Htpasswd>:
279
280 # no additional role configuration required
281 __PACKAGE__->config->{authentication}{htpasswd} = "passwdfile";
282
283Or can be set up manually when using L<Catalyst::Authentication::Store::DBIC>:
284
285 # Authorization using a many-to-many role relationship
286 __PACKAGE__->config->{authorization}{dbic} = {
287 'role_class' => 'My::Model::DBIC::Role',
288 'role_field' => 'name',
289 'user_role_user_field' => 'user',
290
291 # DBIx::Class only (omit if using Class::DBI)
292 'role_rel' => 'user_role',
293
294 # Class::DBI only, (omit if using DBIx::Class)
295 'user_role_class' => 'My::Model::CDBI::UserRole'
296 'user_role_role_field' => 'role',
297 };
298
299To restrict access to any action, you can use the C<check_user_roles> method:
300
301 sub restricted : Local {
302 my ( $self, $c ) = @_;
303
304 $c->detach("unauthorized")
305 unless $c->check_user_roles( "admin" );
306
307 # do something restricted here
308 }
309
c718cfb6 310You can also use the C<assert_user_roles> method. This just gives an
311error if the current user does not have one of the required roles:
cb93c9d7 312
313 sub also_restricted : Global {
314 my ( $self, $c ) = @_;
315 $c->assert_user_roles( qw/ user admin / );
316 }
317
318=head2 Authentication/Authorization
319
320This is done in several steps:
321
322=over 4
323
324=item Verification
325
326Getting the user to identify themselves, by giving you some piece of
c718cfb6 327information known only to you and the user. Then you can assume that
328the user is who they say they are. This is called B<credential
329verification>.
cb93c9d7 330
331=item Authorization
332
c718cfb6 333Making sure the user only accesses functions you want them to
334access. This is done by checking the verified users data against your
335internal list of groups, or allowed persons for the current page.
cb93c9d7 336
337=back
338
339=head3 Modules
340
c718cfb6 341The Catalyst Authentication system is made up of many interacting
342modules, to give you the most flexibility possible.
cb93c9d7 343
344=head4 Credential verifiers
345
c718cfb6 346A Credential module tables the user input, and passes it to a Store,
347or some other system, for verification. Typically, a user object is
348created by either this module or the Store and made accessible by a
349C<< $c->user >> call.
cb93c9d7 350
351Examples:
352
353 Password - Simple username/password checking.
354 HTTPD - Checks using basic HTTP auth.
355 TypeKey - Check using the typekey system.
356
357=head3 Storage backends
358
c718cfb6 359A Storage backend contains the actual data representing the users. It
360is queried by the credential verifiers. Updating the store is not done
361within this system, you will need to do it yourself.
cb93c9d7 362
363Examples:
364
365 DBIC - Storage using a database.
366 Minimal - Storage using a simple hash (for testing).
367
368=head3 User objects
369
c718cfb6 370A User object is created by either the storage backend or the
371credential verifier, and filled with the retrieved user information.
cb93c9d7 372
373Examples:
374
375 Hash - A simple hash of keys and values.
376
377=head3 ACL authorization
378
c718cfb6 379ACL stands for Access Control List. The ACL plugin allows you to
380regulate access on a path by path basis, by listing which users, or
381roles, have access to which paths.
cb93c9d7 382
383=head3 Roles authorization
384
c718cfb6 385Authorization by roles is for assigning users to groups, which can
386then be assigned to ACLs, or just checked when needed.
cb93c9d7 387
388=head3 Logging in
389
390When you have chosen your modules, all you need to do is call the C<<
391$c->login >> method. If called with no parameters, it will try to find
c718cfb6 392suitable parameters, such as B<username> and B<password>, or you can
393pass it these values.
cb93c9d7 394
395=head3 Checking roles
396
c718cfb6 397Role checking is done by using the C<< $c->check_user_roles >> method,
398this will check using the currently logged in user (via C<< $c->user
399>>). You pass it the name of a role to check, and it returns true if
400the user is a member.
cb93c9d7 401
402=head3 EXAMPLE
403
ca7528df 404 use parent qw/Catalyst/;
b411df01 405 use Catalyst qw/Authentication
406 Authentication::Credential::Password
407 Authentication::Store::Htpasswd
408 Authorization::Roles/;
cb93c9d7 409
410 __PACKAGE__->config->{authentication}{htpasswd} = "passwdfile";
411
412 sub login : Local {
413 my ($self, $c) = @_;
414
415 if ( my $user = $c->req->param("user")
416 and my $password = $c->req->param("password") )
417 {
418 if ( $c->login( $user, $password ) ) {
419 $c->res->body( "hello " . $c->user->name );
420 } else {
421 # login incorrect
422 }
423 }
424 else {
425 # invalid form input
426 }
427 }
428
429 sub restricted : Local {
430 my ( $self, $c ) = @_;
431
432 $c->detach("unauthorized")
433 unless $c->check_user_roles( "admin" );
434
435 # do something restricted here
436 }
437
438=head3 Using authentication in a testing environment
439
c718cfb6 440Ideally, to write tests for authentication/authorization code one
441would first set up a test database with known data, then use
442L<Test::WWW::Mechanize::Catalyst> to simulate a user logging
443in. Unfortunately the former can be rather awkward, which is why it's
444a good thing that the authentication framework is so flexible.
cb93c9d7 445
c718cfb6 446Instead of using a test database, one can simply change the
447authentication store to something a bit easier to deal with in a
448testing environment. Additionally, this has the advantage of not
449modifying one's database, which can be problematic if one forgets to
450use the testing instead of production database.
cb93c9d7 451
452e.g.,
453
454 use Catalyst::Plugin::Authentication::Store::Minimal::Backend;
455
456 # Sets up the user `test_user' with password `test_pass'
457 MyApp->default_auth_store(
458 Catalyst::Plugin::Authentication::Store::Minimal::Backend->new({
459 test_user => { password => 'test_pass' },
460 })
461 );
462
463Now, your test code can call C<$c->login('test_user', 'test_pass')> and
464successfully login, without messing with the database at all.
465
466=head3 More information
467
468L<http://search.cpan.org/perldoc?Catalyst::Plugin::Authentication> has a longer explanation.
469
470=head2 Authorization
471
472=head3 Introduction
473
c718cfb6 474Authorization is the step that comes after
475authentication. Authentication establishes that the user agent is
476really representing the user we think it's representing, and then
477authorization determines what this user is allowed to do.
cb93c9d7 478
479=head3 Role Based Access Control
480
c718cfb6 481Under role based access control each user is allowed to perform any
482number of roles. For example, at a zoo no one but specially trained
483personnel can enter the moose cage (Mynd you, møøse bites kan be
484pretty nasti!). For example:
cb93c9d7 485
486 package Zoo::Controller::MooseCage;
487
488 sub feed_moose : Local {
489 my ( $self, $c ) = @_;
490
491 $c->model( "Moose" )->eat( $c->req->param("food") );
492 }
493
c718cfb6 494With this action, anyone can just come into the moose cage and feed
495the moose, which is a very dangerous thing. We need to restrict this
496action, so that only a qualified moose feeder can perform that action.
cb93c9d7 497
c718cfb6 498The Authorization::Roles plugin let's us perform role based access
499control checks. Let's load it:
cb93c9d7 500
ca7528df 501 use parent qw/Catalyst/;
b411df01 502 use Catalyst qw/
503 Authentication # yadda yadda
504 Authorization::Roles
505 /;
cb93c9d7 506
507And now our action should look like this:
508
509 sub feed_moose : Local {
510 my ( $self, $c ) = @_;
511
512 if ( $c->check_roles( "moose_feeder" ) ) {
513 $c->model( "Moose" )->eat( $c->req->param("food") );
514 } else {
515 $c->stash->{error} = "unauthorized";
516 }
517 }
518
c718cfb6 519This checks C<< $c->user >>, and only if the user has B<all> the roles
520in the list, a true value is returned.
cb93c9d7 521
c718cfb6 522C<check_roles> has a sister method, C<assert_roles>, which throws an
523exception if any roles are missing.
cb93c9d7 524
525Some roles that might actually make sense in, say, a forum application:
526
527=over 4
528
529=item *
530
531administrator
532
533=item *
534
535moderator
536
537=back
538
c718cfb6 539each with a distinct task (system administration versus content
540administration).
cb93c9d7 541
542=head3 Access Control Lists
543
544Checking for roles all the time can be tedious and error prone.
545
c718cfb6 546The Authorization::ACL plugin let's us declare where we'd like checks
547to be done automatically for us.
cb93c9d7 548
549For example, we may want to completely block out anyone who isn't a
550C<moose_feeder> from the entire C<MooseCage> controller:
551
552 Zoo->deny_access_unless( "/moose_cage", [qw/moose_feeder/] );
553
c718cfb6 554The role list behaves in the same way as C<check_roles>. However, the
555ACL plugin isn't limited to just interacting with the Roles plugin. We
556can use a code reference instead. For example, to allow either moose
557trainers or moose feeders into the moose cage, we can create a more
558complex check:
cb93c9d7 559
560 Zoo->deny_access_unless( "/moose_cage", sub {
561 my $c = shift;
562 $c->check_roles( "moose_trainer" ) || $c->check_roles( "moose_feeder" );
563 });
564
c718cfb6 565The more specific a role, the earlier it will be checked. Let's say
566moose feeders are now restricted to only the C<feed_moose> action,
567while moose trainers get access everywhere:
cb93c9d7 568
569 Zoo->deny_access_unless( "/moose_cage", [qw/moose_trainer/] );
570 Zoo->allow_access_if( "/moose_cage/feed_moose", [qw/moose_feeder/]);
571
c718cfb6 572When the C<feed_moose> action is accessed the second check will be
573made. If the user is a C<moose_feeder>, then access will be
574immediately granted. Otherwise, the next rule in line will be tested -
575the one checking for a C<moose_trainer>. If this rule is not
576satisfied, access will be immediately denied.
cb93c9d7 577
c718cfb6 578Rules applied to the same path will be checked in the order they were
579added.
cb93c9d7 580
c718cfb6 581Lastly, handling access denial events is done by creating an
582C<access_denied> private action:
cb93c9d7 583
584 sub access_denied : Private {
585 my ( $self, $c, $action ) = @_;
cb93c9d7 586 }
587
c718cfb6 588This action works much like auto, in that it is inherited across
589namespaces (not like object oriented code). This means that the
590C<access_denied> action which is B<nearest> to the action which was
591blocked will be triggered.
cb93c9d7 592
c718cfb6 593If this action does not exist, an error will be thrown, which you can
594clean up in your C<end> private action instead.
cb93c9d7 595
c718cfb6 596Also, it's important to note that if you restrict access to "/" then
597C<end>, C<default>, etc will also be restricted.
cb93c9d7 598
599 MyApp->acl_allow_root_internals;
600
601will create rules that permit access to C<end>, C<begin>, and C<auto> in the
602root of your app (but not in any other controller).
603
604=head1 Models
605
606Models are where application data belongs. Catalyst is exteremely
607flexible with the kind of models that it can use. The recipes here
608are just the start.
609
610=head2 Using existing DBIC (etc.) classes with Catalyst
611
c718cfb6 612Many people have existing Model classes that they would like to use
613with Catalyst (or, conversely, they want to write Catalyst models that
614can be used outside of Catalyst, e.g. in a cron job). It's trivial to
615write a simple component in Catalyst that slurps in an outside Model:
cb93c9d7 616
617 package MyApp::Model::DB;
618 use base qw/Catalyst::Model::DBIC::Schema/;
619 __PACKAGE__->config(
620 schema_class => 'Some::DBIC::Schema',
621 connect_info => ['dbi:SQLite:foo.db', '', '', {AutoCommit=>1}];
622 );
623 1;
624
625and that's it! Now C<Some::DBIC::Schema> is part of your
626Cat app as C<MyApp::Model::DB>.
627
628=head2 DBIx::Class as a Catalyst Model
629
630See L<Catalyst::Model::DBIC::Schema>.
631
8428e94d 632=head2 Create accessors to preload static data once per server instance
633
634When you have data that you want to load just once from the model at
635server load instead of for each request, use mk_group_accessors to
636create accessors and tie them to resultsets in your package that
637inherits from DBIx::Class::Schema
638
639 package My::Schema;
640 use base qw/DBIx::Class::Schema/;
641 __PACKAGE__->register_class('RESULTSOURCEMONIKER',
642 'My::Schema::RESULTSOURCE');
643 __PACKAGE__->mk_group_accessors('simple' =>
644 qw(ACCESSORNAME1 ACCESSORNAME2 ACCESSORNAMEn));
645
646 sub connection {
647 my ($self, @rest) = @_;
648 $self->next::method(@rest);
649 # $self is now a live My::Schema object, complete with DB connection
650
651 $self->ACCESSORNAME1([ $self->resultset('RESULTSOURCEMONIKER')->all ]);
652 $self->ACCESSORNAME2([ $self->resultset('RESULTSOURCEMONIKER')->search({ COLUMN => { '<' => '30' } })->all ]);
653 $self->ACCESSORNAMEn([ $self->resultset('RESULTSOURCEMONIKER')->find(1) ]);
654 }
655
656 1;
657
658and now in the controller, you can now access any of these without a
659per-request fetch:
660
661 $c->stash->{something} = $c->model('My::Schema')->schema->ACCESSORNAMEn;
662
663
cb93c9d7 664=head2 XMLRPC
665
666Unlike SOAP, XMLRPC is a very simple (and imo elegant) web-services
667protocol, exchanging small XML messages like these:
668
669Request:
670
671 POST /api HTTP/1.1
672 TE: deflate,gzip;q=0.3
673 Connection: TE, close
674 Accept: text/xml
675 Accept: multipart/*
676 Host: 127.0.0.1:3000
677 User-Agent: SOAP::Lite/Perl/0.60
678 Content-Length: 192
679 Content-Type: text/xml
680
681 <?xml version="1.0" encoding="UTF-8"?>
682 <methodCall>
683 <methodName>add</methodName>
684 <params>
685 <param><value><int>1</int></value></param>
686 <param><value><int>2</int></value></param>
687 </params>
688 </methodCall>
689
690Response:
691
692 Connection: close
693 Date: Tue, 20 Dec 2005 07:45:55 GMT
694 Content-Length: 133
695 Content-Type: text/xml
696 Status: 200
697 X-Catalyst: 5.70
698
699 <?xml version="1.0" encoding="us-ascii"?>
700 <methodResponse>
701 <params>
702 <param><value><int>3</int></value></param>
703 </params>
704 </methodResponse>
705
706Now follow these few steps to implement the application:
707
7081. Install Catalyst (5.61 or later), Catalyst::Plugin::XMLRPC (0.06 or
709later) and SOAP::Lite (for XMLRPCsh.pl).
710
7112. Create an application framework:
712
713 % catalyst.pl MyApp
714 ...
715 % cd MyApp
716
7173. Add the XMLRPC plugin to MyApp.pm
718
b411df01 719 use Catalyst qw/-Debug Static::Simple XMLRPC/;
cb93c9d7 720
7214. Add an API controller
722
723 % ./script/myapp_create.pl controller API
724
7255. Add a XMLRPC redispatch method and an add method with Remote
726attribute to lib/MyApp/Controller/API.pm
727
85d49fb6 728 sub default :Path {
cb93c9d7 729 my ( $self, $c ) = @_;
730 $c->xmlrpc;
731 }
732
733 sub add : Remote {
734 my ( $self, $c, $a, $b ) = @_;
735 return $a + $b;
736 }
737
738The default action is the entry point for each XMLRPC request. It will
739redispatch every request to methods with Remote attribute in the same
740class.
741
742The C<add> method is not a traditional action; it has no private or
743public path. Only the XMLRPC dispatcher knows it exists.
744
7456. That's it! You have built your first web service. Let's test it with
746XMLRPCsh.pl (part of SOAP::Lite):
747
748 % ./script/myapp_server.pl
749 ...
750 % XMLRPCsh.pl http://127.0.0.1:3000/api
751 Usage: method[(parameters)]
752 > add( 1, 2 )
753 --- XMLRPC RESULT ---
754 '3'
755
756=head3 Tip
757
758Your return data type is usually auto-detected, but you can easily
759enforce a specific one.
760
761 sub add : Remote {
762 my ( $self, $c, $a, $b ) = @_;
763 return RPC::XML::int->new( $a + $b );
764 }
765
766
767
768=head1 Views
769
770Views pertain to the display of your application. As with models,
771catalyst is uncommonly flexible. The recipes below are just a start.
772
773=head2 Catalyst::View::TT
774
775One of the first things you probably want to do when starting a new
776Catalyst application is set up your View. Catalyst doesn't care how you
777display your data; you can choose to generate HTML, PDF files, or plain
778text if you wanted.
779
780Most Catalyst applications use a template system to generate their HTML,
781and though there are several template systems available, Template
782Toolkit is probably the most popular.
783
784Once again, the Catalyst developers have done all the hard work, and
785made things easy for the rest of us. Catalyst::View::TT provides the
786interface to Template Toolkit, and provides Helpers which let us set it
787up that much more easily.
788
789=head3 Creating your View
790
791Catalyst::View::TT provides two different helpers for us to use: TT and
792TTSite.
793
794=head4 TT
795
796Create a basic Template Toolkit View using the provided helper script:
797
798 script/myapp_create.pl view TT TT
799
800This will create lib/MyApp/View/MyView.pm, which is going to be pretty
801empty to start. However, it sets everything up that you need to get
802started. You can now define which template you want and forward to your
803view. For instance:
804
805 sub hello : Local {
806 my ( $self, $c ) = @_;
807
808 $c->stash->{template} = 'hello.tt';
809
810 $c->forward( $c->view('TT') );
811 }
812
813In practice you wouldn't do the forwarding manually, but would
814use L<Catalyst::Action::RenderView>.
815
816=head4 TTSite
817
818Although the TT helper does create a functional, working view, you may
819find yourself having to create the same template files and changing the
820same options every time you create a new application. The TTSite helper
821saves us even more time by creating the basic templates and setting some
822common options for us.
823
824Once again, you can use the helper script:
825
826 script/myapp_create.pl view TT TTSite
827
828This time, the helper sets several options for us in the generated View.
829
830 __PACKAGE__->config({
831 CATALYST_VAR => 'Catalyst',
832 INCLUDE_PATH => [
833 MyApp->path_to( 'root', 'src' ),
834 MyApp->path_to( 'root', 'lib' )
835 ],
836 PRE_PROCESS => 'config/main',
837 WRAPPER => 'site/wrapper',
838 ERROR => 'error.tt2',
839 TIMER => 0
840 });
841
842=over
843
844=item
845
846INCLUDE_PATH defines the directories that Template Toolkit should search
847for the template files.
848
849=item
850
851PRE_PROCESS is used to process configuration options which are common to
852every template file.
853
854=item
855
856WRAPPER is a file which is processed with each template, usually used to
857easily provide a common header and footer for every page.
858
859=back
860
861In addition to setting these options, the TTSite helper also created the
862template and config files for us! In the 'root' directory, you'll notice
863two new directories: src and lib.
864
865Several configuration files in root/lib/config are called by PRE_PROCESS.
866
867The files in root/lib/site are the site-wide templates, called by
868WRAPPER, and display the html framework, control the layout, and provide
869the templates for the header and footer of your page. Using the template
870organization provided makes it much easier to standardize pages and make
871changes when they are (inevitably) needed.
872
873The template files that you will create for your application will go
874into root/src, and you don't need to worry about putting the the <html>
875or <head> sections; just put in the content. The WRAPPER will the rest
876of the page around your template for you.
877
878
879=head3 $c->stash
880
881Of course, having the template system include the header and footer for
882you isn't all that we want our templates to do. We need to be able to
883put data into our templates, and have it appear where and how we want
884it, right? That's where the stash comes in.
885
886In our controllers, we can add data to the stash, and then access it
887from the template. For instance:
888
889 sub hello : Local {
890 my ( $self, $c ) = @_;
891
892 $c->stash->{name} = 'Adam';
893
894 $c->stash->{template} = 'hello.tt';
895
896 $c->forward( $c->view('TT') );
897 }
898
899Then, in hello.tt:
900
901 <strong>Hello, [% name %]!</strong>
902
903When you view this page, it will display "Hello, Adam!"
904
905All of the information in your stash is available, by its name/key, in
906your templates. And your data don't have to be plain, old, boring
907scalars. You can pass array references and hash references, too.
908
909In your controller:
910
911 sub hello : Local {
912 my ( $self, $c ) = @_;
913
914 $c->stash->{names} = [ 'Adam', 'Dave', 'John' ];
915
916 $c->stash->{template} = 'hello.tt';
917
918 $c->forward( $c->view('TT') );
919 }
920
921In hello.tt:
922
923 [% FOREACH name IN names %]
924 <strong>Hello, [% name %]!</strong><br />
925 [% END %]
926
927This allowed us to loop through each item in the arrayref, and display a
928line for each name that we have.
929
930This is the most basic usage, but Template Toolkit is quite powerful,
931and allows you to truly keep your presentation logic separate from the
932rest of your application.
933
934=head3 $c->uri_for()
935
936One of my favorite things about Catalyst is the ability to move an
937application around without having to worry that everything is going to
938break. One of the areas that used to be a problem was with the http
939links in your template files. For example, suppose you have an
940application installed at http://www.domain.com/Calendar. The links point
941to "/Calendar", "/Calendar/2005", "/Calendar/2005/10", etc. If you move
942the application to be at http://www.mydomain.com/Tools/Calendar, then
943all of those links will suddenly break.
944
945That's where $c->uri_for() comes in. This function will merge its
946parameters with either the base location for the app, or its current
947namespace. Let's take a look at a couple of examples.
948
949In your template, you can use the following:
950
951 <a href="[% c.uri_for('/login') %]">Login Here</a>
952
c718cfb6 953Although the parameter starts with a forward slash, this is relative
954to the application root, not the webserver root. This is important to
955remember. So, if your application is installed at
956http://www.domain.com/Calendar, then the link would be
957http://www.mydomain.com/Calendar/Login. If you move your application
958to a different domain or path, then that link will still be correct.
cb93c9d7 959
960Likewise,
961
962 <a href="[% c.uri_for('2005','10', '24') %]">October, 24 2005</a>
963
c718cfb6 964The first parameter does NOT have a forward slash, and so it will be
965relative to the current namespace. If the application is installed at
966http://www.domain.com/Calendar. and if the template is called from
967MyApp::Controller::Display, then the link would become
968http://www.domain.com/Calendar/Display/2005/10/24.
969
970If you want to link to a parent uri of your current namespace you can
971prefix the arguments with multiple '../':
972
973 <a href="[% c.uri_for('../../view', stashed_object.id) %]">User view</a>
cb93c9d7 974
c718cfb6 975Once again, this allows you to move your application around without
976having to worry about broken links. But there's something else, as
977well. Since the links are generated by uri_for, you can use the same
978template file by several different controllers, and each controller
979will get the links that its supposed to. Since we believe in Don't
980Repeat Yourself, this is particularly helpful if you have common
981elements in your site that you want to keep in one file.
cb93c9d7 982
983Further Reading:
984
985L<http://search.cpan.org/perldoc?Catalyst>
986
987L<http://search.cpan.org/perldoc?Catalyst%3A%3AView%3A%3ATT>
988
989L<http://search.cpan.org/perldoc?Template>
990
991=head2 Adding RSS feeds
992
993Adding RSS feeds to your Catalyst applications is simple. We'll see two
994different aproaches here, but the basic premise is that you forward to
995the normal view action first to get the objects, then handle the output
996differently.
997
998=head3 Using TT templates
999
1000This is the aproach used in Agave (L<http://dev.rawmode.org/>).
1001
1002 sub rss : Local {
1003 my ($self,$c) = @_;
1004 $c->forward('view');
1005 $c->stash->{template}='rss.tt';
1006 }
1007
1008Then you need a template. Here's the one from Agave:
1009
1010 <?xml version="1.0" encoding="UTF-8"?>
1011 <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
1012 <channel>
1013 <title>[ [% blog.name || c.config.name || "Agave" %] ] RSS Feed</title>
1014 <link>[% base %]</link>
1015 <description>Recent posts</description>
1016 <language>en-us</language>
1017 <ttl>40</ttl>
1018 [% WHILE (post = posts.next) %]
1019 <item>
1020 <title>[% post.title %]</title>
1021 <description>[% post.formatted_teaser|html%]</description>
1022 <pubDate>[% post.pub_date %]</pubDate>
1023 <guid>[% post.full_uri %]</guid>
1024 <link>[% post.full_uri %]</link>
1025 <dc:creator>[% post.author.screenname %]</dc:creator>
1026 </item>
1027 [% END %]
1028 </channel>
1029 </rss>
1030
1031=head3 Using XML::Feed
1032
1033A more robust solution is to use XML::Feed, as was done in the Catalyst
1034Advent Calendar. Assuming we have a C<view> action that populates
1035'entries' with some DBIx::Class iterator, the code would look something
1036like this:
1037
1038 sub rss : Local {
1039 my ($self,$c) = @_;
1040 $c->forward('view'); # get the entries
1041
1042 my $feed = XML::Feed->new('RSS');
1043 $feed->title( $c->config->{name} . ' RSS Feed' );
1044 $feed->link( $c->req->base ); # link to the site.
1045 $feed->description('Catalyst advent calendar'); Some description
1046
1047 # Process the entries
1048 while( my $entry = $c->stash->{entries}->next ) {
1049 my $feed_entry = XML::Feed::Entry->new('RSS');
1050 $feed_entry->title($entry->title);
1051 $feed_entry->link( $c->uri_for($entry->link) );
1052 $feed_entry->issued( DateTime->from_epoch(epoch => $entry->created) );
1053 $feed->add_entry($feed_entry);
1054 }
1055 $c->res->body( $feed->as_xml );
1056 }
1057
1058A little more code in the controller, but with this approach you're
1059pretty sure to get something that validates.
1060
1061Note that for both of the above aproaches, you'll need to set the
1062content type like this:
1063
1064 $c->res->content_type('application/rss+xml');
1065
1066=head3 Final words
1067
1068You could generalize the second variant easily by replacing 'RSS' with a
1069variable, so you can generate Atom feeds with the same code.
1070
1071Now, go ahead and make RSS feeds for all your stuff. The world *needs*
1072updates on your goldfish!
1073
1074=head2 Forcing the browser to download content
1075
1076Sometimes you need your application to send content for download. For
1077example, you can generate a comma-separated values (CSV) file for your
1078users to download and import into their spreadsheet program.
1079
1080Let's say you have an C<Orders> controller which generates a CSV file
1081in the C<export> action (i.e., C<http://localhost:3000/orders/export>):
1082
1083 sub export : Local Args(0) {
1084 my ( $self, $c ) = @_;
1085
1086 # In a real application, you'd generate this from the database
1087 my $csv = "1,5.99\n2,29.99\n3,3.99\n";
1088
1089 $c->res->content_type('text/comma-separated-values');
1090 $c->res->body($csv);
1091 }
1092
1093Normally the browser uses the last part of the URI to generate a
1094filename for data it cannot display. In this case your browser would
1095likely ask you to save a file named C<export>.
1096
1097Luckily you can have the browser download the content with a specific
1098filename by setting the C<Content-Disposition> header:
1099
1100 my $filename = 'Important Orders.csv';
1101 $c->res->header('Content-Disposition', qq[attachment; filename="$filename"]);
1102
1103Note the use of quotes around the filename; this ensures that any
1104spaces in the filename are handled by the browser.
1105
1106Put this right before calling C<< $c->res->body >> and your browser
1107will download a file named C<Important Orders.csv> instead of
1108C<export>.
1109
1110You can also use this to have the browser download content which it
1111normally displays, such as JPEG images or even HTML. Just be sure to
1112set the appropriate content type and disposition.
1113
1114
1115=head1 Controllers
1116
1117Controllers are the main point of communication between the web server
1118and your application. Here we explore some aspects of how they work.
1119
1120=head2 Extending RenderView (formerly DefaultEnd)
1121
1122The recommended approach for an C<end> action is to use
1123L<Catalyst::Action::RenderView> (taking the place of
1124L<Catalyst::Plugin::DefaultEnd>), which does what you usually need.
1125However there are times when you need to add a bit to it, but don't want
1126to write your own C<end> action.
1127
1128You can extend it like this:
1129
1130To add something to an C<end> action that is called before rendering
1131(this is likely to be what you want), simply place it in the C<end>
1132method:
1133
1134 sub end : ActionClass('RenderView') {
1135 my ( $self, $c ) = @_;
1136 # do stuff here; the RenderView action is called afterwards
1137 }
1138
1139To add things to an C<end> action that are called I<after> rendering,
1140you can set it up like this:
1141
1142 sub render : ActionClass('RenderView') { }
1143
1144 sub end : Private {
1145 my ( $self, $c ) = @_;
1146 $c->forward('render');
1147 # do stuff here
1148 }
1149
1150=head2 Action Types
1151
1152=head3 Introduction
1153
c718cfb6 1154A Catalyst application is driven by one or more Controller
1155modules. There are a number of ways that Catalyst can decide which of
1156the methods in your controller modules it should call. Controller
1157methods are also called actions, because they determine how your
1158catalyst application should (re-)act to any given URL. When the
1159application is started up, catalyst looks at all your actions, and
1160decides which URLs they map to.
cb93c9d7 1161
1162=head3 Type attributes
1163
1164Each action is a normal method in your controller, except that it has an
1165L<attribute|http://search.cpan.org/~nwclark/perl-5.8.7/lib/attributes.pm>
1166attached. These can be one of several types.
1167
1168Assume our Controller module starts with the following package declaration:
1169
1170 package MyApp::Controller::Buckets;
1171
1172and we are running our application on localhost, port 3000 (the test
1173server default).
1174
1175=over 4
1176
1177=item Path
1178
1179A Path attribute also takes an argument, this can be either a relative
c718cfb6 1180or an absolute path. A relative path will be relative to the
1181controller namespace, an absolute path will represent an exact
1182matching URL.
cb93c9d7 1183
1184 sub my_handles : Path('handles') { .. }
1185
1186becomes
1187
1188 http://localhost:3000/buckets/handles
1189
1190and
1191
1192 sub my_handles : Path('/handles') { .. }
1193
1194becomes
1195
1196 http://localhost:3000/handles
1197
6daaedc0 1198See also: L<Catalyst::DispatchType::Path>
1199
cb93c9d7 1200=item Local
1201
c718cfb6 1202When using a Local attribute, no parameters are needed, instead, the
1203name of the action is matched in the URL. The namespaces created by
1204the name of the controller package is always part of the URL.
cb93c9d7 1205
1206 sub my_handles : Local { .. }
1207
1208becomes
1209
1210 http://localhost:3000/buckets/my_handles
1211
1212=item Global
1213
c718cfb6 1214A Global attribute is similar to a Local attribute, except that the
1215namespace of the controller is ignored, and matching starts at root.
cb93c9d7 1216
1217 sub my_handles : Global { .. }
1218
1219becomes
1220
1221 http://localhost:3000/my_handles
1222
1223=item Regex
1224
c718cfb6 1225By now you should have figured that a Regex attribute is just what it
1226sounds like. This one takes a regular expression, and matches starting
1227from root. These differ from the rest as they can match multiple URLs.
cb93c9d7 1228
1229 sub my_handles : Regex('^handles') { .. }
1230
1231matches
1232
1233 http://localhost:3000/handles
1234
1235and
1236
1237 http://localhost:3000/handles_and_other_parts
1238
1239etc.
1240
6daaedc0 1241See also: L<Catalyst::DispatchType::Regex>
1242
cb93c9d7 1243=item LocalRegex
1244
1245A LocalRegex is similar to a Regex, except it only matches below the current
1246controller namespace.
1247
1248 sub my_handles : LocalRegex(^handles') { .. }
1249
1250matches
1251
1252 http://localhost:3000/buckets/handles
1253
1254and
1255
1256 http://localhost:3000/buckets/handles_and_other_parts
1257
1258etc.
1259
6daaedc0 1260=item Chained
1261
1262See L<Catalyst::DispatchType::Chained> for a description of how the chained
1263dispatch type works.
1264
cb93c9d7 1265=item Private
1266
c718cfb6 1267Last but not least, there is the Private attribute, which allows you
1268to create your own internal actions, which can be forwarded to, but
1269won't be matched as URLs.
cb93c9d7 1270
1271 sub my_handles : Private { .. }
1272
1273becomes nothing at all..
1274
c718cfb6 1275Catalyst also predefines some special Private actions, which you can
1276override, these are:
cb93c9d7 1277
1278=over 4
1279
1280=item default
1281
c718cfb6 1282The default action will be called, if no other matching action is
1283found. If you don't have one of these in your namespace, or any sub
1284part of your namespace, you'll get an error page instead. If you want
1285to find out where it was the user was trying to go, you can look in
1286the request object using C<< $c->req->path >>.
cb93c9d7 1287
85d49fb6 1288 sub default :Path { .. }
cb93c9d7 1289
c718cfb6 1290works for all unknown URLs, in this controller namespace, or every one
1291if put directly into MyApp.pm.
cb93c9d7 1292
1293=item index
1294
c718cfb6 1295The index action is called when someone tries to visit the exact
1296namespace of your controller. If index, default and matching Path
1297actions are defined, then index will be used instead of default and
1298Path.
cb93c9d7 1299
85d49fb6 1300 sub index :Path :Args(0) { .. }
cb93c9d7 1301
1302becomes
1303
1304 http://localhost:3000/buckets
1305
1306=item begin
1307
c718cfb6 1308The begin action is called at the beginning of every request involving
1309this namespace directly, before other matching actions are called. It
1310can be used to set up variables/data for this particular part of your
1311app. A single begin action is called, its always the one most relevant
1312to the current namespace.
cb93c9d7 1313
1314 sub begin : Private { .. }
1315
1316is called once when
1317
1318 http://localhost:3000/bucket/(anything)?
1319
1320is visited.
1321
1322=item end
1323
c718cfb6 1324Like begin, this action is always called for the namespace it is in,
1325after every other action has finished. It is commonly used to forward
1326processing to the View component. A single end action is called, its
1327always the one most relevant to the current namespace.
cb93c9d7 1328
1329
1330 sub end : Private { .. }
1331
1332is called once after any actions when
1333
1334 http://localhost:3000/bucket/(anything)?
1335
1336is visited.
1337
1338=item auto
1339
c718cfb6 1340Lastly, the auto action is magic in that B<every> auto action in the
1341chain of paths up to and including the ending namespace, will be
1342called. (In contrast, only one of the begin/end/default actions will
1343be called, the relevant one).
cb93c9d7 1344
1345 package MyApp.pm;
1346 sub auto : Private { .. }
1347
1348and
1349
1350 sub auto : Private { .. }
1351
1352will both be called when visiting
1353
1354 http://localhost:3000/bucket/(anything)?
1355
1356=back
1357
1358=back
1359
1360=head3 A word of warning
1361
c718cfb6 1362Due to possible namespace conflicts with Plugins, it is advised to
1363only put the pre-defined Private actions in your main MyApp.pm file,
1364all others should go in a Controller module.
cb93c9d7 1365
1366=head3 More Information
1367
1368L<http://search.cpan.org/author/SRI/Catalyst-5.61/lib/Catalyst/Manual/Intro.pod>
1369
1370L<http://dev.catalyst.perl.org/wiki/FlowChart>
1371
6daaedc0 1372=head2 DRY Controllers with Chained actions.
1373
1374Imagine that you would like the following paths in your application:
1375
1376=over
1377
1378=item B</cd/<ID>/track/<ID>>
1379
1380Displays info on a particular track.
1381
1382In the case of a multi-volume CD, this is the track sequence.
1383
1384=item B</cd/<ID>/volume/<ID>/track/<ID>>
1385
1386Displays info on a track on a specific volume.
1387
1388=back
1389
1390Here is some example code, showing how to do this with chained controllers:
1391
1392 package CD::Controller;
1393 use base qw/Catalyst::Controller/;
1394
1395 sub root : Chained('/') PathPart('/cd') CaptureArgs(1) {
1396 my ($self, $c, $cd_id) = @_;
1397 $c->stash->{cd_id} = $cd_id;
1398 $c->stash->{cd} = $self->model('CD')->find_by_id($cd_id);
1399 }
1400
1401 sub trackinfo : Chained('track') PathPart('') Args(0) RenderView {
1402 my ($self, $c) = @_;
1403 }
1404
1405 package CD::Controller::ByTrackSeq;
1406 use base qw/CD::Controller/;
1407
1408 sub track : Chained('root') PathPart('track') CaptureArgs(1) {
1409 my ($self, $c, $track_seq) = @_;
1410 $c->stash->{track} = $self->stash->{cd}->find_track_by_seq($track_seq);
1411 }
1412
1413 package CD::Controller::ByTrackVolNo;
1414 use base qw/CD::Controller/;
1415
1416 sub volume : Chained('root') PathPart('volume') CaptureArgs(1) {
1417 my ($self, $c, $volume) = @_;
1418 $c->stash->{volume} = $volume;
1419 }
1420
1421 sub track : Chained('volume') PathPart('track') CaptureArgs(1) {
1422 my ($self, $c, $track_no) = @_;
1423 $c->stash->{track} = $self->stash->{cd}->find_track_by_vol_and_track_no(
1424 $c->stash->{volume}, $track_no
1425 );
1426 }
1427
1428Note that adding other actions (i.e. chain endpoints) which operate on a track
1429is simply a matter of adding a new sub to CD::Controller - no code is duplicated,
1430even though there are two different methods of looking up a track.
1431
1432This technique can be expanded as needed to fulfil your requirements - for example,
1433if you inherit the first action of a chain from a base class, then mixing in a
1434different base class can be used to duplicate an entire URL hieratchy at a different
1435point within your application.
1436
cb93c9d7 1437=head2 Component-based Subrequests
1438
1439See L<Catalyst::Plugin::SubRequest>.
1440
1441=head2 File uploads
1442
1443=head3 Single file upload with Catalyst
1444
1445To implement uploads in Catalyst, you need to have a HTML form similar to
1446this:
1447
1448 <form action="/upload" method="post" enctype="multipart/form-data">
1449 <input type="hidden" name="form_submit" value="yes">
1450 <input type="file" name="my_file">
1451 <input type="submit" value="Send">
1452 </form>
1453
1454It's very important not to forget C<enctype="multipart/form-data"> in
1455the form.
1456
1457Catalyst Controller module 'upload' action:
1458
1459 sub upload : Global {
1460 my ($self, $c) = @_;
1461
1462 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
1463
1464 if ( my $upload = $c->request->upload('my_file') ) {
1465
1466 my $filename = $upload->filename;
1467 my $target = "/tmp/upload/$filename";
1468
1469 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
1470 die( "Failed to copy '$filename' to '$target': $!" );
1471 }
1472 }
1473 }
1474
1475 $c->stash->{template} = 'file_upload.html';
1476 }
1477
1478=head3 Multiple file upload with Catalyst
1479
1480Code for uploading multiple files from one form needs a few changes:
1481
1482The form should have this basic structure:
1483
1484 <form action="/upload" method="post" enctype="multipart/form-data">
1485 <input type="hidden" name="form_submit" value="yes">
1486 <input type="file" name="file1" size="50"><br>
1487 <input type="file" name="file2" size="50"><br>
1488 <input type="file" name="file3" size="50"><br>
1489 <input type="submit" value="Send">
1490 </form>
1491
1492And in the controller:
1493
1494 sub upload : Local {
1495 my ($self, $c) = @_;
1496
1497 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
1498
1499 for my $field ( $c->req->upload ) {
1500
1501 my $upload = $c->req->upload($field);
1502 my $filename = $upload->filename;
1503 my $target = "/tmp/upload/$filename";
1504
1505 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
1506 die( "Failed to copy '$filename' to '$target': $!" );
1507 }
1508 }
1509 }
1510
1511 $c->stash->{template} = 'file_upload.html';
1512 }
1513
1514C<for my $field ($c-E<gt>req->upload)> loops automatically over all file
1515input fields and gets input names. After that is basic file saving code,
1516just like in single file upload.
1517
1518Notice: C<die>ing might not be what you want to do, when an error
1519occurs, but it works as an example. A better idea would be to store
1520error C<$!> in $c->stash->{error} and show a custom error template
1521displaying this message.
1522
1523For more information about uploads and usable methods look at
1524L<Catalyst::Request::Upload> and L<Catalyst::Request>.
1525
1526=head2 Forwarding with arguments
1527
1528Sometimes you want to pass along arguments when forwarding to another
1529action. As of version 5.30, arguments can be passed in the call to
1530C<forward>; in earlier versions, you can manually set the arguments in
1531the Catalyst Request object:
1532
1533 # version 5.30 and later:
1534 $c->forward('/wherever', [qw/arg1 arg2 arg3/]);
1535
1536 # pre-5.30
1537 $c->req->args([qw/arg1 arg2 arg3/]);
1538 $c->forward('/wherever');
1539
1540(See the L<Catalyst::Manual::Intro> Flow_Control section for more
1541information on passing arguments via C<forward>.)
1542
6daaedc0 1543=head2 Chained dispatch using base classes, and inner packages.
1544
1545 package MyApp::Controller::Base;
1546 use base qw/Catalyst::Controller/;
1547
1548 sub key1 : Chained('/')
cb93c9d7 1549
1550=head1 Deployment
1551
1552The recipes below describe aspects of the deployment process,
1553including web server engines and tips to improve application efficiency.
1554
1555=head2 mod_perl Deployment
1556
1557mod_perl is the best solution for many applications, but we'll list some pros
1558and cons so you can decide for yourself. The other production deployment
1559option is FastCGI, for which see below.
1560
1561=head3 Pros
1562
1563=head4 Speed
1564
1565mod_perl is very fast and your app will benefit from being loaded in memory
1566within each Apache process.
1567
1568=head4 Shared memory for multiple apps
1569
1570If you need to run several Catalyst apps on the same server, mod_perl will
1571share the memory for common modules.
1572
1573=head3 Cons
1574
1575=head4 Memory usage
1576
1577Since your application is fully loaded in memory, every Apache process will
1578be rather large. This means a large Apache process will be tied up while
1579serving static files, large files, or dealing with slow clients. For this
1580reason, it is best to run a two-tiered web architecture with a lightweight
1581frontend server passing dynamic requests to a large backend mod_perl
1582server.
1583
1584=head4 Reloading
1585
1586Any changes made to the core code of your app require a full Apache restart.
1587Catalyst does not support Apache::Reload or StatINC. This is another good
1588reason to run a frontend web server where you can set up an
1589C<ErrorDocument 502> page to report that your app is down for maintenance.
1590
1591=head4 Cannot run multiple versions of the same app
1592
1593It is not possible to run two different versions of the same application in
1594the same Apache instance because the namespaces will collide.
1595
1596=head4 Setup
1597
1598Now that we have that out of the way, let's talk about setting up mod_perl
1599to run a Catalyst app.
1600
1601=head4 1. Install Catalyst::Engine::Apache
1602
1603You should install the latest versions of both Catalyst and
1604Catalyst::Engine::Apache. The Apache engines were separated from the
1605Catalyst core in version 5.50 to allow for updates to the engine without
1606requiring a new Catalyst release.
1607
1608=head4 2. Install Apache with mod_perl
1609
1610Both Apache 1.3 and Apache 2 are supported, although Apache 2 is highly
1611recommended. With Apache 2, make sure you are using the prefork MPM and not
1612the worker MPM. The reason for this is that many Perl modules are not
1613thread-safe and may have problems running within the threaded worker
1614environment. Catalyst is thread-safe however, so if you know what you're
1615doing, you may be able to run using worker.
1616
1617In Debian, the following commands should get you going.
1618
1619 apt-get install apache2-mpm-prefork
1620 apt-get install libapache2-mod-perl2
1621
1622=head4 3. Configure your application
1623
1624Every Catalyst application will automagically become a mod_perl handler
1625when run within mod_perl. This makes the configuration extremely easy.
1626Here is a basic Apache 2 configuration.
1627
1628 PerlSwitches -I/var/www/MyApp/lib
1629 PerlModule MyApp
1630
1631 <Location />
1632 SetHandler modperl
1633 PerlResponseHandler MyApp
1634 </Location>
1635
1636The most important line here is C<PerlModule MyApp>. This causes mod_perl
1637to preload your entire application into shared memory, including all of your
1638controller, model, and view classes and configuration. If you have -Debug
1639mode enabled, you will see the startup output scroll by when you first
1640start Apache.
1641
1642For an example Apache 1.3 configuration, please see the documentation for
1643L<Catalyst::Engine::Apache::MP13>.
1644
1645=head3 Test It
1646
1647That's it, your app is now a full-fledged mod_perl application! Try it out
1648by going to http://your.server.com/.
1649
1650=head3 Other Options
1651
1652=head4 Non-root location
1653
1654You may not always want to run your app at the root of your server or virtual
1655host. In this case, it's a simple change to run at any non-root location
1656of your choice.
1657
1658 <Location /myapp>
1659 SetHandler modperl
1660 PerlResponseHandler MyApp
1661 </Location>
1662
1663When running this way, it is best to make use of the C<uri_for> method in
1664Catalyst for constructing correct links.
1665
1666=head4 Static file handling
1667
1668Static files can be served directly by Apache for a performance boost.
1669
1670 DocumentRoot /var/www/MyApp/root
1671 <Location /static>
1672 SetHandler default-handler
1673 </Location>
1674
1675This will let all files within root/static be handled directly by Apache. In
1676a two-tiered setup, the frontend server should handle static files.
1677The configuration to do this on the frontend will vary.
1678
3cca8359 1679The same is accomplished in lighttpd with the following snippet:
1680
1681 $HTTP["url"] !~ "^/(?:img/|static/|css/|favicon.ico$)" {
1682 fastcgi.server = (
1683 "" => (
1684 "MyApp" => (
1685 "socket" => "/tmp/myapp.socket",
1686 "check-local" => "disable",
1687 )
1688 )
1689 )
1690 }
1691
1692Which serves everything in the img, static, css directories
1693statically, as well as the favicon file.
1694
c1c35b01 1695Note the path of the application needs to be stated explicitly in the
1696web server configuration for both these recipes.
3cca8359 1697
cb93c9d7 1698=head2 Catalyst on shared hosting
1699
1700So, you want to put your Catalyst app out there for the whole world to
1701see, but you don't want to break the bank. There is an answer - if you
1702can get shared hosting with FastCGI and a shell, you can install your
1703Catalyst app in a local directory on your shared host. First, run
1704
1705 perl -MCPAN -e shell
1706
1707and go through the standard CPAN configuration process. Then exit out
1708without installing anything. Next, open your .bashrc and add
1709
1710 export PATH=$HOME/local/bin:$HOME/local/script:$PATH
1711 perlversion=`perl -v | grep 'built for' | awk '{print $4}' | sed -e 's/v//;'`
1712 export PERL5LIB=$HOME/local/share/perl/$perlversion:$HOME/local/lib/perl/$perlversion:$HOME/local/lib:$PERL5LIB
1713
1714and log out, then back in again (or run C<". .bashrc"> if you
1715prefer). Finally, edit C<.cpan/CPAN/MyConfig.pm> and add
1716
1717 'make_install_arg' => qq[SITEPREFIX=$ENV{HOME}/local],
1718 'makepl_arg' => qq[INSTALLDIRS=site install_base=$ENV{HOME}/local],
1719
1720Now you can install the modules you need using CPAN as normal; they
1721will be installed into your local directory, and perl will pick them
1722up. Finally, change directory into the root of your virtual host and
1723symlink your application's script directory in:
1724
1725 cd path/to/mydomain.com
1726 ln -s ~/lib/MyApp/script script
1727
1728And add the following lines to your .htaccess file (assuming the server
1729is setup to handle .pl as fcgi - you may need to rename the script to
1730myapp_fastcgi.fcgi and/or use a SetHandler directive):
1731
1732 RewriteEngine On
1733 RewriteCond %{REQUEST_URI} !^/?script/myapp_fastcgi.pl
1734 RewriteRule ^(.*)$ script/myapp_fastcgi.pl/$1 [PT,L]
1735
1736Now C<http://mydomain.com/> should now Just Work. Congratulations, now
1737you can tell your friends about your new website (or in our case, tell
1738the client it's time to pay the invoice :) )
1739
1740=head2 FastCGI Deployment
1741
1742FastCGI is a high-performance extension to CGI. It is suitable
1743for production environments.
1744
1745=head3 Pros
1746
1747=head4 Speed
1748
1749FastCGI performs equally as well as mod_perl. Don't let the 'CGI' fool you;
1750your app runs as multiple persistent processes ready to receive connections
1751from the web server.
1752
1753=head4 App Server
1754
1755When using external FastCGI servers, your application runs as a standalone
1756application server. It may be restarted independently from the web server.
1757This allows for a more robust environment and faster reload times when
1758pushing new app changes. The frontend server can even be configured to
1759display a friendly "down for maintenance" page while the application is
1760restarting.
1761
1762=head4 Load-balancing
1763
1764You can launch your application on multiple backend servers and allow the
1765frontend web server to load-balance between all of them. And of course, if
1766one goes down, your app continues to run fine.
1767
1768=head4 Multiple versions of the same app
1769
1770Each FastCGI application is a separate process, so you can run different
1771versions of the same app on a single server.
1772
1773=head4 Can run with threaded Apache
1774
1775Since your app is not running inside of Apache, the faster mpm_worker module
1776can be used without worrying about the thread safety of your application.
1777
1778=head3 Cons
1779
278f816d 1780You may have to disable mod_deflate. If you experience page hangs with
1781mod_fastcgi then remove deflate.load and deflate.conf from mods-enabled/
1782
cb93c9d7 1783=head4 More complex environment
1784
1785With FastCGI, there are more things to monitor and more processes running
1786than when using mod_perl.
1787
1788=head3 Setup
1789
1790=head4 1. Install Apache with mod_fastcgi
1791
1792mod_fastcgi for Apache is a third party module, and can be found at
1793L<http://www.fastcgi.com/>. It is also packaged in many distributions,
1794for example, libapache2-mod-fastcgi in Debian.
1795
1796=head4 2. Configure your application
1797
1798 # Serve static content directly
1799 DocumentRoot /var/www/MyApp/root
1800 Alias /static /var/www/MyApp/root/static
1801
1802 FastCgiServer /var/www/MyApp/script/myapp_fastcgi.pl -processes 3
1803 Alias /myapp/ /var/www/MyApp/script/myapp_fastcgi.pl/
1804
1805 # Or, run at the root
1806 Alias / /var/www/MyApp/script/myapp_fastcgi.pl/
1807
1808The above commands will launch 3 app processes and make the app available at
1809/myapp/
1810
1811=head3 Standalone server mode
1812
1813While not as easy as the previous method, running your app as an external
1814server gives you much more flexibility.
1815
1816First, launch your app as a standalone server listening on a socket.
1817
1818 script/myapp_fastcgi.pl -l /tmp/myapp.socket -n 5 -p /tmp/myapp.pid -d
1819
1820You can also listen on a TCP port if your web server is not on the same
1821machine.
1822
1823 script/myapp_fastcgi.pl -l :8080 -n 5 -p /tmp/myapp.pid -d
1824
1825You will probably want to write an init script to handle starting/stopping
1826of the app using the pid file.
1827
1828Now, we simply configure Apache to connect to the running server.
1829
1830 # 502 is a Bad Gateway error, and will occur if the backend server is down
1831 # This allows us to display a friendly static page that says "down for
1832 # maintenance"
1833 Alias /_errors /var/www/MyApp/root/error-pages
1834 ErrorDocument 502 /_errors/502.html
1835
31bdf270 1836 FastCgiExternalServer /tmp/myapp.fcgi -socket /tmp/myapp.socket
1837 Alias /myapp/ /tmp/myapp.fcgi/
cb93c9d7 1838
1839 # Or, run at the root
31bdf270 1840 Alias / /tmp/myapp.fcgi/
cb93c9d7 1841
1842=head3 More Info
1843
1844L<Catalyst::Engine::FastCGI>.
1845
1846=head2 Development server deployment
1847
1848The development server is a mini web server written in perl. If you
1849expect a low number of hits or you don't need mod_perl/FastCGI speed,
1850you could use the development server as the application server with a
ad2a47ab 1851lightweight proxy web server at the front. However, consider using
816fc503 1852L<Catalyst::Engine::HTTP::Prefork> for this kind of deployment instead, since
ad2a47ab 1853it can better handle multiple concurrent requests without forking, or can
1854prefork a set number of servers for improved performance.
cb93c9d7 1855
1856=head3 Pros
1857
1858As this is an application server setup, the pros are the same as
1859FastCGI (with the exception of speed).
1860It is also:
1861
1862=head4 Simple
1863
1864The development server is what you create your code on, so if it works
1865here, it should work in production!
1866
1867=head3 Cons
1868
1869=head4 Speed
1870
1871Not as fast as mod_perl or FastCGI. Needs to fork for each request
1872that comes in - make sure static files are served by the web server to
1873save forking.
1874
1875=head3 Setup
1876
1877=head4 Start up the development server
1878
ad2a47ab 1879 script/myapp_server.pl -p 8080 -k -f -pidfile=/tmp/myapp.pid
cb93c9d7 1880
1881You will probably want to write an init script to handle stop/starting
1882the app using the pid file.
1883
1884=head4 Configuring Apache
1885
1886Make sure mod_proxy is enabled and add:
1887
1888 # Serve static content directly
1889 DocumentRoot /var/www/MyApp/root
1890 Alias /static /var/www/MyApp/root/static
1891
1892 ProxyRequests Off
1893 <Proxy *>
1894 Order deny,allow
1895 Allow from all
1896 </Proxy>
816fc503 1897
1898 # Need to specifically stop these paths from being passed to proxy
1899 ProxyPass /static !
1900 ProxyPass /favicon.ico !
1901
cb93c9d7 1902 ProxyPass / http://localhost:8080/
1903 ProxyPassReverse / http://localhost:8080/
1904
816fc503 1905 # This is optional if you'd like to show a custom error page
1906 # if the proxy is not available
1907 ErrorDocument 502 /static/error_pages/http502.html
1908
cb93c9d7 1909You can wrap the above within a VirtualHost container if you want
1910different apps served on the same host.
1911
1912=head2 Quick deployment: Building PAR Packages
1913
1914You have an application running on your development box, but then you
1915have to quickly move it to another one for
1916demonstration/deployment/testing...
1917
1918PAR packages can save you from a lot of trouble here. They are usual Zip
1919files that contain a blib tree; you can even include all prereqs and a
1920perl interpreter by setting a few flags!
1921
1922=head3 Follow these few points to try it out!
1923
19241. Install Catalyst and PAR 0.89 (or later)
1925
1926 % perl -MCPAN -e 'install Catalyst'
1927 ...
1928 % perl -MCPAN -e 'install PAR'
1929 ...
1930
19312. Create a application
1932
1933 % catalyst.pl MyApp
1934 ...
1935 % cd MyApp
1936
1937Recent versions of Catalyst (5.62 and up) include
1938L<Module::Install::Catalyst>, which simplifies the process greatly. From the shell in your application directory:
1939
1940 % perl Makefile.PL
1941 % make catalyst_par
1942
1943Congratulations! Your package "myapp.par" is ready, the following
1944steps are just optional.
1945
19463. Test your PAR package with "parl" (no typo)
1947
1948 % parl myapp.par
1949 Usage:
1950 [parl] myapp[.par] [script] [arguments]
1951
1952 Examples:
1953 parl myapp.par myapp_server.pl -r
1954 myapp myapp_cgi.pl
1955
1956 Available scripts:
1957 myapp_cgi.pl
1958 myapp_create.pl
1959 myapp_fastcgi.pl
1960 myapp_server.pl
1961 myapp_test.pl
1962
1963 % parl myapp.par myapp_server.pl
1964 You can connect to your server at http://localhost:3000
1965
1966Yes, this nifty little starter application gets automatically included.
1967You can also use "catalyst_par_script('myapp_server.pl')" to set a
1968default script to execute.
1969
19706. Want to create a binary that includes the Perl interpreter?
1971
1972 % pp -o myapp myapp.par
1973 % ./myapp myapp_server.pl
1974 You can connect to your server at http://localhost:3000
1975
1976=head2 Serving static content
1977
1978Serving static content in Catalyst used to be somewhat tricky; the use
1979of L<Catalyst::Plugin::Static::Simple> makes everything much easier.
1980This plugin will automatically serve your static content during development,
1981but allows you to easily switch to Apache (or other server) in a
1982production environment.
1983
1984=head3 Introduction to Static::Simple
1985
1986Static::Simple is a plugin that will help to serve static content for your
1987application. By default, it will serve most types of files, excluding some
1988standard Template Toolkit extensions, out of your B<root> file directory. All
1989files are served by path, so if B<images/me.jpg> is requested, then
1990B<root/images/me.jpg> is found and served.
1991
1992=head3 Usage
1993
1994Using the plugin is as simple as setting your use line in MyApp.pm to include:
1995
b411df01 1996 use Catalyst qw/Static::Simple/;
cb93c9d7 1997
1998and already files will be served.
1999
2000=head3 Configuring
2001
2002Static content is best served from a single directory within your root
2003directory. Having many different directories such as C<root/css> and
2004C<root/images> requires more code to manage, because you must separately
2005identify each static directory--if you decide to add a C<root/js>
2006directory, you'll need to change your code to account for it. In
2007contrast, keeping all static directories as subdirectories of a main
2008C<root/static> directory makes things much easier to manage. Here's an
2009example of a typical root directory structure:
2010
2011 root/
2012 root/content.tt
2013 root/controller/stuff.tt
2014 root/header.tt
2015 root/static/
2016 root/static/css/main.css
2017 root/static/images/logo.jpg
2018 root/static/js/code.js
2019
2020
2021All static content lives under C<root/static>, with everything else being
2022Template Toolkit files.
2023
2024=over 4
2025
2026=item Include Path
2027
2028You may of course want to change the default locations, and make
2029Static::Simple look somewhere else, this is as easy as:
2030
2031 MyApp->config->{static}->{include_path} = [
2032 MyApp->config->{root},
2033 '/path/to/my/files'
2034 ];
2035
2036When you override include_path, it will not automatically append the
2037normal root path, so you need to add it yourself if you still want
2038it. These will be searched in order given, and the first matching file
2039served.
2040
2041=item Static directories
2042
2043If you want to force some directories to be only static, you can set
2044them using paths relative to the root dir, or regular expressions:
2045
2046 MyApp->config->{static}->{dirs} = [
2047 'static',
2048 qr/^(images|css)/,
2049 ];
2050
2051=item File extensions
2052
2053By default, the following extensions are not served (that is, they will
2054be processed by Catalyst): B<tmpl, tt, tt2, html, xhtml>. This list can
2055be replaced easily:
2056
2057 MyApp->config->{static}->{ignore_extensions} = [
2058 qw/tmpl tt tt2 html xhtml/
2059 ];
2060
2061=item Ignoring directories
2062
2063Entire directories can be ignored. If used with include_path,
2064directories relative to the include_path dirs will also be ignored:
2065
2066 MyApp->config->{static}->{ignore_dirs} = [ qw/tmpl css/ ];
2067
2068=back
2069
2070=head3 More information
2071
2072L<http://search.cpan.org/dist/Catalyst-Plugin-Static-Simple/>
2073
2074=head3 Serving manually with the Static plugin with HTTP::Daemon (myapp_server.pl)
2075
2076In some situations you might want to control things more directly,
2077using L<Catalyst::Plugin::Static>.
2078
2079In your main application class (MyApp.pm), load the plugin:
2080
b411df01 2081 use Catalyst qw/-Debug FormValidator Static OtherPlugin/;
cb93c9d7 2082
2083You will also need to make sure your end method does I<not> forward
2084static content to the view, perhaps like this:
2085
2086 sub end : Private {
2087 my ( $self, $c ) = @_;
2088
2089 $c->forward( 'MyApp::View::TT' )
2090 unless ( $c->res->body || !$c->stash->{template} );
2091 }
2092
2093This code will only forward to the view if a template has been
2094previously defined by a controller and if there is not already data in
2095C<$c-E<gt>res-E<gt>body>.
2096
2097Next, create a controller to handle requests for the /static path. Use
2098the Helper to save time. This command will create a stub controller as
2099C<lib/MyApp/Controller/Static.pm>.
2100
2101 $ script/myapp_create.pl controller Static
2102
2103Edit the file and add the following methods:
2104
2105 # serve all files under /static as static files
2106 sub default : Path('/static') {
2107 my ( $self, $c ) = @_;
2108
2109 # Optional, allow the browser to cache the content
2110 $c->res->headers->header( 'Cache-Control' => 'max-age=86400' );
2111
2112 $c->serve_static; # from Catalyst::Plugin::Static
2113 }
2114
2115 # also handle requests for /favicon.ico
2116 sub favicon : Path('/favicon.ico') {
2117 my ( $self, $c ) = @_;
2118
2119 $c->serve_static;
2120 }
2121
2122You can also define a different icon for the browser to use instead of
2123favicon.ico by using this in your HTML header:
2124
2125 <link rel="icon" href="/static/myapp.ico" type="image/x-icon" />
2126
2127=head3 Common problems with the Static plugin
2128
2129The Static plugin makes use of the C<shared-mime-info> package to
2130automatically determine MIME types. This package is notoriously
2131difficult to install, especially on win32 and OS X. For OS X the easiest
2132path might be to install Fink, then use C<apt-get install
2133shared-mime-info>. Restart the server, and everything should be fine.
2134
2135Make sure you are using the latest version (>= 0.16) for best
2136results. If you are having errors serving CSS files, or if they get
2137served as text/plain instead of text/css, you may have an outdated
2138shared-mime-info version. You may also wish to simply use the following
2139code in your Static controller:
2140
2141 if ($c->req->path =~ /css$/i) {
2142 $c->serve_static( "text/css" );
2143 } else {
2144 $c->serve_static;
2145 }
2146
2147=head3 Serving Static Files with Apache
2148
2149When using Apache, you can bypass Catalyst and any Static
2150plugins/controllers controller by intercepting requests for the
2151C<root/static> path at the server level. All that is required is to
2152define a DocumentRoot and add a separate Location block for your static
2153content. Here is a complete config for this application under mod_perl
21541.x:
2155
2156 <Perl>
2157 use lib qw(/var/www/MyApp/lib);
2158 </Perl>
2159 PerlModule MyApp
2160
2161 <VirtualHost *>
2162 ServerName myapp.example.com
2163 DocumentRoot /var/www/MyApp/root
2164 <Location />
2165 SetHandler perl-script
2166 PerlHandler MyApp
2167 </Location>
2168 <LocationMatch "/(static|favicon.ico)">
2169 SetHandler default-handler
2170 </LocationMatch>
2171 </VirtualHost>
2172
2173And here's a simpler example that'll get you started:
2174
2175 Alias /static/ "/my/static/files/"
2176 <Location "/static">
2177 SetHandler none
2178 </Location>
2179
2180=head2 Caching
2181
2182Catalyst makes it easy to employ several different types of caching to
2183speed up your applications.
2184
2185=head3 Cache Plugins
2186
2187There are three wrapper plugins around common CPAN cache modules:
2188Cache::FastMmap, Cache::FileCache, and Cache::Memcached. These can be
2189used to cache the result of slow operations.
2190
ca7528df 2191The Catalyst Advent Calendar uses the FileCache plugin to cache the
cb93c9d7 2192rendered XHTML version of the source POD document. This is an ideal
ca7528df 2193application for a cache because the source document changes
2194infrequently but may be viewed many times.
cb93c9d7 2195
b411df01 2196 use Catalyst qw/Cache::FileCache/;
cb93c9d7 2197
2198 ...
2199
2200 use File::stat;
2201 sub render_pod : Local {
2202 my ( self, $c ) = @_;
2203
2204 # the cache is keyed on the filename and the modification time
2205 # to check for updates to the file.
2206 my $file = $c->path_to( 'root', '2005', '11.pod' );
2207 my $mtime = ( stat $file )->mtime;
2208
2209 my $cached_pod = $c->cache->get("$file $mtime");
2210 if ( !$cached_pod ) {
2211 $cached_pod = do_slow_pod_rendering();
2212 # cache the result for 12 hours
2213 $c->cache->set( "$file $mtime", $cached_pod, '12h' );
2214 }
2215 $c->stash->{pod} = $cached_pod;
2216 }
2217
2218We could actually cache the result forever, but using a value such as 12 hours
2219allows old entries to be automatically expired when they are no longer needed.
2220
2221=head3 Page Caching
2222
2223Another method of caching is to cache the entire HTML page. While this is
2224traditionally handled by a front-end proxy server like Squid, the Catalyst
2225PageCache plugin makes it trivial to cache the entire output from
2226frequently-used or slow actions.
2227
2228Many sites have a busy content-filled front page that might look something
2229like this. It probably takes a while to process, and will do the exact same
2230thing for every single user who views the page.
2231
2232 sub front_page : Path('/') {
2233 my ( $self, $c ) = @_;
2234
2235 $c->forward( 'get_news_articles' );
2236 $c->forward( 'build_lots_of_boxes' );
2237 $c->forward( 'more_slow_stuff' );
2238
2239 $c->stash->{template} = 'index.tt';
2240 }
2241
2242We can add the PageCache plugin to speed things up.
2243
b411df01 2244 use Catalyst qw/Cache::FileCache PageCache/;
cb93c9d7 2245
2246 sub front_page : Path ('/') {
2247 my ( $self, $c ) = @_;
2248
2249 $c->cache_page( 300 );
2250
2251 # same processing as above
2252 }
2253
2254Now the entire output of the front page, from <html> to </html>, will be
2255cached for 5 minutes. After 5 minutes, the next request will rebuild the
2256page and it will be re-cached.
2257
2258Note that the page cache is keyed on the page URI plus all parameters, so
2259requests for / and /?foo=bar will result in different cache items. Also,
2260only GET requests will be cached by the plugin.
2261
2262You can even get that front-end Squid proxy to help out by enabling HTTP
2263headers for the cached page.
2264
2265 MyApp->config->{page_cache}->{set_http_headers} = 1;
2266
2267This would now set the following headers so proxies and browsers may cache
2268the content themselves.
2269
2270 Cache-Control: max-age=($expire_time - time)
2271 Expires: $expire_time
2272 Last-Modified: $cache_created_time
2273
2274=head3 Template Caching
2275
2276Template Toolkit provides support for caching compiled versions of your
2277templates. To enable this in Catalyst, use the following configuration.
2278TT will cache compiled templates keyed on the file mtime, so changes will
2279still be automatically detected.
2280
2281 package MyApp::View::TT;
2282
2283 use strict;
2284 use warnings;
2285 use base 'Catalyst::View::TT';
2286
2287 __PACKAGE__->config(
2288 COMPILE_DIR => '/tmp/template_cache',
2289 );
2290
2291 1;
2292
2293=head3 More Info
2294
2295See the documentation for each cache plugin for more details and other
2296available configuration options.
2297
2298L<Catalyst::Plugin::Cache::FastMmap>
2299L<Catalyst::Plugin::Cache::FileCache>
2300L<Catalyst::Plugin::Cache::Memcached>
2301L<Catalyst::Plugin::PageCache>
2302L<http://search.cpan.org/dist/Template-Toolkit/lib/Template/Manual/Config.pod#Caching_and_Compiling_Options>
2303
2304=head1 Testing
2305
2306Testing is an integral part of the web application development
2307process. Tests make multi developer teams easier to coordinate, and
2308they help ensure that there are no nasty surprises after upgrades or
2309alterations.
2310
2311=head2 Testing
2312
2313Catalyst provides a convenient way of testing your application during
2314development and before deployment in a real environment.
2315
2316C<Catalyst::Test> makes it possible to run the same tests both locally
2317(without an external daemon) and against a remote server via HTTP.
2318
2319=head3 Tests
2320
2321Let's examine a skeleton application's C<t/> directory:
2322
2323 mundus:~/MyApp chansen$ ls -l t/
2324 total 24
2325 -rw-r--r-- 1 chansen chansen 95 18 Dec 20:50 01app.t
2326 -rw-r--r-- 1 chansen chansen 190 18 Dec 20:50 02pod.t
2327 -rw-r--r-- 1 chansen chansen 213 18 Dec 20:50 03podcoverage.t
2328
2329=over 4
2330
2331=item C<01app.t>
2332
2333Verifies that the application loads, compiles, and returns a successful
2334response.
2335
2336=item C<02pod.t>
2337
2338Verifies that all POD is free from errors. Only executed if the C<TEST_POD>
2339environment variable is true.
2340
2341=item C<03podcoverage.t>
2342
2343Verifies that all methods/functions have POD coverage. Only executed if the
2344C<TEST_POD> environment variable is true.
2345
2346=back
2347
2348=head3 Creating tests
2349
2350 mundus:~/MyApp chansen$ cat t/01app.t | perl -ne 'printf( "%2d %s", $., $_ )'
2351 1 use Test::More tests => 2;
2352 2 use_ok( Catalyst::Test, 'MyApp' );
2353 3
2354 4 ok( request('/')->is_success );
2355
2356The first line declares how many tests we are going to run, in this case
2357two. The second line tests and loads our application in test mode. The
2358fourth line verifies that our application returns a successful response.
2359
2360C<Catalyst::Test> exports two functions, C<request> and C<get>. Each can
2361take three different arguments:
2362
2363=over 4
2364
2365=item A string which is a relative or absolute URI.
2366
2367 request('/my/path');
2368 request('http://www.host.com/my/path');
2369
2370=item An instance of C<URI>.
2371
2372 request( URI->new('http://www.host.com/my/path') );
2373
2374=item An instance of C<HTTP::Request>.
2375
2376 request( HTTP::Request->new( GET => 'http://www.host.com/my/path') );
2377
2378=back
2379
2380C<request> returns an instance of C<HTTP::Response> and C<get> returns the
2381content (body) of the response.
2382
2383=head3 Running tests locally
2384
2385 mundus:~/MyApp chansen$ CATALYST_DEBUG=0 TEST_POD=1 prove --lib lib/ t/
2386 t/01app............ok
2387 t/02pod............ok
2388 t/03podcoverage....ok
2389 All tests successful.
2390 Files=3, Tests=4, 2 wallclock secs ( 1.60 cusr + 0.36 csys = 1.96 CPU)
2391
2392C<CATALYST_DEBUG=0> ensures that debugging is off; if it's enabled you
2393will see debug logs between tests.
2394
2395C<TEST_POD=1> enables POD checking and coverage.
2396
2397C<prove> A command-line tool that makes it easy to run tests. You can
2398find out more about it from the links below.
2399
2400=head3 Running tests remotely
2401
2402 mundus:~/MyApp chansen$ CATALYST_SERVER=http://localhost:3000/ prove --lib lib/ t/01app.t
2403 t/01app....ok
2404 All tests successful.
2405 Files=1, Tests=2, 0 wallclock secs ( 0.40 cusr + 0.01 csys = 0.41 CPU)
2406
2407C<CATALYST_SERVER=http://localhost:3000/> is the absolute deployment URI of
2408your application. In C<CGI> or C<FastCGI> it should be the host and path
2409to the script.
2410
2411=head3 C<Test::WWW::Mechanize> and Catalyst
2412
2413Be sure to check out C<Test::WWW::Mechanize::Catalyst>. It makes it easy to
2414test HTML, forms and links. A short example of usage:
2415
2416 use Test::More tests => 6;
2417 use_ok( Test::WWW::Mechanize::Catalyst, 'MyApp' );
2418
2419 my $mech = Test::WWW::Mechanize::Catalyst->new;
2420 $mech->get_ok("http://localhost/", 'Got index page');
2421 $mech->title_like( qr/^MyApp on Catalyst/, 'Got right index title' );
2422 ok( $mech->find_link( text_regex => qr/^Wiki/i ), 'Found link to Wiki' );
2423 ok( $mech->find_link( text_regex => qr/^Mailing-List/i ), 'Found link to Mailing-List' );
2424 ok( $mech->find_link( text_regex => qr/^IRC channel/i ), 'Found link to IRC channel' );
2425
2426=head3 Further Reading
2427
2428=over 4
2429
2430=item Catalyst::Test
2431
2432L<http://search.cpan.org/dist/Catalyst/lib/Catalyst/Test.pm>
2433
2434=item Test::WWW::Mechanize::Catalyst
2435
2436L<http://search.cpan.org/dist/Test-WWW-Mechanize-Catalyst/lib/Test/WWW/Mechanize/Catalyst.pm>
2437
2438=item Test::WWW::Mechanize
2439
2440L<http://search.cpan.org/dist/Test-WWW-Mechanize/Mechanize.pm>
2441
2442=item WWW::Mechanize
2443
2444L<http://search.cpan.org/dist/WWW-Mechanize/lib/WWW/Mechanize.pm>
2445
2446=item LWP::UserAgent
2447
2448L<http://search.cpan.org/dist/libwww-perl/lib/LWP/UserAgent.pm>
2449
2450=item HTML::Form
2451
2452L<http://search.cpan.org/dist/libwww-perl/lib/HTML/Form.pm>
2453
2454=item HTTP::Message
2455
2456L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Message.pm>
2457
2458=item HTTP::Request
2459
2460L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Request.pm>
2461
2462=item HTTP::Request::Common
2463
2464L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Request/Common.pm>
2465
2466=item HTTP::Response
2467
2468L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Response.pm>
2469
2470=item HTTP::Status
2471
2472L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Status.pm>
2473
2474=item URI
2475
2476L<http://search.cpan.org/dist/URI/URI.pm>
2477
2478=item Test::More
2479
2480L<http://search.cpan.org/dist/Test-Simple/lib/Test/More.pm>
2481
2482=item Test::Pod
2483
2484L<http://search.cpan.org/dist/Test-Pod/Pod.pm>
2485
2486=item Test::Pod::Coverage
2487
2488L<http://search.cpan.org/dist/Test-Pod-Coverage/Coverage.pm>
2489
2490=item prove (Test::Harness)
2491
2492L<http://search.cpan.org/dist/Test-Harness/bin/prove>
2493
2494=back
2495
2496=head3 More Information
2497
2498L<http://search.cpan.org/perldoc?Catalyst::Plugin::Authorization::Roles>
2499L<http://search.cpan.org/perldoc?Catalyst::Plugin::Authorization::ACL>
2500
2501=head1 AUTHORS
2502
2503Sebastian Riedel C<sri@oook.de>
2504
2505Danijel Milicevic C<me@danijel.de>
2506
2507Viljo Marrandi C<vilts@yahoo.com>
2508
2509Marcus Ramberg C<mramberg@cpan.org>
2510
2511Jesse Sheidlower C<jester@panix.com>
2512
2513Andy Grundman C<andy@hybridized.org>
2514
2515Chisel Wright C<pause@herlpacker.co.uk>
2516
2517Will Hawes C<info@whawes.co.uk>
2518
2519Gavin Henry C<ghenry@perl.me.uk>
2520
2521Kieren Diment C<kd@totaldatasolution.com>
2522
2523=head1 COPYRIGHT
2524
2525This document is free, you can redistribute it and/or modify it
2526under the same terms as Perl itself.
2527