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