rollback to use Catalyst qw/@plugins/
[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
1198=item Local
1199
c718cfb6 1200When using a Local attribute, no parameters are needed, instead, the
1201name of the action is matched in the URL. The namespaces created by
1202the name of the controller package is always part of the URL.
cb93c9d7 1203
1204 sub my_handles : Local { .. }
1205
1206becomes
1207
1208 http://localhost:3000/buckets/my_handles
1209
1210=item Global
1211
c718cfb6 1212A Global attribute is similar to a Local attribute, except that the
1213namespace of the controller is ignored, and matching starts at root.
cb93c9d7 1214
1215 sub my_handles : Global { .. }
1216
1217becomes
1218
1219 http://localhost:3000/my_handles
1220
1221=item Regex
1222
c718cfb6 1223By now you should have figured that a Regex attribute is just what it
1224sounds like. This one takes a regular expression, and matches starting
1225from root. These differ from the rest as they can match multiple URLs.
cb93c9d7 1226
1227 sub my_handles : Regex('^handles') { .. }
1228
1229matches
1230
1231 http://localhost:3000/handles
1232
1233and
1234
1235 http://localhost:3000/handles_and_other_parts
1236
1237etc.
1238
1239=item LocalRegex
1240
1241A LocalRegex is similar to a Regex, except it only matches below the current
1242controller namespace.
1243
1244 sub my_handles : LocalRegex(^handles') { .. }
1245
1246matches
1247
1248 http://localhost:3000/buckets/handles
1249
1250and
1251
1252 http://localhost:3000/buckets/handles_and_other_parts
1253
1254etc.
1255
1256=item Private
1257
c718cfb6 1258Last but not least, there is the Private attribute, which allows you
1259to create your own internal actions, which can be forwarded to, but
1260won't be matched as URLs.
cb93c9d7 1261
1262 sub my_handles : Private { .. }
1263
1264becomes nothing at all..
1265
c718cfb6 1266Catalyst also predefines some special Private actions, which you can
1267override, these are:
cb93c9d7 1268
1269=over 4
1270
1271=item default
1272
c718cfb6 1273The default action will be called, if no other matching action is
1274found. If you don't have one of these in your namespace, or any sub
1275part of your namespace, you'll get an error page instead. If you want
1276to find out where it was the user was trying to go, you can look in
1277the request object using C<< $c->req->path >>.
cb93c9d7 1278
85d49fb6 1279 sub default :Path { .. }
cb93c9d7 1280
c718cfb6 1281works for all unknown URLs, in this controller namespace, or every one
1282if put directly into MyApp.pm.
cb93c9d7 1283
1284=item index
1285
c718cfb6 1286The index action is called when someone tries to visit the exact
1287namespace of your controller. If index, default and matching Path
1288actions are defined, then index will be used instead of default and
1289Path.
cb93c9d7 1290
85d49fb6 1291 sub index :Path :Args(0) { .. }
cb93c9d7 1292
1293becomes
1294
1295 http://localhost:3000/buckets
1296
1297=item begin
1298
c718cfb6 1299The begin action is called at the beginning of every request involving
1300this namespace directly, before other matching actions are called. It
1301can be used to set up variables/data for this particular part of your
1302app. A single begin action is called, its always the one most relevant
1303to the current namespace.
cb93c9d7 1304
1305 sub begin : Private { .. }
1306
1307is called once when
1308
1309 http://localhost:3000/bucket/(anything)?
1310
1311is visited.
1312
1313=item end
1314
c718cfb6 1315Like begin, this action is always called for the namespace it is in,
1316after every other action has finished. It is commonly used to forward
1317processing to the View component. A single end action is called, its
1318always the one most relevant to the current namespace.
cb93c9d7 1319
1320
1321 sub end : Private { .. }
1322
1323is called once after any actions when
1324
1325 http://localhost:3000/bucket/(anything)?
1326
1327is visited.
1328
1329=item auto
1330
c718cfb6 1331Lastly, the auto action is magic in that B<every> auto action in the
1332chain of paths up to and including the ending namespace, will be
1333called. (In contrast, only one of the begin/end/default actions will
1334be called, the relevant one).
cb93c9d7 1335
1336 package MyApp.pm;
1337 sub auto : Private { .. }
1338
1339and
1340
1341 sub auto : Private { .. }
1342
1343will both be called when visiting
1344
1345 http://localhost:3000/bucket/(anything)?
1346
1347=back
1348
1349=back
1350
1351=head3 A word of warning
1352
c718cfb6 1353Due to possible namespace conflicts with Plugins, it is advised to
1354only put the pre-defined Private actions in your main MyApp.pm file,
1355all others should go in a Controller module.
cb93c9d7 1356
1357=head3 More Information
1358
1359L<http://search.cpan.org/author/SRI/Catalyst-5.61/lib/Catalyst/Manual/Intro.pod>
1360
1361L<http://dev.catalyst.perl.org/wiki/FlowChart>
1362
1363=head2 Component-based Subrequests
1364
1365See L<Catalyst::Plugin::SubRequest>.
1366
1367=head2 File uploads
1368
1369=head3 Single file upload with Catalyst
1370
1371To implement uploads in Catalyst, you need to have a HTML form similar to
1372this:
1373
1374 <form action="/upload" method="post" enctype="multipart/form-data">
1375 <input type="hidden" name="form_submit" value="yes">
1376 <input type="file" name="my_file">
1377 <input type="submit" value="Send">
1378 </form>
1379
1380It's very important not to forget C<enctype="multipart/form-data"> in
1381the form.
1382
1383Catalyst Controller module 'upload' action:
1384
1385 sub upload : Global {
1386 my ($self, $c) = @_;
1387
1388 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
1389
1390 if ( my $upload = $c->request->upload('my_file') ) {
1391
1392 my $filename = $upload->filename;
1393 my $target = "/tmp/upload/$filename";
1394
1395 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
1396 die( "Failed to copy '$filename' to '$target': $!" );
1397 }
1398 }
1399 }
1400
1401 $c->stash->{template} = 'file_upload.html';
1402 }
1403
1404=head3 Multiple file upload with Catalyst
1405
1406Code for uploading multiple files from one form needs a few changes:
1407
1408The form should have this basic structure:
1409
1410 <form action="/upload" method="post" enctype="multipart/form-data">
1411 <input type="hidden" name="form_submit" value="yes">
1412 <input type="file" name="file1" size="50"><br>
1413 <input type="file" name="file2" size="50"><br>
1414 <input type="file" name="file3" size="50"><br>
1415 <input type="submit" value="Send">
1416 </form>
1417
1418And in the controller:
1419
1420 sub upload : Local {
1421 my ($self, $c) = @_;
1422
1423 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
1424
1425 for my $field ( $c->req->upload ) {
1426
1427 my $upload = $c->req->upload($field);
1428 my $filename = $upload->filename;
1429 my $target = "/tmp/upload/$filename";
1430
1431 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
1432 die( "Failed to copy '$filename' to '$target': $!" );
1433 }
1434 }
1435 }
1436
1437 $c->stash->{template} = 'file_upload.html';
1438 }
1439
1440C<for my $field ($c-E<gt>req->upload)> loops automatically over all file
1441input fields and gets input names. After that is basic file saving code,
1442just like in single file upload.
1443
1444Notice: C<die>ing might not be what you want to do, when an error
1445occurs, but it works as an example. A better idea would be to store
1446error C<$!> in $c->stash->{error} and show a custom error template
1447displaying this message.
1448
1449For more information about uploads and usable methods look at
1450L<Catalyst::Request::Upload> and L<Catalyst::Request>.
1451
1452=head2 Forwarding with arguments
1453
1454Sometimes you want to pass along arguments when forwarding to another
1455action. As of version 5.30, arguments can be passed in the call to
1456C<forward>; in earlier versions, you can manually set the arguments in
1457the Catalyst Request object:
1458
1459 # version 5.30 and later:
1460 $c->forward('/wherever', [qw/arg1 arg2 arg3/]);
1461
1462 # pre-5.30
1463 $c->req->args([qw/arg1 arg2 arg3/]);
1464 $c->forward('/wherever');
1465
1466(See the L<Catalyst::Manual::Intro> Flow_Control section for more
1467information on passing arguments via C<forward>.)
1468
1469
1470=head1 Deployment
1471
1472The recipes below describe aspects of the deployment process,
1473including web server engines and tips to improve application efficiency.
1474
1475=head2 mod_perl Deployment
1476
1477mod_perl is the best solution for many applications, but we'll list some pros
1478and cons so you can decide for yourself. The other production deployment
1479option is FastCGI, for which see below.
1480
1481=head3 Pros
1482
1483=head4 Speed
1484
1485mod_perl is very fast and your app will benefit from being loaded in memory
1486within each Apache process.
1487
1488=head4 Shared memory for multiple apps
1489
1490If you need to run several Catalyst apps on the same server, mod_perl will
1491share the memory for common modules.
1492
1493=head3 Cons
1494
1495=head4 Memory usage
1496
1497Since your application is fully loaded in memory, every Apache process will
1498be rather large. This means a large Apache process will be tied up while
1499serving static files, large files, or dealing with slow clients. For this
1500reason, it is best to run a two-tiered web architecture with a lightweight
1501frontend server passing dynamic requests to a large backend mod_perl
1502server.
1503
1504=head4 Reloading
1505
1506Any changes made to the core code of your app require a full Apache restart.
1507Catalyst does not support Apache::Reload or StatINC. This is another good
1508reason to run a frontend web server where you can set up an
1509C<ErrorDocument 502> page to report that your app is down for maintenance.
1510
1511=head4 Cannot run multiple versions of the same app
1512
1513It is not possible to run two different versions of the same application in
1514the same Apache instance because the namespaces will collide.
1515
1516=head4 Setup
1517
1518Now that we have that out of the way, let's talk about setting up mod_perl
1519to run a Catalyst app.
1520
1521=head4 1. Install Catalyst::Engine::Apache
1522
1523You should install the latest versions of both Catalyst and
1524Catalyst::Engine::Apache. The Apache engines were separated from the
1525Catalyst core in version 5.50 to allow for updates to the engine without
1526requiring a new Catalyst release.
1527
1528=head4 2. Install Apache with mod_perl
1529
1530Both Apache 1.3 and Apache 2 are supported, although Apache 2 is highly
1531recommended. With Apache 2, make sure you are using the prefork MPM and not
1532the worker MPM. The reason for this is that many Perl modules are not
1533thread-safe and may have problems running within the threaded worker
1534environment. Catalyst is thread-safe however, so if you know what you're
1535doing, you may be able to run using worker.
1536
1537In Debian, the following commands should get you going.
1538
1539 apt-get install apache2-mpm-prefork
1540 apt-get install libapache2-mod-perl2
1541
1542=head4 3. Configure your application
1543
1544Every Catalyst application will automagically become a mod_perl handler
1545when run within mod_perl. This makes the configuration extremely easy.
1546Here is a basic Apache 2 configuration.
1547
1548 PerlSwitches -I/var/www/MyApp/lib
1549 PerlModule MyApp
1550
1551 <Location />
1552 SetHandler modperl
1553 PerlResponseHandler MyApp
1554 </Location>
1555
1556The most important line here is C<PerlModule MyApp>. This causes mod_perl
1557to preload your entire application into shared memory, including all of your
1558controller, model, and view classes and configuration. If you have -Debug
1559mode enabled, you will see the startup output scroll by when you first
1560start Apache.
1561
1562For an example Apache 1.3 configuration, please see the documentation for
1563L<Catalyst::Engine::Apache::MP13>.
1564
1565=head3 Test It
1566
1567That's it, your app is now a full-fledged mod_perl application! Try it out
1568by going to http://your.server.com/.
1569
1570=head3 Other Options
1571
1572=head4 Non-root location
1573
1574You may not always want to run your app at the root of your server or virtual
1575host. In this case, it's a simple change to run at any non-root location
1576of your choice.
1577
1578 <Location /myapp>
1579 SetHandler modperl
1580 PerlResponseHandler MyApp
1581 </Location>
1582
1583When running this way, it is best to make use of the C<uri_for> method in
1584Catalyst for constructing correct links.
1585
1586=head4 Static file handling
1587
1588Static files can be served directly by Apache for a performance boost.
1589
1590 DocumentRoot /var/www/MyApp/root
1591 <Location /static>
1592 SetHandler default-handler
1593 </Location>
1594
1595This will let all files within root/static be handled directly by Apache. In
1596a two-tiered setup, the frontend server should handle static files.
1597The configuration to do this on the frontend will vary.
1598
3cca8359 1599The same is accomplished in lighttpd with the following snippet:
1600
1601 $HTTP["url"] !~ "^/(?:img/|static/|css/|favicon.ico$)" {
1602 fastcgi.server = (
1603 "" => (
1604 "MyApp" => (
1605 "socket" => "/tmp/myapp.socket",
1606 "check-local" => "disable",
1607 )
1608 )
1609 )
1610 }
1611
1612Which serves everything in the img, static, css directories
1613statically, as well as the favicon file.
1614
c1c35b01 1615Note the path of the application needs to be stated explicitly in the
1616web server configuration for both these recipes.
3cca8359 1617
cb93c9d7 1618=head2 Catalyst on shared hosting
1619
1620So, you want to put your Catalyst app out there for the whole world to
1621see, but you don't want to break the bank. There is an answer - if you
1622can get shared hosting with FastCGI and a shell, you can install your
1623Catalyst app in a local directory on your shared host. First, run
1624
1625 perl -MCPAN -e shell
1626
1627and go through the standard CPAN configuration process. Then exit out
1628without installing anything. Next, open your .bashrc and add
1629
1630 export PATH=$HOME/local/bin:$HOME/local/script:$PATH
1631 perlversion=`perl -v | grep 'built for' | awk '{print $4}' | sed -e 's/v//;'`
1632 export PERL5LIB=$HOME/local/share/perl/$perlversion:$HOME/local/lib/perl/$perlversion:$HOME/local/lib:$PERL5LIB
1633
1634and log out, then back in again (or run C<". .bashrc"> if you
1635prefer). Finally, edit C<.cpan/CPAN/MyConfig.pm> and add
1636
1637 'make_install_arg' => qq[SITEPREFIX=$ENV{HOME}/local],
1638 'makepl_arg' => qq[INSTALLDIRS=site install_base=$ENV{HOME}/local],
1639
1640Now you can install the modules you need using CPAN as normal; they
1641will be installed into your local directory, and perl will pick them
1642up. Finally, change directory into the root of your virtual host and
1643symlink your application's script directory in:
1644
1645 cd path/to/mydomain.com
1646 ln -s ~/lib/MyApp/script script
1647
1648And add the following lines to your .htaccess file (assuming the server
1649is setup to handle .pl as fcgi - you may need to rename the script to
1650myapp_fastcgi.fcgi and/or use a SetHandler directive):
1651
1652 RewriteEngine On
1653 RewriteCond %{REQUEST_URI} !^/?script/myapp_fastcgi.pl
1654 RewriteRule ^(.*)$ script/myapp_fastcgi.pl/$1 [PT,L]
1655
1656Now C<http://mydomain.com/> should now Just Work. Congratulations, now
1657you can tell your friends about your new website (or in our case, tell
1658the client it's time to pay the invoice :) )
1659
1660=head2 FastCGI Deployment
1661
1662FastCGI is a high-performance extension to CGI. It is suitable
1663for production environments.
1664
1665=head3 Pros
1666
1667=head4 Speed
1668
1669FastCGI performs equally as well as mod_perl. Don't let the 'CGI' fool you;
1670your app runs as multiple persistent processes ready to receive connections
1671from the web server.
1672
1673=head4 App Server
1674
1675When using external FastCGI servers, your application runs as a standalone
1676application server. It may be restarted independently from the web server.
1677This allows for a more robust environment and faster reload times when
1678pushing new app changes. The frontend server can even be configured to
1679display a friendly "down for maintenance" page while the application is
1680restarting.
1681
1682=head4 Load-balancing
1683
1684You can launch your application on multiple backend servers and allow the
1685frontend web server to load-balance between all of them. And of course, if
1686one goes down, your app continues to run fine.
1687
1688=head4 Multiple versions of the same app
1689
1690Each FastCGI application is a separate process, so you can run different
1691versions of the same app on a single server.
1692
1693=head4 Can run with threaded Apache
1694
1695Since your app is not running inside of Apache, the faster mpm_worker module
1696can be used without worrying about the thread safety of your application.
1697
1698=head3 Cons
1699
1700=head4 More complex environment
1701
1702With FastCGI, there are more things to monitor and more processes running
1703than when using mod_perl.
1704
1705=head3 Setup
1706
1707=head4 1. Install Apache with mod_fastcgi
1708
1709mod_fastcgi for Apache is a third party module, and can be found at
1710L<http://www.fastcgi.com/>. It is also packaged in many distributions,
1711for example, libapache2-mod-fastcgi in Debian.
1712
1713=head4 2. Configure your application
1714
1715 # Serve static content directly
1716 DocumentRoot /var/www/MyApp/root
1717 Alias /static /var/www/MyApp/root/static
1718
1719 FastCgiServer /var/www/MyApp/script/myapp_fastcgi.pl -processes 3
1720 Alias /myapp/ /var/www/MyApp/script/myapp_fastcgi.pl/
1721
1722 # Or, run at the root
1723 Alias / /var/www/MyApp/script/myapp_fastcgi.pl/
1724
1725The above commands will launch 3 app processes and make the app available at
1726/myapp/
1727
1728=head3 Standalone server mode
1729
1730While not as easy as the previous method, running your app as an external
1731server gives you much more flexibility.
1732
1733First, launch your app as a standalone server listening on a socket.
1734
1735 script/myapp_fastcgi.pl -l /tmp/myapp.socket -n 5 -p /tmp/myapp.pid -d
1736
1737You can also listen on a TCP port if your web server is not on the same
1738machine.
1739
1740 script/myapp_fastcgi.pl -l :8080 -n 5 -p /tmp/myapp.pid -d
1741
1742You will probably want to write an init script to handle starting/stopping
1743of the app using the pid file.
1744
1745Now, we simply configure Apache to connect to the running server.
1746
1747 # 502 is a Bad Gateway error, and will occur if the backend server is down
1748 # This allows us to display a friendly static page that says "down for
1749 # maintenance"
1750 Alias /_errors /var/www/MyApp/root/error-pages
1751 ErrorDocument 502 /_errors/502.html
1752
31bdf270 1753 FastCgiExternalServer /tmp/myapp.fcgi -socket /tmp/myapp.socket
1754 Alias /myapp/ /tmp/myapp.fcgi/
cb93c9d7 1755
1756 # Or, run at the root
31bdf270 1757 Alias / /tmp/myapp.fcgi/
cb93c9d7 1758
1759=head3 More Info
1760
1761L<Catalyst::Engine::FastCGI>.
1762
1763=head2 Development server deployment
1764
1765The development server is a mini web server written in perl. If you
1766expect a low number of hits or you don't need mod_perl/FastCGI speed,
1767you could use the development server as the application server with a
ad2a47ab 1768lightweight proxy web server at the front. However, consider using
816fc503 1769L<Catalyst::Engine::HTTP::Prefork> for this kind of deployment instead, since
ad2a47ab 1770it can better handle multiple concurrent requests without forking, or can
1771prefork a set number of servers for improved performance.
cb93c9d7 1772
1773=head3 Pros
1774
1775As this is an application server setup, the pros are the same as
1776FastCGI (with the exception of speed).
1777It is also:
1778
1779=head4 Simple
1780
1781The development server is what you create your code on, so if it works
1782here, it should work in production!
1783
1784=head3 Cons
1785
1786=head4 Speed
1787
1788Not as fast as mod_perl or FastCGI. Needs to fork for each request
1789that comes in - make sure static files are served by the web server to
1790save forking.
1791
1792=head3 Setup
1793
1794=head4 Start up the development server
1795
ad2a47ab 1796 script/myapp_server.pl -p 8080 -k -f -pidfile=/tmp/myapp.pid
cb93c9d7 1797
1798You will probably want to write an init script to handle stop/starting
1799the app using the pid file.
1800
1801=head4 Configuring Apache
1802
1803Make sure mod_proxy is enabled and add:
1804
1805 # Serve static content directly
1806 DocumentRoot /var/www/MyApp/root
1807 Alias /static /var/www/MyApp/root/static
1808
1809 ProxyRequests Off
1810 <Proxy *>
1811 Order deny,allow
1812 Allow from all
1813 </Proxy>
816fc503 1814
1815 # Need to specifically stop these paths from being passed to proxy
1816 ProxyPass /static !
1817 ProxyPass /favicon.ico !
1818
cb93c9d7 1819 ProxyPass / http://localhost:8080/
1820 ProxyPassReverse / http://localhost:8080/
1821
816fc503 1822 # This is optional if you'd like to show a custom error page
1823 # if the proxy is not available
1824 ErrorDocument 502 /static/error_pages/http502.html
1825
cb93c9d7 1826You can wrap the above within a VirtualHost container if you want
1827different apps served on the same host.
1828
1829=head2 Quick deployment: Building PAR Packages
1830
1831You have an application running on your development box, but then you
1832have to quickly move it to another one for
1833demonstration/deployment/testing...
1834
1835PAR packages can save you from a lot of trouble here. They are usual Zip
1836files that contain a blib tree; you can even include all prereqs and a
1837perl interpreter by setting a few flags!
1838
1839=head3 Follow these few points to try it out!
1840
18411. Install Catalyst and PAR 0.89 (or later)
1842
1843 % perl -MCPAN -e 'install Catalyst'
1844 ...
1845 % perl -MCPAN -e 'install PAR'
1846 ...
1847
18482. Create a application
1849
1850 % catalyst.pl MyApp
1851 ...
1852 % cd MyApp
1853
1854Recent versions of Catalyst (5.62 and up) include
1855L<Module::Install::Catalyst>, which simplifies the process greatly. From the shell in your application directory:
1856
1857 % perl Makefile.PL
1858 % make catalyst_par
1859
1860Congratulations! Your package "myapp.par" is ready, the following
1861steps are just optional.
1862
18633. Test your PAR package with "parl" (no typo)
1864
1865 % parl myapp.par
1866 Usage:
1867 [parl] myapp[.par] [script] [arguments]
1868
1869 Examples:
1870 parl myapp.par myapp_server.pl -r
1871 myapp myapp_cgi.pl
1872
1873 Available scripts:
1874 myapp_cgi.pl
1875 myapp_create.pl
1876 myapp_fastcgi.pl
1877 myapp_server.pl
1878 myapp_test.pl
1879
1880 % parl myapp.par myapp_server.pl
1881 You can connect to your server at http://localhost:3000
1882
1883Yes, this nifty little starter application gets automatically included.
1884You can also use "catalyst_par_script('myapp_server.pl')" to set a
1885default script to execute.
1886
18876. Want to create a binary that includes the Perl interpreter?
1888
1889 % pp -o myapp myapp.par
1890 % ./myapp myapp_server.pl
1891 You can connect to your server at http://localhost:3000
1892
1893=head2 Serving static content
1894
1895Serving static content in Catalyst used to be somewhat tricky; the use
1896of L<Catalyst::Plugin::Static::Simple> makes everything much easier.
1897This plugin will automatically serve your static content during development,
1898but allows you to easily switch to Apache (or other server) in a
1899production environment.
1900
1901=head3 Introduction to Static::Simple
1902
1903Static::Simple is a plugin that will help to serve static content for your
1904application. By default, it will serve most types of files, excluding some
1905standard Template Toolkit extensions, out of your B<root> file directory. All
1906files are served by path, so if B<images/me.jpg> is requested, then
1907B<root/images/me.jpg> is found and served.
1908
1909=head3 Usage
1910
1911Using the plugin is as simple as setting your use line in MyApp.pm to include:
1912
b411df01 1913 use Catalyst qw/Static::Simple/;
cb93c9d7 1914
1915and already files will be served.
1916
1917=head3 Configuring
1918
1919Static content is best served from a single directory within your root
1920directory. Having many different directories such as C<root/css> and
1921C<root/images> requires more code to manage, because you must separately
1922identify each static directory--if you decide to add a C<root/js>
1923directory, you'll need to change your code to account for it. In
1924contrast, keeping all static directories as subdirectories of a main
1925C<root/static> directory makes things much easier to manage. Here's an
1926example of a typical root directory structure:
1927
1928 root/
1929 root/content.tt
1930 root/controller/stuff.tt
1931 root/header.tt
1932 root/static/
1933 root/static/css/main.css
1934 root/static/images/logo.jpg
1935 root/static/js/code.js
1936
1937
1938All static content lives under C<root/static>, with everything else being
1939Template Toolkit files.
1940
1941=over 4
1942
1943=item Include Path
1944
1945You may of course want to change the default locations, and make
1946Static::Simple look somewhere else, this is as easy as:
1947
1948 MyApp->config->{static}->{include_path} = [
1949 MyApp->config->{root},
1950 '/path/to/my/files'
1951 ];
1952
1953When you override include_path, it will not automatically append the
1954normal root path, so you need to add it yourself if you still want
1955it. These will be searched in order given, and the first matching file
1956served.
1957
1958=item Static directories
1959
1960If you want to force some directories to be only static, you can set
1961them using paths relative to the root dir, or regular expressions:
1962
1963 MyApp->config->{static}->{dirs} = [
1964 'static',
1965 qr/^(images|css)/,
1966 ];
1967
1968=item File extensions
1969
1970By default, the following extensions are not served (that is, they will
1971be processed by Catalyst): B<tmpl, tt, tt2, html, xhtml>. This list can
1972be replaced easily:
1973
1974 MyApp->config->{static}->{ignore_extensions} = [
1975 qw/tmpl tt tt2 html xhtml/
1976 ];
1977
1978=item Ignoring directories
1979
1980Entire directories can be ignored. If used with include_path,
1981directories relative to the include_path dirs will also be ignored:
1982
1983 MyApp->config->{static}->{ignore_dirs} = [ qw/tmpl css/ ];
1984
1985=back
1986
1987=head3 More information
1988
1989L<http://search.cpan.org/dist/Catalyst-Plugin-Static-Simple/>
1990
1991=head3 Serving manually with the Static plugin with HTTP::Daemon (myapp_server.pl)
1992
1993In some situations you might want to control things more directly,
1994using L<Catalyst::Plugin::Static>.
1995
1996In your main application class (MyApp.pm), load the plugin:
1997
b411df01 1998 use Catalyst qw/-Debug FormValidator Static OtherPlugin/;
cb93c9d7 1999
2000You will also need to make sure your end method does I<not> forward
2001static content to the view, perhaps like this:
2002
2003 sub end : Private {
2004 my ( $self, $c ) = @_;
2005
2006 $c->forward( 'MyApp::View::TT' )
2007 unless ( $c->res->body || !$c->stash->{template} );
2008 }
2009
2010This code will only forward to the view if a template has been
2011previously defined by a controller and if there is not already data in
2012C<$c-E<gt>res-E<gt>body>.
2013
2014Next, create a controller to handle requests for the /static path. Use
2015the Helper to save time. This command will create a stub controller as
2016C<lib/MyApp/Controller/Static.pm>.
2017
2018 $ script/myapp_create.pl controller Static
2019
2020Edit the file and add the following methods:
2021
2022 # serve all files under /static as static files
2023 sub default : Path('/static') {
2024 my ( $self, $c ) = @_;
2025
2026 # Optional, allow the browser to cache the content
2027 $c->res->headers->header( 'Cache-Control' => 'max-age=86400' );
2028
2029 $c->serve_static; # from Catalyst::Plugin::Static
2030 }
2031
2032 # also handle requests for /favicon.ico
2033 sub favicon : Path('/favicon.ico') {
2034 my ( $self, $c ) = @_;
2035
2036 $c->serve_static;
2037 }
2038
2039You can also define a different icon for the browser to use instead of
2040favicon.ico by using this in your HTML header:
2041
2042 <link rel="icon" href="/static/myapp.ico" type="image/x-icon" />
2043
2044=head3 Common problems with the Static plugin
2045
2046The Static plugin makes use of the C<shared-mime-info> package to
2047automatically determine MIME types. This package is notoriously
2048difficult to install, especially on win32 and OS X. For OS X the easiest
2049path might be to install Fink, then use C<apt-get install
2050shared-mime-info>. Restart the server, and everything should be fine.
2051
2052Make sure you are using the latest version (>= 0.16) for best
2053results. If you are having errors serving CSS files, or if they get
2054served as text/plain instead of text/css, you may have an outdated
2055shared-mime-info version. You may also wish to simply use the following
2056code in your Static controller:
2057
2058 if ($c->req->path =~ /css$/i) {
2059 $c->serve_static( "text/css" );
2060 } else {
2061 $c->serve_static;
2062 }
2063
2064=head3 Serving Static Files with Apache
2065
2066When using Apache, you can bypass Catalyst and any Static
2067plugins/controllers controller by intercepting requests for the
2068C<root/static> path at the server level. All that is required is to
2069define a DocumentRoot and add a separate Location block for your static
2070content. Here is a complete config for this application under mod_perl
20711.x:
2072
2073 <Perl>
2074 use lib qw(/var/www/MyApp/lib);
2075 </Perl>
2076 PerlModule MyApp
2077
2078 <VirtualHost *>
2079 ServerName myapp.example.com
2080 DocumentRoot /var/www/MyApp/root
2081 <Location />
2082 SetHandler perl-script
2083 PerlHandler MyApp
2084 </Location>
2085 <LocationMatch "/(static|favicon.ico)">
2086 SetHandler default-handler
2087 </LocationMatch>
2088 </VirtualHost>
2089
2090And here's a simpler example that'll get you started:
2091
2092 Alias /static/ "/my/static/files/"
2093 <Location "/static">
2094 SetHandler none
2095 </Location>
2096
2097=head2 Caching
2098
2099Catalyst makes it easy to employ several different types of caching to
2100speed up your applications.
2101
2102=head3 Cache Plugins
2103
2104There are three wrapper plugins around common CPAN cache modules:
2105Cache::FastMmap, Cache::FileCache, and Cache::Memcached. These can be
2106used to cache the result of slow operations.
2107
ca7528df 2108The Catalyst Advent Calendar uses the FileCache plugin to cache the
cb93c9d7 2109rendered XHTML version of the source POD document. This is an ideal
ca7528df 2110application for a cache because the source document changes
2111infrequently but may be viewed many times.
cb93c9d7 2112
b411df01 2113 use Catalyst qw/Cache::FileCache/;
cb93c9d7 2114
2115 ...
2116
2117 use File::stat;
2118 sub render_pod : Local {
2119 my ( self, $c ) = @_;
2120
2121 # the cache is keyed on the filename and the modification time
2122 # to check for updates to the file.
2123 my $file = $c->path_to( 'root', '2005', '11.pod' );
2124 my $mtime = ( stat $file )->mtime;
2125
2126 my $cached_pod = $c->cache->get("$file $mtime");
2127 if ( !$cached_pod ) {
2128 $cached_pod = do_slow_pod_rendering();
2129 # cache the result for 12 hours
2130 $c->cache->set( "$file $mtime", $cached_pod, '12h' );
2131 }
2132 $c->stash->{pod} = $cached_pod;
2133 }
2134
2135We could actually cache the result forever, but using a value such as 12 hours
2136allows old entries to be automatically expired when they are no longer needed.
2137
2138=head3 Page Caching
2139
2140Another method of caching is to cache the entire HTML page. While this is
2141traditionally handled by a front-end proxy server like Squid, the Catalyst
2142PageCache plugin makes it trivial to cache the entire output from
2143frequently-used or slow actions.
2144
2145Many sites have a busy content-filled front page that might look something
2146like this. It probably takes a while to process, and will do the exact same
2147thing for every single user who views the page.
2148
2149 sub front_page : Path('/') {
2150 my ( $self, $c ) = @_;
2151
2152 $c->forward( 'get_news_articles' );
2153 $c->forward( 'build_lots_of_boxes' );
2154 $c->forward( 'more_slow_stuff' );
2155
2156 $c->stash->{template} = 'index.tt';
2157 }
2158
2159We can add the PageCache plugin to speed things up.
2160
b411df01 2161 use Catalyst qw/Cache::FileCache PageCache/;
cb93c9d7 2162
2163 sub front_page : Path ('/') {
2164 my ( $self, $c ) = @_;
2165
2166 $c->cache_page( 300 );
2167
2168 # same processing as above
2169 }
2170
2171Now the entire output of the front page, from <html> to </html>, will be
2172cached for 5 minutes. After 5 minutes, the next request will rebuild the
2173page and it will be re-cached.
2174
2175Note that the page cache is keyed on the page URI plus all parameters, so
2176requests for / and /?foo=bar will result in different cache items. Also,
2177only GET requests will be cached by the plugin.
2178
2179You can even get that front-end Squid proxy to help out by enabling HTTP
2180headers for the cached page.
2181
2182 MyApp->config->{page_cache}->{set_http_headers} = 1;
2183
2184This would now set the following headers so proxies and browsers may cache
2185the content themselves.
2186
2187 Cache-Control: max-age=($expire_time - time)
2188 Expires: $expire_time
2189 Last-Modified: $cache_created_time
2190
2191=head3 Template Caching
2192
2193Template Toolkit provides support for caching compiled versions of your
2194templates. To enable this in Catalyst, use the following configuration.
2195TT will cache compiled templates keyed on the file mtime, so changes will
2196still be automatically detected.
2197
2198 package MyApp::View::TT;
2199
2200 use strict;
2201 use warnings;
2202 use base 'Catalyst::View::TT';
2203
2204 __PACKAGE__->config(
2205 COMPILE_DIR => '/tmp/template_cache',
2206 );
2207
2208 1;
2209
2210=head3 More Info
2211
2212See the documentation for each cache plugin for more details and other
2213available configuration options.
2214
2215L<Catalyst::Plugin::Cache::FastMmap>
2216L<Catalyst::Plugin::Cache::FileCache>
2217L<Catalyst::Plugin::Cache::Memcached>
2218L<Catalyst::Plugin::PageCache>
2219L<http://search.cpan.org/dist/Template-Toolkit/lib/Template/Manual/Config.pod#Caching_and_Compiling_Options>
2220
2221=head1 Testing
2222
2223Testing is an integral part of the web application development
2224process. Tests make multi developer teams easier to coordinate, and
2225they help ensure that there are no nasty surprises after upgrades or
2226alterations.
2227
2228=head2 Testing
2229
2230Catalyst provides a convenient way of testing your application during
2231development and before deployment in a real environment.
2232
2233C<Catalyst::Test> makes it possible to run the same tests both locally
2234(without an external daemon) and against a remote server via HTTP.
2235
2236=head3 Tests
2237
2238Let's examine a skeleton application's C<t/> directory:
2239
2240 mundus:~/MyApp chansen$ ls -l t/
2241 total 24
2242 -rw-r--r-- 1 chansen chansen 95 18 Dec 20:50 01app.t
2243 -rw-r--r-- 1 chansen chansen 190 18 Dec 20:50 02pod.t
2244 -rw-r--r-- 1 chansen chansen 213 18 Dec 20:50 03podcoverage.t
2245
2246=over 4
2247
2248=item C<01app.t>
2249
2250Verifies that the application loads, compiles, and returns a successful
2251response.
2252
2253=item C<02pod.t>
2254
2255Verifies that all POD is free from errors. Only executed if the C<TEST_POD>
2256environment variable is true.
2257
2258=item C<03podcoverage.t>
2259
2260Verifies that all methods/functions have POD coverage. Only executed if the
2261C<TEST_POD> environment variable is true.
2262
2263=back
2264
2265=head3 Creating tests
2266
2267 mundus:~/MyApp chansen$ cat t/01app.t | perl -ne 'printf( "%2d %s", $., $_ )'
2268 1 use Test::More tests => 2;
2269 2 use_ok( Catalyst::Test, 'MyApp' );
2270 3
2271 4 ok( request('/')->is_success );
2272
2273The first line declares how many tests we are going to run, in this case
2274two. The second line tests and loads our application in test mode. The
2275fourth line verifies that our application returns a successful response.
2276
2277C<Catalyst::Test> exports two functions, C<request> and C<get>. Each can
2278take three different arguments:
2279
2280=over 4
2281
2282=item A string which is a relative or absolute URI.
2283
2284 request('/my/path');
2285 request('http://www.host.com/my/path');
2286
2287=item An instance of C<URI>.
2288
2289 request( URI->new('http://www.host.com/my/path') );
2290
2291=item An instance of C<HTTP::Request>.
2292
2293 request( HTTP::Request->new( GET => 'http://www.host.com/my/path') );
2294
2295=back
2296
2297C<request> returns an instance of C<HTTP::Response> and C<get> returns the
2298content (body) of the response.
2299
2300=head3 Running tests locally
2301
2302 mundus:~/MyApp chansen$ CATALYST_DEBUG=0 TEST_POD=1 prove --lib lib/ t/
2303 t/01app............ok
2304 t/02pod............ok
2305 t/03podcoverage....ok
2306 All tests successful.
2307 Files=3, Tests=4, 2 wallclock secs ( 1.60 cusr + 0.36 csys = 1.96 CPU)
2308
2309C<CATALYST_DEBUG=0> ensures that debugging is off; if it's enabled you
2310will see debug logs between tests.
2311
2312C<TEST_POD=1> enables POD checking and coverage.
2313
2314C<prove> A command-line tool that makes it easy to run tests. You can
2315find out more about it from the links below.
2316
2317=head3 Running tests remotely
2318
2319 mundus:~/MyApp chansen$ CATALYST_SERVER=http://localhost:3000/ prove --lib lib/ t/01app.t
2320 t/01app....ok
2321 All tests successful.
2322 Files=1, Tests=2, 0 wallclock secs ( 0.40 cusr + 0.01 csys = 0.41 CPU)
2323
2324C<CATALYST_SERVER=http://localhost:3000/> is the absolute deployment URI of
2325your application. In C<CGI> or C<FastCGI> it should be the host and path
2326to the script.
2327
2328=head3 C<Test::WWW::Mechanize> and Catalyst
2329
2330Be sure to check out C<Test::WWW::Mechanize::Catalyst>. It makes it easy to
2331test HTML, forms and links. A short example of usage:
2332
2333 use Test::More tests => 6;
2334 use_ok( Test::WWW::Mechanize::Catalyst, 'MyApp' );
2335
2336 my $mech = Test::WWW::Mechanize::Catalyst->new;
2337 $mech->get_ok("http://localhost/", 'Got index page');
2338 $mech->title_like( qr/^MyApp on Catalyst/, 'Got right index title' );
2339 ok( $mech->find_link( text_regex => qr/^Wiki/i ), 'Found link to Wiki' );
2340 ok( $mech->find_link( text_regex => qr/^Mailing-List/i ), 'Found link to Mailing-List' );
2341 ok( $mech->find_link( text_regex => qr/^IRC channel/i ), 'Found link to IRC channel' );
2342
2343=head3 Further Reading
2344
2345=over 4
2346
2347=item Catalyst::Test
2348
2349L<http://search.cpan.org/dist/Catalyst/lib/Catalyst/Test.pm>
2350
2351=item Test::WWW::Mechanize::Catalyst
2352
2353L<http://search.cpan.org/dist/Test-WWW-Mechanize-Catalyst/lib/Test/WWW/Mechanize/Catalyst.pm>
2354
2355=item Test::WWW::Mechanize
2356
2357L<http://search.cpan.org/dist/Test-WWW-Mechanize/Mechanize.pm>
2358
2359=item WWW::Mechanize
2360
2361L<http://search.cpan.org/dist/WWW-Mechanize/lib/WWW/Mechanize.pm>
2362
2363=item LWP::UserAgent
2364
2365L<http://search.cpan.org/dist/libwww-perl/lib/LWP/UserAgent.pm>
2366
2367=item HTML::Form
2368
2369L<http://search.cpan.org/dist/libwww-perl/lib/HTML/Form.pm>
2370
2371=item HTTP::Message
2372
2373L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Message.pm>
2374
2375=item HTTP::Request
2376
2377L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Request.pm>
2378
2379=item HTTP::Request::Common
2380
2381L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Request/Common.pm>
2382
2383=item HTTP::Response
2384
2385L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Response.pm>
2386
2387=item HTTP::Status
2388
2389L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Status.pm>
2390
2391=item URI
2392
2393L<http://search.cpan.org/dist/URI/URI.pm>
2394
2395=item Test::More
2396
2397L<http://search.cpan.org/dist/Test-Simple/lib/Test/More.pm>
2398
2399=item Test::Pod
2400
2401L<http://search.cpan.org/dist/Test-Pod/Pod.pm>
2402
2403=item Test::Pod::Coverage
2404
2405L<http://search.cpan.org/dist/Test-Pod-Coverage/Coverage.pm>
2406
2407=item prove (Test::Harness)
2408
2409L<http://search.cpan.org/dist/Test-Harness/bin/prove>
2410
2411=back
2412
2413=head3 More Information
2414
2415L<http://search.cpan.org/perldoc?Catalyst::Plugin::Authorization::Roles>
2416L<http://search.cpan.org/perldoc?Catalyst::Plugin::Authorization::ACL>
2417
2418=head1 AUTHORS
2419
2420Sebastian Riedel C<sri@oook.de>
2421
2422Danijel Milicevic C<me@danijel.de>
2423
2424Viljo Marrandi C<vilts@yahoo.com>
2425
2426Marcus Ramberg C<mramberg@cpan.org>
2427
2428Jesse Sheidlower C<jester@panix.com>
2429
2430Andy Grundman C<andy@hybridized.org>
2431
2432Chisel Wright C<pause@herlpacker.co.uk>
2433
2434Will Hawes C<info@whawes.co.uk>
2435
2436Gavin Henry C<ghenry@perl.me.uk>
2437
2438Kieren Diment C<kd@totaldatasolution.com>
2439
2440=head1 COPYRIGHT
2441
2442This document is free, you can redistribute it and/or modify it
2443under the same terms as Perl itself.
2444