Fix DBIC::Schema helper invocation examples (RT#100597)
[catagits/Catalyst-Manual.git] / lib / Catalyst / Manual / Cookbook.pod
CommitLineData
7e4aa7c6 1=encoding utf8
2
cb93c9d7 3=head1 NAME
4
5Catalyst::Manual::Cookbook - Cooking with Catalyst
6
7=head1 DESCRIPTION
8
9Yummy code like your mum used to bake!
10
11=head1 RECIPES
12
13=head1 Basics
14
c718cfb6 15These recipes cover some basic stuff that is worth knowing for
46a5f2f5 16Catalyst developers.
cb93c9d7 17
18=head2 Delivering a Custom Error Page
19
20By default, Catalyst will display its own error page whenever it
21encounters an error in your application. When running under C<-Debug>
c718cfb6 22mode, the error page is a useful screen including the error message
23and L<Data::Dump> output of the relevant parts of the C<$c> context
24object. When not in C<-Debug>, users see a simple "Please come back
25later" screen.
cb93c9d7 26
c718cfb6 27To use a custom error page, use a special C<end> method to
28short-circuit the error processing. The following is an example; you
29might want to adjust it further depending on the needs of your
30application (for example, any calls to C<fillform> will probably need
31to go into this C<end> method; see L<Catalyst::Plugin::FillInForm>).
cb93c9d7 32
33 sub end : Private {
34 my ( $self, $c ) = @_;
35
36 if ( scalar @{ $c->error } ) {
37 $c->stash->{errors} = $c->error;
bf6900ba 38 for my $error ( @{ $c->error } ) {
39 $c->log->error($error);
40 }
cb93c9d7 41 $c->stash->{template} = 'errors.tt';
42 $c->forward('MyApp::View::TT');
bf6900ba 43 $c->clear_errors;
cb93c9d7 44 }
45
46 return 1 if $c->response->status =~ /^3\d\d$/;
47 return 1 if $c->response->body;
48
49 unless ( $c->response->content_type ) {
50 $c->response->content_type('text/html; charset=utf-8');
51 }
52
53 $c->forward('MyApp::View::TT');
54 }
55
56You can manually set errors in your code to trigger this page by calling
57
58 $c->error( 'You broke me!' );
59
60=head2 Disable statistics
61
c718cfb6 62Just add this line to your application class if you don't want those
63nifty statistics in your debug messages.
cb93c9d7 64
65 sub Catalyst::Log::info { }
66
67=head2 Enable debug status in the environment
68
69Normally you enable the debugging info by adding the C<-Debug> flag to
b411df01 70your C<use Catalyst> statement . However, you can also enable it using
71environment variable, so you can (for example) get debug info without
72modifying your application scripts. Just set C<CATALYST_DEBUG> or
7c098817 73C<E<lt>MYAPPE<gt>_DEBUG> to a true value.
cb93c9d7 74
75=head2 Sessions
76
c718cfb6 77When you have your users identified, you will want to somehow remember
78that fact, to save them from having to identify themselves for every
79single page. One way to do this is to send the username and password
80parameters in every single page, but that's ugly, and won't work for
81static pages.
cb93c9d7 82
c718cfb6 83Sessions are a method of saving data related to some transaction, and
84giving the whole collection a single ID. This ID is then given to the
85user to return to us on every page they visit while logged in. The
86usual way to do this is using a browser cookie.
cb93c9d7 87
88Catalyst uses two types of plugins to represent sessions:
89
90=head3 State
91
c718cfb6 92A State module is used to keep track of the state of the session
93between the users browser, and your application.
cb93c9d7 94
c718cfb6 95A common example is the Cookie state module, which sends the browser a
96cookie containing the session ID. It will use default value for the
97cookie name and domain, so will "just work" when used.
cb93c9d7 98
99=head3 Store
100
c718cfb6 101A Store module is used to hold all the data relating to your session,
102for example the users ID, or the items for their shopping cart. You
103can store data in memory (FastMmap), in a file (File) or in a database
104(DBI).
cb93c9d7 105
106=head3 Authentication magic
107
108If you have included the session modules in your application, the
109Authentication modules will automagically use your session to save and
110retrieve the user data for you.
111
112=head3 Using a session
113
114Once the session modules are loaded, the session is available as C<<
5336f546 115$c->session >>, and can be written to and read from as a simple hash
c718cfb6 116reference.
cb93c9d7 117
118=head3 EXAMPLE
119
b1a08fe1 120 package MyApp;
121 use Moose;
122 use namespace::autoclean;
123
b411df01 124 use Catalyst qw/
ca7528df 125 Session
126 Session::Store::FastMmap
127 Session::State::Cookie
b411df01 128 /;
b1a08fe1 129 extends 'Catalyst';
130 __PACKAGE__->setup;
cb93c9d7 131
b1a08fe1 132 package MyApp::Controller::Foo;
133 use Moose;
134 use namespace::autoclean;
46a5f2f5 135 BEGIN { extends 'Catalyst::Controller' };
cb93c9d7 136 ## Write data into the session
137
138 sub add_item : Local {
139 my ( $self, $c ) = @_;
140
76776098 141 my $item_id = $c->req->params->{item};
cb93c9d7 142
143 push @{ $c->session->{items} }, $item_id;
144
145 }
146
147 ## A page later we retrieve the data from the session:
148
149 sub get_items : Local {
150 my ( $self, $c ) = @_;
151
152 $c->stash->{items_to_display} = $c->session->{items};
153
154 }
155
156
157=head3 More information
158
159L<http://search.cpan.org/dist/Catalyst-Plugin-Session>
160
161L<http://search.cpan.org/dist/Catalyst-Plugin-Session-State-Cookie>
162
163L<http://search.cpan.org/dist/Catalyst-Plugin-Session-State-URI>
164
165L<http://search.cpan.org/dist/Catalyst-Plugin-Session-Store-FastMmap>
166
167L<http://search.cpan.org/dist/Catalyst-Plugin-Session-Store-File>
168
169L<http://search.cpan.org/dist/Catalyst-Plugin-Session-Store-DBI>
170
171=head2 Configure your application
172
173You configure your application with the C<config> method in your
174application class. This can be hard-coded, or brought in from a
175separate configuration file.
176
c010ae0d 177=head3 Using Config::General
cb93c9d7 178
c010ae0d 179L<Config::General|Config::General> is a method for creating flexible
180and readable configuration files. It's a great way to keep your
181Catalyst application configuration in one easy-to-understand location.
cb93c9d7 182
c010ae0d 183Now create C<myapp.conf> in your application home:
cb93c9d7 184
c010ae0d 185 name MyApp
cb93c9d7 186
187 # session; perldoc Catalyst::Plugin::Session::FastMmap
c010ae0d 188 <Session>
189 expires 3600
190 rewrite 0
191 storage /tmp/myapp.session
192 </Session>
cb93c9d7 193
194 # emails; perldoc Catalyst::Plugin::Email
195 # this passes options as an array :(
c010ae0d 196 Mail SMTP
197 Mail localhost
cb93c9d7 198
199This is equivalent to:
200
201 # configure base package
202 __PACKAGE__->config( name => MyApp );
203 # configure authentication
19a5b486 204 __PACKAGE__->config(
205 'Plugin::Authentication' => {
206 user_class => 'MyApp::Model::MyDB::Customer',
207 ...
208 },
209 _;
cb93c9d7 210 # configure sessions
19a5b486 211 __PACKAGE__->config(
212 session => {
213 expires => 3600,
214 ...
215 },
216 );
cb93c9d7 217 # configure email sending
19a5b486 218 __PACKAGE__->config( email => [qw/SMTP localhost/] );
cb93c9d7 219
552daee0 220L<Catalyst> explains precedence of multiple sources for configuration
221values, how to access the values in your components, and many 'base'
222config variables used internally.
223
c010ae0d 224See also L<Config::General|Config::General>.
cb93c9d7 225
226=head1 Skipping your VCS's directories
227
46a5f2f5 228Catalyst uses Module::Pluggable to load Models, Views, and Controllers.
cb93c9d7 229Module::Pluggable will scan through all directories and load modules
230it finds. Sometimes you might want to skip some of these directories,
231for example when your version control system makes a subdirectory with
232meta-information in every version-controlled directory. While
233Catalyst skips subversion and CVS directories already, there are other
234source control systems. Here is the configuration you need to add
235their directories to the list to skip.
236
46a5f2f5 237You can make Catalyst skip these directories using the Catalyst config:
cb93c9d7 238
239 # Configure the application
240 __PACKAGE__->config(
241 name => 'MyApp',
242 setup_components => { except => qr/SCCS/ },
243 );
244
245See the Module::Pluggable manual page for more information on B<except>
246and other options.
247
248=head1 Users and Access Control
249
46a5f2f5 250Most multiuser, and some single-user web applications require that
cb93c9d7 251users identify themselves, and the application is often required to
252define those roles. The recipes below describe some ways of doing
253this.
254
255=head2 Authentication (logging in)
256
257This is extensively covered in other documentation; see in particular
258L<Catalyst::Plugin::Authentication> and the Authentication chapter
5641c0c1 259of the Tutorial at L<Catalyst::Manual::Tutorial::06_Authorization>.
cb93c9d7 260
261=head2 Pass-through login (and other actions)
262
263An easy way of having assorted actions that occur during the processing
264of a request that are orthogonal to its actual purpose - logins, silent
265commands etc. Provide actions for these, but when they're required for
266something else fill e.g. a form variable __login and have a sub begin
267like so:
268
269 sub begin : Private {
270 my ($self, $c) = @_;
271 foreach my $action (qw/login docommand foo bar whatever/) {
272 if ($c->req->params->{"__${action}"}) {
273 $c->forward($action);
274 }
275 }
276 }
277
cb93c9d7 278=head2 Authentication/Authorization
279
280This is done in several steps:
281
282=over 4
283
284=item Verification
285
286Getting the user to identify themselves, by giving you some piece of
c718cfb6 287information known only to you and the user. Then you can assume that
288the user is who they say they are. This is called B<credential
289verification>.
cb93c9d7 290
291=item Authorization
292
c718cfb6 293Making sure the user only accesses functions you want them to
46a5f2f5 294access. This is done by checking the verified user's data against your
c718cfb6 295internal list of groups, or allowed persons for the current page.
cb93c9d7 296
297=back
298
299=head3 Modules
300
c718cfb6 301The Catalyst Authentication system is made up of many interacting
302modules, to give you the most flexibility possible.
cb93c9d7 303
304=head4 Credential verifiers
305
c718cfb6 306A Credential module tables the user input, and passes it to a Store,
307or some other system, for verification. Typically, a user object is
308created by either this module or the Store and made accessible by a
309C<< $c->user >> call.
cb93c9d7 310
311Examples:
312
313 Password - Simple username/password checking.
314 HTTPD - Checks using basic HTTP auth.
315 TypeKey - Check using the typekey system.
316
317=head3 Storage backends
318
c718cfb6 319A Storage backend contains the actual data representing the users. It
320is queried by the credential verifiers. Updating the store is not done
46a5f2f5 321within this system; you will need to do it yourself.
cb93c9d7 322
323Examples:
324
46a5f2f5 325 DBIC - Storage using a database via DBIx::Class.
cb93c9d7 326 Minimal - Storage using a simple hash (for testing).
327
328=head3 User objects
329
c718cfb6 330A User object is created by either the storage backend or the
46a5f2f5 331credential verifier, and is filled with the retrieved user information.
cb93c9d7 332
333Examples:
334
335 Hash - A simple hash of keys and values.
336
337=head3 ACL authorization
338
c718cfb6 339ACL stands for Access Control List. The ACL plugin allows you to
46a5f2f5 340regulate access on a path-by-path basis, by listing which users, or
c718cfb6 341roles, have access to which paths.
cb93c9d7 342
343=head3 Roles authorization
344
c718cfb6 345Authorization by roles is for assigning users to groups, which can
346then be assigned to ACLs, or just checked when needed.
cb93c9d7 347
348=head3 Logging in
349
350When you have chosen your modules, all you need to do is call the C<<
b1a08fe1 351$c->authenticate >> method. If called with no parameters, it will try to find
c718cfb6 352suitable parameters, such as B<username> and B<password>, or you can
353pass it these values.
cb93c9d7 354
355=head3 Checking roles
356
46a5f2f5 357Role checking is done by using the C<< $c->check_user_roles >> method.
358This will check using the currently logged-in user (via C<< $c->user
c718cfb6 359>>). You pass it the name of a role to check, and it returns true if
360the user is a member.
cb93c9d7 361
362=head3 EXAMPLE
363
bbddff00 364 package MyApp;
365 use Moose;
366 use namespace::autoclean;
367 extends qw/Catalyst/;
368 use Catalyst qw/
369 Authentication
370 Authorization::Roles
371 /;
372
373 __PACKAGE__->config(
374 authentication => {
375 default_realm => 'test',
376 realms => {
377 test => {
378 credential => {
379 class => 'Password',
380 password_field => 'password',
381 password_type => 'self_check',
382 },
383 store => {
384 class => 'Htpasswd',
385 file => 'htpasswd',
386 },
387 },
388 },
31e9c5ef 389 },
bbddff00 390 );
391
392 package MyApp::Controller::Root;
393 use Moose;
394 use namespace::autoclean;
31e9c5ef 395
bbddff00 396 BEGIN { extends 'Catalyst::Controller' }
31e9c5ef 397
bbddff00 398 __PACKAGE__->config(namespace => '');
31e9c5ef 399
bbddff00 400 sub login : Local {
cb93c9d7 401 my ($self, $c) = @_;
402
76776098 403 if ( my $user = $c->req->params->{user}
404 and my $password = $c->req->param->{password} )
cb93c9d7 405 {
b1a08fe1 406 if ( $c->authenticate( username => $user, password => $password ) ) {
cb93c9d7 407 $c->res->body( "hello " . $c->user->name );
408 } else {
409 # login incorrect
410 }
411 }
412 else {
413 # invalid form input
414 }
415 }
416
417 sub restricted : Local {
418 my ( $self, $c ) = @_;
419
420 $c->detach("unauthorized")
421 unless $c->check_user_roles( "admin" );
422
423 # do something restricted here
424 }
425
426=head3 Using authentication in a testing environment
427
46a5f2f5 428Ideally, to write tests for authentication/authorization code one would
429first set up a test database with known data, then use
c718cfb6 430L<Test::WWW::Mechanize::Catalyst> to simulate a user logging
46a5f2f5 431in. Unfortunately this can be rather awkward, which is why it's a good
432thing that the authentication framework is so flexible.
cb93c9d7 433
c718cfb6 434Instead of using a test database, one can simply change the
435authentication store to something a bit easier to deal with in a
436testing environment. Additionally, this has the advantage of not
437modifying one's database, which can be problematic if one forgets to
438use the testing instead of production database.
cb93c9d7 439
46a5f2f5 440Alternatively, if you want to authenticate real users, but not have to
441worry about their passwords, you can use
442L<Catalyst::Authentication::Credential::Testing> to force all users to
443authenticate with a global password.
cb93c9d7 444
445=head3 More information
446
bbddff00 447L<Catalyst::Plugin::Authentication> has a longer explanation.
cb93c9d7 448
449=head2 Authorization
450
451=head3 Introduction
452
c718cfb6 453Authorization is the step that comes after
46a5f2f5 454authentication. Authentication establishes that the user agent is really
455representing the user we think it's representing, and then authorization
456determines what this user is allowed to do.
cb93c9d7 457
458=head3 Role Based Access Control
459
c718cfb6 460Under role based access control each user is allowed to perform any
461number of roles. For example, at a zoo no one but specially trained
bfa445ed 462personnel can enter the moose cage (Mynd you, møøse bites kan be
c718cfb6 463pretty nasti!). For example:
cb93c9d7 464
465 package Zoo::Controller::MooseCage;
466
467 sub feed_moose : Local {
468 my ( $self, $c ) = @_;
469
76776098 470 $c->model( "Moose" )->eat( $c->req->params->{food} );
cb93c9d7 471 }
472
c718cfb6 473With this action, anyone can just come into the moose cage and feed
474the moose, which is a very dangerous thing. We need to restrict this
475action, so that only a qualified moose feeder can perform that action.
cb93c9d7 476
46a5f2f5 477The Authorization::Roles plugin lets us perform role based access
c718cfb6 478control checks. Let's load it:
cb93c9d7 479
ca7528df 480 use parent qw/Catalyst/;
b411df01 481 use Catalyst qw/
b1a08fe1 482 Authentication
b411df01 483 Authorization::Roles
484 /;
cb93c9d7 485
486And now our action should look like this:
487
488 sub feed_moose : Local {
489 my ( $self, $c ) = @_;
490
491 if ( $c->check_roles( "moose_feeder" ) ) {
76776098 492 $c->model( "Moose" )->eat( $c->req->params->{food} );
cb93c9d7 493 } else {
494 $c->stash->{error} = "unauthorized";
495 }
496 }
497
c718cfb6 498This checks C<< $c->user >>, and only if the user has B<all> the roles
499in the list, a true value is returned.
cb93c9d7 500
c718cfb6 501C<check_roles> has a sister method, C<assert_roles>, which throws an
502exception if any roles are missing.
cb93c9d7 503
504Some roles that might actually make sense in, say, a forum application:
505
506=over 4
507
508=item *
509
510administrator
511
512=item *
513
514moderator
515
516=back
517
c718cfb6 518each with a distinct task (system administration versus content
519administration).
cb93c9d7 520
521=head3 Access Control Lists
522
523Checking for roles all the time can be tedious and error prone.
524
46a5f2f5 525The Authorization::ACL plugin lets us declare where we'd like checks
c718cfb6 526to be done automatically for us.
cb93c9d7 527
528For example, we may want to completely block out anyone who isn't a
529C<moose_feeder> from the entire C<MooseCage> controller:
530
531 Zoo->deny_access_unless( "/moose_cage", [qw/moose_feeder/] );
532
c718cfb6 533The role list behaves in the same way as C<check_roles>. However, the
534ACL plugin isn't limited to just interacting with the Roles plugin. We
535can use a code reference instead. For example, to allow either moose
536trainers or moose feeders into the moose cage, we can create a more
537complex check:
cb93c9d7 538
539 Zoo->deny_access_unless( "/moose_cage", sub {
540 my $c = shift;
541 $c->check_roles( "moose_trainer" ) || $c->check_roles( "moose_feeder" );
542 });
543
c718cfb6 544The more specific a role, the earlier it will be checked. Let's say
545moose feeders are now restricted to only the C<feed_moose> action,
546while moose trainers get access everywhere:
cb93c9d7 547
548 Zoo->deny_access_unless( "/moose_cage", [qw/moose_trainer/] );
549 Zoo->allow_access_if( "/moose_cage/feed_moose", [qw/moose_feeder/]);
550
c718cfb6 551When the C<feed_moose> action is accessed the second check will be
552made. If the user is a C<moose_feeder>, then access will be
553immediately granted. Otherwise, the next rule in line will be tested -
554the one checking for a C<moose_trainer>. If this rule is not
555satisfied, access will be immediately denied.
cb93c9d7 556
c718cfb6 557Rules applied to the same path will be checked in the order they were
558added.
cb93c9d7 559
c718cfb6 560Lastly, handling access denial events is done by creating an
561C<access_denied> private action:
cb93c9d7 562
563 sub access_denied : Private {
564 my ( $self, $c, $action ) = @_;
cb93c9d7 565 }
566
c718cfb6 567This action works much like auto, in that it is inherited across
568namespaces (not like object oriented code). This means that the
569C<access_denied> action which is B<nearest> to the action which was
570blocked will be triggered.
cb93c9d7 571
c718cfb6 572If this action does not exist, an error will be thrown, which you can
573clean up in your C<end> private action instead.
cb93c9d7 574
c718cfb6 575Also, it's important to note that if you restrict access to "/" then
46a5f2f5 576C<end>, C<default>, etc. will also be restricted.
cb93c9d7 577
578 MyApp->acl_allow_root_internals;
579
580will create rules that permit access to C<end>, C<begin>, and C<auto> in the
581root of your app (but not in any other controller).
582
583=head1 Models
584
433f1ad4 585Models are where application data belongs. Catalyst is extremely
cb93c9d7 586flexible with the kind of models that it can use. The recipes here
587are just the start.
588
589=head2 Using existing DBIC (etc.) classes with Catalyst
590
c718cfb6 591Many people have existing Model classes that they would like to use
592with Catalyst (or, conversely, they want to write Catalyst models that
593can be used outside of Catalyst, e.g. in a cron job). It's trivial to
594write a simple component in Catalyst that slurps in an outside Model:
cb93c9d7 595
596 package MyApp::Model::DB;
46a5f2f5 597
cb93c9d7 598 use base qw/Catalyst::Model::DBIC::Schema/;
46a5f2f5 599
cb93c9d7 600 __PACKAGE__->config(
601 schema_class => 'Some::DBIC::Schema',
0b60bbb5 602 connect_info => ['dbi:SQLite:foo.db', '', '', {AutoCommit=>1}],
cb93c9d7 603 );
46a5f2f5 604
cb93c9d7 605 1;
606
607and that's it! Now C<Some::DBIC::Schema> is part of your
608Cat app as C<MyApp::Model::DB>.
609
610=head2 DBIx::Class as a Catalyst Model
611
612See L<Catalyst::Model::DBIC::Schema>.
613
8428e94d 614=head2 Create accessors to preload static data once per server instance
615
616When you have data that you want to load just once from the model at
46a5f2f5 617startup, instead of for each request, use mk_group_accessors to
8428e94d 618create accessors and tie them to resultsets in your package that
46a5f2f5 619inherits from DBIx::Class::Schema:
8428e94d 620
621 package My::Schema;
622 use base qw/DBIx::Class::Schema/;
623 __PACKAGE__->register_class('RESULTSOURCEMONIKER',
624 'My::Schema::RESULTSOURCE');
625 __PACKAGE__->mk_group_accessors('simple' =>
626 qw(ACCESSORNAME1 ACCESSORNAME2 ACCESSORNAMEn));
627
628 sub connection {
629 my ($self, @rest) = @_;
630 $self->next::method(@rest);
b1a08fe1 631 # $self is now a live My::Schema object, complete with DB connection
8428e94d 632
633 $self->ACCESSORNAME1([ $self->resultset('RESULTSOURCEMONIKER')->all ]);
634 $self->ACCESSORNAME2([ $self->resultset('RESULTSOURCEMONIKER')->search({ COLUMN => { '<' => '30' } })->all ]);
635 $self->ACCESSORNAMEn([ $self->resultset('RESULTSOURCEMONIKER')->find(1) ]);
636 }
637
638 1;
639
640and now in the controller, you can now access any of these without a
641per-request fetch:
642
46a5f2f5 643 $c->stash->{something} = $c->model('My::Schema')->schema->ACCESSORNAME;
8428e94d 644
645
cb93c9d7 646=head2 XMLRPC
647
46a5f2f5 648Unlike SOAP, XMLRPC is a very simple (and elegant) web-services
cb93c9d7 649protocol, exchanging small XML messages like these:
650
651Request:
652
653 POST /api HTTP/1.1
654 TE: deflate,gzip;q=0.3
655 Connection: TE, close
656 Accept: text/xml
657 Accept: multipart/*
658 Host: 127.0.0.1:3000
659 User-Agent: SOAP::Lite/Perl/0.60
660 Content-Length: 192
661 Content-Type: text/xml
662
663 <?xml version="1.0" encoding="UTF-8"?>
664 <methodCall>
665 <methodName>add</methodName>
666 <params>
667 <param><value><int>1</int></value></param>
668 <param><value><int>2</int></value></param>
669 </params>
670 </methodCall>
671
672Response:
673
674 Connection: close
675 Date: Tue, 20 Dec 2005 07:45:55 GMT
676 Content-Length: 133
677 Content-Type: text/xml
678 Status: 200
679 X-Catalyst: 5.70
680
681 <?xml version="1.0" encoding="us-ascii"?>
682 <methodResponse>
683 <params>
684 <param><value><int>3</int></value></param>
685 </params>
686 </methodResponse>
687
688Now follow these few steps to implement the application:
689
6901. Install Catalyst (5.61 or later), Catalyst::Plugin::XMLRPC (0.06 or
691later) and SOAP::Lite (for XMLRPCsh.pl).
692
6932. Create an application framework:
694
695 % catalyst.pl MyApp
696 ...
697 % cd MyApp
698
6993. Add the XMLRPC plugin to MyApp.pm
700
b411df01 701 use Catalyst qw/-Debug Static::Simple XMLRPC/;
cb93c9d7 702
7034. Add an API controller
704
705 % ./script/myapp_create.pl controller API
706
7075. Add a XMLRPC redispatch method and an add method with Remote
708attribute to lib/MyApp/Controller/API.pm
709
85d49fb6 710 sub default :Path {
cb93c9d7 711 my ( $self, $c ) = @_;
712 $c->xmlrpc;
713 }
714
715 sub add : Remote {
716 my ( $self, $c, $a, $b ) = @_;
717 return $a + $b;
718 }
719
720The default action is the entry point for each XMLRPC request. It will
721redispatch every request to methods with Remote attribute in the same
722class.
723
724The C<add> method is not a traditional action; it has no private or
725public path. Only the XMLRPC dispatcher knows it exists.
726
7276. That's it! You have built your first web service. Let's test it with
728XMLRPCsh.pl (part of SOAP::Lite):
729
730 % ./script/myapp_server.pl
731 ...
732 % XMLRPCsh.pl http://127.0.0.1:3000/api
733 Usage: method[(parameters)]
734 > add( 1, 2 )
735 --- XMLRPC RESULT ---
736 '3'
737
738=head3 Tip
739
740Your return data type is usually auto-detected, but you can easily
741enforce a specific one.
742
743 sub add : Remote {
744 my ( $self, $c, $a, $b ) = @_;
745 return RPC::XML::int->new( $a + $b );
746 }
cb93c9d7 747
748=head1 Views
749
750Views pertain to the display of your application. As with models,
46a5f2f5 751Catalyst is uncommonly flexible. The recipes below are just a start.
cb93c9d7 752
753=head2 Catalyst::View::TT
754
755One of the first things you probably want to do when starting a new
756Catalyst application is set up your View. Catalyst doesn't care how you
757display your data; you can choose to generate HTML, PDF files, or plain
758text if you wanted.
759
760Most Catalyst applications use a template system to generate their HTML,
31e9c5ef 761and though there are several template systems available,
1febc43a 762L<Template Toolkit|Template> is probably the most popular.
cb93c9d7 763
764Once again, the Catalyst developers have done all the hard work, and
765made things easy for the rest of us. Catalyst::View::TT provides the
766interface to Template Toolkit, and provides Helpers which let us set it
767up that much more easily.
768
769=head3 Creating your View
770
771Catalyst::View::TT provides two different helpers for us to use: TT and
772TTSite.
773
774=head4 TT
775
776Create a basic Template Toolkit View using the provided helper script:
777
778 script/myapp_create.pl view TT TT
779
780This will create lib/MyApp/View/MyView.pm, which is going to be pretty
781empty to start. However, it sets everything up that you need to get
782started. You can now define which template you want and forward to your
783view. For instance:
784
785 sub hello : Local {
786 my ( $self, $c ) = @_;
787
788 $c->stash->{template} = 'hello.tt';
789
790 $c->forward( $c->view('TT') );
791 }
792
793In practice you wouldn't do the forwarding manually, but would
794use L<Catalyst::Action::RenderView>.
795
796=head4 TTSite
797
798Although the TT helper does create a functional, working view, you may
799find yourself having to create the same template files and changing the
800same options every time you create a new application. The TTSite helper
801saves us even more time by creating the basic templates and setting some
802common options for us.
803
804Once again, you can use the helper script:
805
806 script/myapp_create.pl view TT TTSite
807
808This time, the helper sets several options for us in the generated View.
809
810 __PACKAGE__->config({
811 CATALYST_VAR => 'Catalyst',
812 INCLUDE_PATH => [
813 MyApp->path_to( 'root', 'src' ),
814 MyApp->path_to( 'root', 'lib' )
815 ],
816 PRE_PROCESS => 'config/main',
817 WRAPPER => 'site/wrapper',
818 ERROR => 'error.tt2',
819 TIMER => 0
820 });
821
822=over
823
b1a08fe1 824=item
cb93c9d7 825
826INCLUDE_PATH defines the directories that Template Toolkit should search
827for the template files.
828
829=item
830
831PRE_PROCESS is used to process configuration options which are common to
832every template file.
833
834=item
835
836WRAPPER is a file which is processed with each template, usually used to
837easily provide a common header and footer for every page.
838
839=back
840
841In addition to setting these options, the TTSite helper also created the
842template and config files for us! In the 'root' directory, you'll notice
843two new directories: src and lib.
844
845Several configuration files in root/lib/config are called by PRE_PROCESS.
846
847The files in root/lib/site are the site-wide templates, called by
848WRAPPER, and display the html framework, control the layout, and provide
849the templates for the header and footer of your page. Using the template
850organization provided makes it much easier to standardize pages and make
851changes when they are (inevitably) needed.
852
853The template files that you will create for your application will go
5336f546 854into root/src, and you don't need to worry about putting the <html>
cb93c9d7 855or <head> sections; just put in the content. The WRAPPER will the rest
856of the page around your template for you.
857
858
859=head3 $c->stash
860
861Of course, having the template system include the header and footer for
862you isn't all that we want our templates to do. We need to be able to
863put data into our templates, and have it appear where and how we want
864it, right? That's where the stash comes in.
865
866In our controllers, we can add data to the stash, and then access it
867from the template. For instance:
868
869 sub hello : Local {
870 my ( $self, $c ) = @_;
871
872 $c->stash->{name} = 'Adam';
873
874 $c->stash->{template} = 'hello.tt';
875
876 $c->forward( $c->view('TT') );
877 }
878
879Then, in hello.tt:
880
881 <strong>Hello, [% name %]!</strong>
882
883When you view this page, it will display "Hello, Adam!"
884
885All of the information in your stash is available, by its name/key, in
886your templates. And your data don't have to be plain, old, boring
887scalars. You can pass array references and hash references, too.
888
889In your controller:
890
891 sub hello : Local {
892 my ( $self, $c ) = @_;
893
894 $c->stash->{names} = [ 'Adam', 'Dave', 'John' ];
895
896 $c->stash->{template} = 'hello.tt';
897
898 $c->forward( $c->view('TT') );
899 }
900
901In hello.tt:
902
903 [% FOREACH name IN names %]
904 <strong>Hello, [% name %]!</strong><br />
905 [% END %]
906
907This allowed us to loop through each item in the arrayref, and display a
908line for each name that we have.
909
910This is the most basic usage, but Template Toolkit is quite powerful,
911and allows you to truly keep your presentation logic separate from the
912rest of your application.
913
914=head3 $c->uri_for()
915
916One of my favorite things about Catalyst is the ability to move an
917application around without having to worry that everything is going to
918break. One of the areas that used to be a problem was with the http
919links in your template files. For example, suppose you have an
920application installed at http://www.domain.com/Calendar. The links point
921to "/Calendar", "/Calendar/2005", "/Calendar/2005/10", etc. If you move
922the application to be at http://www.mydomain.com/Tools/Calendar, then
923all of those links will suddenly break.
924
925That's where $c->uri_for() comes in. This function will merge its
926parameters with either the base location for the app, or its current
927namespace. Let's take a look at a couple of examples.
928
929In your template, you can use the following:
930
931 <a href="[% c.uri_for('/login') %]">Login Here</a>
932
c718cfb6 933Although the parameter starts with a forward slash, this is relative
934to the application root, not the webserver root. This is important to
935remember. So, if your application is installed at
936http://www.domain.com/Calendar, then the link would be
937http://www.mydomain.com/Calendar/Login. If you move your application
938to a different domain or path, then that link will still be correct.
cb93c9d7 939
940Likewise,
941
942 <a href="[% c.uri_for('2005','10', '24') %]">October, 24 2005</a>
943
c718cfb6 944The first parameter does NOT have a forward slash, and so it will be
945relative to the current namespace. If the application is installed at
946http://www.domain.com/Calendar. and if the template is called from
947MyApp::Controller::Display, then the link would become
948http://www.domain.com/Calendar/Display/2005/10/24.
949
950If you want to link to a parent uri of your current namespace you can
951prefix the arguments with multiple '../':
952
953 <a href="[% c.uri_for('../../view', stashed_object.id) %]">User view</a>
cb93c9d7 954
c718cfb6 955Once again, this allows you to move your application around without
956having to worry about broken links. But there's something else, as
957well. Since the links are generated by uri_for, you can use the same
958template file by several different controllers, and each controller
959will get the links that its supposed to. Since we believe in Don't
960Repeat Yourself, this is particularly helpful if you have common
961elements in your site that you want to keep in one file.
cb93c9d7 962
963Further Reading:
964
965L<http://search.cpan.org/perldoc?Catalyst>
966
967L<http://search.cpan.org/perldoc?Catalyst%3A%3AView%3A%3ATT>
968
969L<http://search.cpan.org/perldoc?Template>
970
b1a08fe1 971=head2 Adding RSS feeds
cb93c9d7 972
973Adding RSS feeds to your Catalyst applications is simple. We'll see two
433f1ad4 974different approaches here, but the basic premise is that you forward to
cb93c9d7 975the normal view action first to get the objects, then handle the output
976differently.
977
cb93c9d7 978=head3 Using XML::Feed
979
32cebe9b 980Assuming we have a C<view> action that populates
cb93c9d7 981'entries' with some DBIx::Class iterator, the code would look something
982like this:
983
984 sub rss : Local {
985 my ($self,$c) = @_;
986 $c->forward('view'); # get the entries
987
988 my $feed = XML::Feed->new('RSS');
989 $feed->title( $c->config->{name} . ' RSS Feed' );
990 $feed->link( $c->req->base ); # link to the site.
991 $feed->description('Catalyst advent calendar'); Some description
992
993 # Process the entries
994 while( my $entry = $c->stash->{entries}->next ) {
995 my $feed_entry = XML::Feed::Entry->new('RSS');
996 $feed_entry->title($entry->title);
997 $feed_entry->link( $c->uri_for($entry->link) );
998 $feed_entry->issued( DateTime->from_epoch(epoch => $entry->created) );
999 $feed->add_entry($feed_entry);
1000 }
1001 $c->res->body( $feed->as_xml );
1002 }
1003
32cebe9b 1004With this approach you're
b1a08fe1 1005pretty sure to get something that validates.
cb93c9d7 1006
433f1ad4 1007Note that for both of the above approaches, you'll need to set the
cb93c9d7 1008content type like this:
1009
1010 $c->res->content_type('application/rss+xml');
1011
1012=head3 Final words
1013
1014You could generalize the second variant easily by replacing 'RSS' with a
1015variable, so you can generate Atom feeds with the same code.
1016
1017Now, go ahead and make RSS feeds for all your stuff. The world *needs*
1018updates on your goldfish!
1019
1020=head2 Forcing the browser to download content
1021
1022Sometimes you need your application to send content for download. For
1023example, you can generate a comma-separated values (CSV) file for your
1024users to download and import into their spreadsheet program.
1025
1026Let's say you have an C<Orders> controller which generates a CSV file
1027in the C<export> action (i.e., C<http://localhost:3000/orders/export>):
1028
1029 sub export : Local Args(0) {
1030 my ( $self, $c ) = @_;
1031
1032 # In a real application, you'd generate this from the database
1033 my $csv = "1,5.99\n2,29.99\n3,3.99\n";
1034
1035 $c->res->content_type('text/comma-separated-values');
1036 $c->res->body($csv);
1037 }
1038
1039Normally the browser uses the last part of the URI to generate a
1040filename for data it cannot display. In this case your browser would
1041likely ask you to save a file named C<export>.
1042
1043Luckily you can have the browser download the content with a specific
1044filename by setting the C<Content-Disposition> header:
1045
1046 my $filename = 'Important Orders.csv';
1047 $c->res->header('Content-Disposition', qq[attachment; filename="$filename"]);
1048
1049Note the use of quotes around the filename; this ensures that any
1050spaces in the filename are handled by the browser.
1051
1052Put this right before calling C<< $c->res->body >> and your browser
1053will download a file named C<Important Orders.csv> instead of
1054C<export>.
1055
1056You can also use this to have the browser download content which it
1057normally displays, such as JPEG images or even HTML. Just be sure to
1058set the appropriate content type and disposition.
1059
1060
1061=head1 Controllers
1062
1063Controllers are the main point of communication between the web server
1064and your application. Here we explore some aspects of how they work.
1065
cb93c9d7 1066=head2 Action Types
1067
1068=head3 Introduction
1069
c718cfb6 1070A Catalyst application is driven by one or more Controller
1071modules. There are a number of ways that Catalyst can decide which of
1072the methods in your controller modules it should call. Controller
1073methods are also called actions, because they determine how your
1074catalyst application should (re-)act to any given URL. When the
1075application is started up, catalyst looks at all your actions, and
1076decides which URLs they map to.
cb93c9d7 1077
1078=head3 Type attributes
1079
1080Each action is a normal method in your controller, except that it has an
a7b39a0c 1081L<attribute|attributes>
cb93c9d7 1082attached. These can be one of several types.
1083
1084Assume our Controller module starts with the following package declaration:
1085
1086 package MyApp::Controller::Buckets;
1087
1088and we are running our application on localhost, port 3000 (the test
1089server default).
1090
1091=over 4
1092
1093=item Path
1094
1095A Path attribute also takes an argument, this can be either a relative
c718cfb6 1096or an absolute path. A relative path will be relative to the
1097controller namespace, an absolute path will represent an exact
1098matching URL.
cb93c9d7 1099
1100 sub my_handles : Path('handles') { .. }
1101
1102becomes
1103
1104 http://localhost:3000/buckets/handles
1105
1106and
1107
1108 sub my_handles : Path('/handles') { .. }
1109
b1a08fe1 1110becomes
cb93c9d7 1111
1112 http://localhost:3000/handles
1113
6daaedc0 1114See also: L<Catalyst::DispatchType::Path>
1115
cb93c9d7 1116=item Local
1117
c718cfb6 1118When using a Local attribute, no parameters are needed, instead, the
1119name of the action is matched in the URL. The namespaces created by
1120the name of the controller package is always part of the URL.
cb93c9d7 1121
1122 sub my_handles : Local { .. }
1123
1124becomes
1125
1126 http://localhost:3000/buckets/my_handles
1127
1128=item Global
1129
c718cfb6 1130A Global attribute is similar to a Local attribute, except that the
1131namespace of the controller is ignored, and matching starts at root.
cb93c9d7 1132
1133 sub my_handles : Global { .. }
1134
1135becomes
1136
1137 http://localhost:3000/my_handles
1138
1139=item Regex
1140
c718cfb6 1141By now you should have figured that a Regex attribute is just what it
1142sounds like. This one takes a regular expression, and matches starting
1143from root. These differ from the rest as they can match multiple URLs.
cb93c9d7 1144
1145 sub my_handles : Regex('^handles') { .. }
1146
1147matches
1148
1149 http://localhost:3000/handles
1150
b1a08fe1 1151and
cb93c9d7 1152
1153 http://localhost:3000/handles_and_other_parts
1154
1155etc.
1156
6daaedc0 1157See also: L<Catalyst::DispatchType::Regex>
1158
cb93c9d7 1159=item LocalRegex
1160
1161A LocalRegex is similar to a Regex, except it only matches below the current
1162controller namespace.
1163
1164 sub my_handles : LocalRegex(^handles') { .. }
1165
1166matches
1167
1168 http://localhost:3000/buckets/handles
1169
1170and
1171
1172 http://localhost:3000/buckets/handles_and_other_parts
1173
1174etc.
1175
6daaedc0 1176=item Chained
1177
1178See L<Catalyst::DispatchType::Chained> for a description of how the chained
1179dispatch type works.
1180
cb93c9d7 1181=item Private
1182
c718cfb6 1183Last but not least, there is the Private attribute, which allows you
1184to create your own internal actions, which can be forwarded to, but
1185won't be matched as URLs.
cb93c9d7 1186
1187 sub my_handles : Private { .. }
1188
1189becomes nothing at all..
1190
c718cfb6 1191Catalyst also predefines some special Private actions, which you can
1192override, these are:
cb93c9d7 1193
1194=over 4
1195
1196=item default
1197
c718cfb6 1198The default action will be called, if no other matching action is
1199found. If you don't have one of these in your namespace, or any sub
1200part of your namespace, you'll get an error page instead. If you want
1201to find out where it was the user was trying to go, you can look in
1202the request object using C<< $c->req->path >>.
cb93c9d7 1203
85d49fb6 1204 sub default :Path { .. }
cb93c9d7 1205
c718cfb6 1206works for all unknown URLs, in this controller namespace, or every one
1207if put directly into MyApp.pm.
cb93c9d7 1208
b1a08fe1 1209=item index
cb93c9d7 1210
c718cfb6 1211The index action is called when someone tries to visit the exact
1212namespace of your controller. If index, default and matching Path
1213actions are defined, then index will be used instead of default and
1214Path.
cb93c9d7 1215
85d49fb6 1216 sub index :Path :Args(0) { .. }
cb93c9d7 1217
1218becomes
1219
1220 http://localhost:3000/buckets
1221
1222=item begin
1223
c718cfb6 1224The begin action is called at the beginning of every request involving
1225this namespace directly, before other matching actions are called. It
1226can be used to set up variables/data for this particular part of your
1227app. A single begin action is called, its always the one most relevant
1228to the current namespace.
cb93c9d7 1229
1230 sub begin : Private { .. }
1231
b1a08fe1 1232is called once when
cb93c9d7 1233
1234 http://localhost:3000/bucket/(anything)?
1235
1236is visited.
1237
1238=item end
1239
c718cfb6 1240Like begin, this action is always called for the namespace it is in,
1241after every other action has finished. It is commonly used to forward
1242processing to the View component. A single end action is called, its
1243always the one most relevant to the current namespace.
cb93c9d7 1244
1245
1246 sub end : Private { .. }
1247
1248is called once after any actions when
1249
1250 http://localhost:3000/bucket/(anything)?
1251
1252is visited.
1253
1254=item auto
1255
c718cfb6 1256Lastly, the auto action is magic in that B<every> auto action in the
1257chain of paths up to and including the ending namespace, will be
1258called. (In contrast, only one of the begin/end/default actions will
1259be called, the relevant one).
cb93c9d7 1260
b1a08fe1 1261 package MyApp::Controller::Root;
cb93c9d7 1262 sub auto : Private { .. }
1263
b1a08fe1 1264and
cb93c9d7 1265
1266 sub auto : Private { .. }
1267
b1a08fe1 1268will both be called when visiting
cb93c9d7 1269
1270 http://localhost:3000/bucket/(anything)?
1271
1272=back
1273
1274=back
1275
1276=head3 A word of warning
1277
b1a08fe1 1278You can put root actions in your main MyApp.pm file, but this is deprecated,
1279please put your actions into your Root controller.
cb93c9d7 1280
7cfce6e1 1281=head3 Flowchart
cb93c9d7 1282
7cfce6e1 1283A graphical flowchart of how the dispatcher works can be found on the wiki at
1284L<http://dev.catalyst.perl.org/attachment/wiki/WikiStart/catalyst-flow.png>.
cb93c9d7 1285
7cfce6e1 1286=head2 DRY Controllers with Chained actions
6daaedc0 1287
1288Imagine that you would like the following paths in your application:
1289
1290=over
1291
1b4425c5 1292=item B<< /cd/<ID>/track/<ID> >>
6daaedc0 1293
1294Displays info on a particular track.
b1a08fe1 1295
6daaedc0 1296In the case of a multi-volume CD, this is the track sequence.
1297
1b4425c5 1298=item B<< /cd/<ID>/volume/<ID>/track/<ID> >>
6daaedc0 1299
1300Displays info on a track on a specific volume.
1301
1302=back
1303
1304Here is some example code, showing how to do this with chained controllers:
1305
1306 package CD::Controller;
1307 use base qw/Catalyst::Controller/;
b1a08fe1 1308
6daaedc0 1309 sub root : Chained('/') PathPart('/cd') CaptureArgs(1) {
1310 my ($self, $c, $cd_id) = @_;
1311 $c->stash->{cd_id} = $cd_id;
1312 $c->stash->{cd} = $self->model('CD')->find_by_id($cd_id);
1313 }
b1a08fe1 1314
6daaedc0 1315 sub trackinfo : Chained('track') PathPart('') Args(0) RenderView {
1316 my ($self, $c) = @_;
1317 }
b1a08fe1 1318
6daaedc0 1319 package CD::Controller::ByTrackSeq;
1320 use base qw/CD::Controller/;
b1a08fe1 1321
6daaedc0 1322 sub track : Chained('root') PathPart('track') CaptureArgs(1) {
1323 my ($self, $c, $track_seq) = @_;
1324 $c->stash->{track} = $self->stash->{cd}->find_track_by_seq($track_seq);
1325 }
b1a08fe1 1326
6daaedc0 1327 package CD::Controller::ByTrackVolNo;
1328 use base qw/CD::Controller/;
b1a08fe1 1329
6daaedc0 1330 sub volume : Chained('root') PathPart('volume') CaptureArgs(1) {
1331 my ($self, $c, $volume) = @_;
1332 $c->stash->{volume} = $volume;
1333 }
b1a08fe1 1334
6daaedc0 1335 sub track : Chained('volume') PathPart('track') CaptureArgs(1) {
1336 my ($self, $c, $track_no) = @_;
1337 $c->stash->{track} = $self->stash->{cd}->find_track_by_vol_and_track_no(
1338 $c->stash->{volume}, $track_no
1339 );
1340 }
b1a08fe1 1341
1342Note that adding other actions (i.e. chain endpoints) which operate on a track
6daaedc0 1343is simply a matter of adding a new sub to CD::Controller - no code is duplicated,
1344even though there are two different methods of looking up a track.
1345
1346This technique can be expanded as needed to fulfil your requirements - for example,
1347if you inherit the first action of a chain from a base class, then mixing in a
433f1ad4 1348different base class can be used to duplicate an entire URL hierarchy at a different
6daaedc0 1349point within your application.
1350
cb93c9d7 1351=head2 Component-based Subrequests
1352
1353See L<Catalyst::Plugin::SubRequest>.
1354
1355=head2 File uploads
1356
1357=head3 Single file upload with Catalyst
1358
1359To implement uploads in Catalyst, you need to have a HTML form similar to
1360this:
1361
1362 <form action="/upload" method="post" enctype="multipart/form-data">
1363 <input type="hidden" name="form_submit" value="yes">
1364 <input type="file" name="my_file">
1365 <input type="submit" value="Send">
1366 </form>
1367
1368It's very important not to forget C<enctype="multipart/form-data"> in
1369the form.
1370
1371Catalyst Controller module 'upload' action:
1372
1373 sub upload : Global {
1374 my ($self, $c) = @_;
1375
1376 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
1377
1378 if ( my $upload = $c->request->upload('my_file') ) {
1379
1380 my $filename = $upload->filename;
1381 my $target = "/tmp/upload/$filename";
1382
1383 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
1384 die( "Failed to copy '$filename' to '$target': $!" );
1385 }
1386 }
1387 }
1388
1389 $c->stash->{template} = 'file_upload.html';
1390 }
1391
1392=head3 Multiple file upload with Catalyst
1393
1394Code for uploading multiple files from one form needs a few changes:
1395
1396The form should have this basic structure:
1397
1398 <form action="/upload" method="post" enctype="multipart/form-data">
1399 <input type="hidden" name="form_submit" value="yes">
1400 <input type="file" name="file1" size="50"><br>
1401 <input type="file" name="file2" size="50"><br>
1402 <input type="file" name="file3" size="50"><br>
1403 <input type="submit" value="Send">
1404 </form>
1405
1406And in the controller:
1407
1408 sub upload : Local {
1409 my ($self, $c) = @_;
1410
1411 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
1412
1413 for my $field ( $c->req->upload ) {
1414
1415 my $upload = $c->req->upload($field);
1416 my $filename = $upload->filename;
1417 my $target = "/tmp/upload/$filename";
1418
1419 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
1420 die( "Failed to copy '$filename' to '$target': $!" );
1421 }
1422 }
1423 }
1424
1425 $c->stash->{template} = 'file_upload.html';
1426 }
1427
1428C<for my $field ($c-E<gt>req->upload)> loops automatically over all file
1429input fields and gets input names. After that is basic file saving code,
1430just like in single file upload.
1431
1432Notice: C<die>ing might not be what you want to do, when an error
1433occurs, but it works as an example. A better idea would be to store
1434error C<$!> in $c->stash->{error} and show a custom error template
1435displaying this message.
1436
1437For more information about uploads and usable methods look at
1438L<Catalyst::Request::Upload> and L<Catalyst::Request>.
1439
1440=head2 Forwarding with arguments
1441
1442Sometimes you want to pass along arguments when forwarding to another
1443action. As of version 5.30, arguments can be passed in the call to
1444C<forward>; in earlier versions, you can manually set the arguments in
1445the Catalyst Request object:
1446
1447 # version 5.30 and later:
1448 $c->forward('/wherever', [qw/arg1 arg2 arg3/]);
1449
1450 # pre-5.30
1451 $c->req->args([qw/arg1 arg2 arg3/]);
1452 $c->forward('/wherever');
1453
b1a08fe1 1454(See the L<Catalyst::Manual::Intro> Flow_Control section for more
cb93c9d7 1455information on passing arguments via C<forward>.)
1456
6daaedc0 1457=head2 Chained dispatch using base classes, and inner packages.
1458
1459 package MyApp::Controller::Base;
1460 use base qw/Catalyst::Controller/;
1461
b1a08fe1 1462 sub key1 : Chained('/')
31e9c5ef 1463
e2728084 1464=head2 Extending RenderView (formerly DefaultEnd)
1465
1466The recommended approach for an C<end> action is to use
1467L<Catalyst::Action::RenderView> (taking the place of
1468L<Catalyst::Plugin::DefaultEnd>), which does what you usually need.
1469However there are times when you need to add a bit to it, but don't want
1470to write your own C<end> action.
1471
1472You can extend it like this:
1473
1474To add something to an C<end> action that is called before rendering
1475(this is likely to be what you want), simply place it in the C<end>
1476method:
1477
1478 sub end : ActionClass('RenderView') {
1479 my ( $self, $c ) = @_;
1480 # do stuff here; the RenderView action is called afterwards
1481 }
1482
1483To add things to an C<end> action that are called I<after> rendering,
1484you can set it up like this:
1485
1486 sub render : ActionClass('RenderView') { }
1487
1488 sub end : Private {
1489 my ( $self, $c ) = @_;
1490 $c->forward('render');
1491 # do stuff here
1492 }
1493
31e9c5ef 1494
cb93c9d7 1495=head2 Serving static content
1496
1497Serving static content in Catalyst used to be somewhat tricky; the use
1498of L<Catalyst::Plugin::Static::Simple> makes everything much easier.
1499This plugin will automatically serve your static content during development,
1500but allows you to easily switch to Apache (or other server) in a
1501production environment.
1502
1503=head3 Introduction to Static::Simple
1504
1505Static::Simple is a plugin that will help to serve static content for your
1506application. By default, it will serve most types of files, excluding some
1507standard Template Toolkit extensions, out of your B<root> file directory. All
1508files are served by path, so if B<images/me.jpg> is requested, then
1509B<root/images/me.jpg> is found and served.
1510
1511=head3 Usage
1512
1513Using the plugin is as simple as setting your use line in MyApp.pm to include:
1514
b411df01 1515 use Catalyst qw/Static::Simple/;
cb93c9d7 1516
1517and already files will be served.
1518
1519=head3 Configuring
1520
1521Static content is best served from a single directory within your root
1522directory. Having many different directories such as C<root/css> and
1523C<root/images> requires more code to manage, because you must separately
1524identify each static directory--if you decide to add a C<root/js>
1525directory, you'll need to change your code to account for it. In
1526contrast, keeping all static directories as subdirectories of a main
1527C<root/static> directory makes things much easier to manage. Here's an
1528example of a typical root directory structure:
1529
1530 root/
1531 root/content.tt
1532 root/controller/stuff.tt
1533 root/header.tt
1534 root/static/
1535 root/static/css/main.css
1536 root/static/images/logo.jpg
1537 root/static/js/code.js
1538
1539
1540All static content lives under C<root/static>, with everything else being
1541Template Toolkit files.
1542
1543=over 4
1544
1545=item Include Path
1546
1547You may of course want to change the default locations, and make
1548Static::Simple look somewhere else, this is as easy as:
1549
19a5b486 1550 MyApp->config(
1551 static => {
1552 include_path => [
1553 MyApp->path_to('/'),
1554 '/path/to/my/files',
1555 ],
1556 },
1557 );
cb93c9d7 1558
1559When you override include_path, it will not automatically append the
1560normal root path, so you need to add it yourself if you still want
1561it. These will be searched in order given, and the first matching file
1562served.
1563
1564=item Static directories
1565
1566If you want to force some directories to be only static, you can set
1567them using paths relative to the root dir, or regular expressions:
1568
19a5b486 1569 MyApp->config(
1570 static => {
1571 dirs => [
1572 'static',
1573 qr/^(images|css)/,
1574 ],
1575 },
1576 );
cb93c9d7 1577
1578=item File extensions
1579
1580By default, the following extensions are not served (that is, they will
1581be processed by Catalyst): B<tmpl, tt, tt2, html, xhtml>. This list can
1582be replaced easily:
1583
19a5b486 1584 MyApp->config(
1585 static => {
1586 ignore_extensions => [
1587 qw/tmpl tt tt2 html xhtml/
1588 ],
1589 },
1590 );
cb93c9d7 1591
1592=item Ignoring directories
1593
1594Entire directories can be ignored. If used with include_path,
1595directories relative to the include_path dirs will also be ignored:
1596
19a5b486 1597 MyApp->config( static => {
1598 ignore_dirs => [ qw/tmpl css/ ],
1599 });
cb93c9d7 1600
1601=back
1602
1603=head3 More information
1604
1605L<http://search.cpan.org/dist/Catalyst-Plugin-Static-Simple/>
1606
1607=head3 Serving manually with the Static plugin with HTTP::Daemon (myapp_server.pl)
1608
1609In some situations you might want to control things more directly,
1610using L<Catalyst::Plugin::Static>.
1611
1612In your main application class (MyApp.pm), load the plugin:
1613
b411df01 1614 use Catalyst qw/-Debug FormValidator Static OtherPlugin/;
cb93c9d7 1615
1616You will also need to make sure your end method does I<not> forward
1617static content to the view, perhaps like this:
1618
1619 sub end : Private {
1620 my ( $self, $c ) = @_;
1621
b1a08fe1 1622 $c->forward( 'MyApp::View::TT' )
cb93c9d7 1623 unless ( $c->res->body || !$c->stash->{template} );
1624 }
1625
1626This code will only forward to the view if a template has been
1627previously defined by a controller and if there is not already data in
1628C<$c-E<gt>res-E<gt>body>.
1629
1630Next, create a controller to handle requests for the /static path. Use
1631the Helper to save time. This command will create a stub controller as
1632C<lib/MyApp/Controller/Static.pm>.
1633
1634 $ script/myapp_create.pl controller Static
1635
1636Edit the file and add the following methods:
1637
1638 # serve all files under /static as static files
1639 sub default : Path('/static') {
1640 my ( $self, $c ) = @_;
1641
1642 # Optional, allow the browser to cache the content
1643 $c->res->headers->header( 'Cache-Control' => 'max-age=86400' );
1644
1645 $c->serve_static; # from Catalyst::Plugin::Static
1646 }
1647
1648 # also handle requests for /favicon.ico
1649 sub favicon : Path('/favicon.ico') {
1650 my ( $self, $c ) = @_;
1651
1652 $c->serve_static;
1653 }
1654
1655You can also define a different icon for the browser to use instead of
1656favicon.ico by using this in your HTML header:
1657
1658 <link rel="icon" href="/static/myapp.ico" type="image/x-icon" />
1659
1660=head3 Common problems with the Static plugin
1661
1662The Static plugin makes use of the C<shared-mime-info> package to
1663automatically determine MIME types. This package is notoriously
1664difficult to install, especially on win32 and OS X. For OS X the easiest
1665path might be to install Fink, then use C<apt-get install
1666shared-mime-info>. Restart the server, and everything should be fine.
1667
1668Make sure you are using the latest version (>= 0.16) for best
1669results. If you are having errors serving CSS files, or if they get
1670served as text/plain instead of text/css, you may have an outdated
1671shared-mime-info version. You may also wish to simply use the following
1672code in your Static controller:
1673
1674 if ($c->req->path =~ /css$/i) {
1675 $c->serve_static( "text/css" );
1676 } else {
1677 $c->serve_static;
1678 }
1679
1680=head3 Serving Static Files with Apache
1681
1682When using Apache, you can bypass Catalyst and any Static
1683plugins/controllers controller by intercepting requests for the
1684C<root/static> path at the server level. All that is required is to
1685define a DocumentRoot and add a separate Location block for your static
1686content. Here is a complete config for this application under mod_perl
16871.x:
1688
1689 <Perl>
1690 use lib qw(/var/www/MyApp/lib);
1691 </Perl>
1692 PerlModule MyApp
1693
1694 <VirtualHost *>
1695 ServerName myapp.example.com
1696 DocumentRoot /var/www/MyApp/root
1697 <Location />
1698 SetHandler perl-script
1699 PerlHandler MyApp
1700 </Location>
1701 <LocationMatch "/(static|favicon.ico)">
1702 SetHandler default-handler
1703 </LocationMatch>
1704 </VirtualHost>
1705
1706And here's a simpler example that'll get you started:
1707
1708 Alias /static/ "/my/static/files/"
1709 <Location "/static">
1710 SetHandler none
1711 </Location>
1712
1713=head2 Caching
1714
1715Catalyst makes it easy to employ several different types of caching to
1716speed up your applications.
1717
1718=head3 Cache Plugins
1719
1720There are three wrapper plugins around common CPAN cache modules:
1721Cache::FastMmap, Cache::FileCache, and Cache::Memcached. These can be
1722used to cache the result of slow operations.
1723
ca7528df 1724The Catalyst Advent Calendar uses the FileCache plugin to cache the
cb93c9d7 1725rendered XHTML version of the source POD document. This is an ideal
ca7528df 1726application for a cache because the source document changes
1727infrequently but may be viewed many times.
cb93c9d7 1728
b411df01 1729 use Catalyst qw/Cache::FileCache/;
b1a08fe1 1730
cb93c9d7 1731 ...
b1a08fe1 1732
cb93c9d7 1733 use File::stat;
1734 sub render_pod : Local {
1735 my ( self, $c ) = @_;
b1a08fe1 1736
cb93c9d7 1737 # the cache is keyed on the filename and the modification time
1738 # to check for updates to the file.
1739 my $file = $c->path_to( 'root', '2005', '11.pod' );
1740 my $mtime = ( stat $file )->mtime;
b1a08fe1 1741
cb93c9d7 1742 my $cached_pod = $c->cache->get("$file $mtime");
1743 if ( !$cached_pod ) {
1744 $cached_pod = do_slow_pod_rendering();
1745 # cache the result for 12 hours
1746 $c->cache->set( "$file $mtime", $cached_pod, '12h' );
1747 }
1748 $c->stash->{pod} = $cached_pod;
1749 }
b1a08fe1 1750
cb93c9d7 1751We could actually cache the result forever, but using a value such as 12 hours
1752allows old entries to be automatically expired when they are no longer needed.
1753
1754=head3 Page Caching
1755
1756Another method of caching is to cache the entire HTML page. While this is
5abded07 1757traditionally handled by a frontend proxy server like Squid, the Catalyst
cb93c9d7 1758PageCache plugin makes it trivial to cache the entire output from
1759frequently-used or slow actions.
1760
1761Many sites have a busy content-filled front page that might look something
1762like this. It probably takes a while to process, and will do the exact same
1763thing for every single user who views the page.
1764
1765 sub front_page : Path('/') {
1766 my ( $self, $c ) = @_;
b1a08fe1 1767
cb93c9d7 1768 $c->forward( 'get_news_articles' );
1769 $c->forward( 'build_lots_of_boxes' );
1770 $c->forward( 'more_slow_stuff' );
b1a08fe1 1771
cb93c9d7 1772 $c->stash->{template} = 'index.tt';
1773 }
1774
1775We can add the PageCache plugin to speed things up.
1776
b411df01 1777 use Catalyst qw/Cache::FileCache PageCache/;
b1a08fe1 1778
cb93c9d7 1779 sub front_page : Path ('/') {
1780 my ( $self, $c ) = @_;
b1a08fe1 1781
cb93c9d7 1782 $c->cache_page( 300 );
b1a08fe1 1783
cb93c9d7 1784 # same processing as above
1785 }
b1a08fe1 1786
cb93c9d7 1787Now the entire output of the front page, from <html> to </html>, will be
1788cached for 5 minutes. After 5 minutes, the next request will rebuild the
1789page and it will be re-cached.
1790
1791Note that the page cache is keyed on the page URI plus all parameters, so
1792requests for / and /?foo=bar will result in different cache items. Also,
1793only GET requests will be cached by the plugin.
1794
5abded07 1795You can even get that frontend Squid proxy to help out by enabling HTTP
cb93c9d7 1796headers for the cached page.
1797
19a5b486 1798 MyApp->config(
1799 page_cache => {
1800 set_http_headers => 1,
1801 },
1802 );
b1a08fe1 1803
cb93c9d7 1804This would now set the following headers so proxies and browsers may cache
1805the content themselves.
1806
1807 Cache-Control: max-age=($expire_time - time)
1808 Expires: $expire_time
1809 Last-Modified: $cache_created_time
b1a08fe1 1810
cb93c9d7 1811=head3 Template Caching
1812
1813Template Toolkit provides support for caching compiled versions of your
1814templates. To enable this in Catalyst, use the following configuration.
1815TT will cache compiled templates keyed on the file mtime, so changes will
1816still be automatically detected.
1817
1818 package MyApp::View::TT;
b1a08fe1 1819
cb93c9d7 1820 use strict;
1821 use warnings;
1822 use base 'Catalyst::View::TT';
b1a08fe1 1823
cb93c9d7 1824 __PACKAGE__->config(
1825 COMPILE_DIR => '/tmp/template_cache',
1826 );
b1a08fe1 1827
cb93c9d7 1828 1;
b1a08fe1 1829
cb93c9d7 1830=head3 More Info
1831
1832See the documentation for each cache plugin for more details and other
1833available configuration options.
1834
1835L<Catalyst::Plugin::Cache::FastMmap>
1836L<Catalyst::Plugin::Cache::FileCache>
1837L<Catalyst::Plugin::Cache::Memcached>
1838L<Catalyst::Plugin::PageCache>
1839L<http://search.cpan.org/dist/Template-Toolkit/lib/Template/Manual/Config.pod#Caching_and_Compiling_Options>
1840
1841=head1 Testing
1842
1843Testing is an integral part of the web application development
1844process. Tests make multi developer teams easier to coordinate, and
1845they help ensure that there are no nasty surprises after upgrades or
1846alterations.
1847
1848=head2 Testing
1849
b1a08fe1 1850Catalyst provides a convenient way of testing your application during
cb93c9d7 1851development and before deployment in a real environment.
1852
b1a08fe1 1853C<Catalyst::Test> makes it possible to run the same tests both locally
cb93c9d7 1854(without an external daemon) and against a remote server via HTTP.
1855
1856=head3 Tests
1857
1858Let's examine a skeleton application's C<t/> directory:
1859
1860 mundus:~/MyApp chansen$ ls -l t/
1861 total 24
1862 -rw-r--r-- 1 chansen chansen 95 18 Dec 20:50 01app.t
1863 -rw-r--r-- 1 chansen chansen 190 18 Dec 20:50 02pod.t
1864 -rw-r--r-- 1 chansen chansen 213 18 Dec 20:50 03podcoverage.t
1865
1866=over 4
1867
1868=item C<01app.t>
1869
1870Verifies that the application loads, compiles, and returns a successful
1871response.
1872
1873=item C<02pod.t>
1874
b1a08fe1 1875Verifies that all POD is free from errors. Only executed if the C<TEST_POD>
cb93c9d7 1876environment variable is true.
1877
1878=item C<03podcoverage.t>
1879
1880Verifies that all methods/functions have POD coverage. Only executed if the
1881C<TEST_POD> environment variable is true.
1882
1883=back
1884
1885=head3 Creating tests
1886
1887 mundus:~/MyApp chansen$ cat t/01app.t | perl -ne 'printf( "%2d %s", $., $_ )'
1888 1 use Test::More tests => 2;
e5415384 1889 2 BEGIN { use_ok( Catalyst::Test, 'MyApp' ) }
cb93c9d7 1890 3
1891 4 ok( request('/')->is_success );
1892
1893The first line declares how many tests we are going to run, in this case
1894two. The second line tests and loads our application in test mode. The
1895fourth line verifies that our application returns a successful response.
1896
1897C<Catalyst::Test> exports two functions, C<request> and C<get>. Each can
1898take three different arguments:
1899
1900=over 4
1901
1902=item A string which is a relative or absolute URI.
1903
1904 request('/my/path');
1905 request('http://www.host.com/my/path');
1906
1907=item An instance of C<URI>.
1908
1909 request( URI->new('http://www.host.com/my/path') );
1910
1911=item An instance of C<HTTP::Request>.
1912
1913 request( HTTP::Request->new( GET => 'http://www.host.com/my/path') );
1914
1915=back
1916
b1a08fe1 1917C<request> returns an instance of C<HTTP::Response> and C<get> returns the
cb93c9d7 1918content (body) of the response.
1919
1920=head3 Running tests locally
1921
1922 mundus:~/MyApp chansen$ CATALYST_DEBUG=0 TEST_POD=1 prove --lib lib/ t/
b1a08fe1 1923 t/01app............ok
1924 t/02pod............ok
1925 t/03podcoverage....ok
cb93c9d7 1926 All tests successful.
1927 Files=3, Tests=4, 2 wallclock secs ( 1.60 cusr + 0.36 csys = 1.96 CPU)
b1a08fe1 1928
cb93c9d7 1929C<CATALYST_DEBUG=0> ensures that debugging is off; if it's enabled you
1930will see debug logs between tests.
1931
1932C<TEST_POD=1> enables POD checking and coverage.
1933
1934C<prove> A command-line tool that makes it easy to run tests. You can
1935find out more about it from the links below.
1936
1937=head3 Running tests remotely
1938
1939 mundus:~/MyApp chansen$ CATALYST_SERVER=http://localhost:3000/ prove --lib lib/ t/01app.t
b1a08fe1 1940 t/01app....ok
cb93c9d7 1941 All tests successful.
1942 Files=1, Tests=2, 0 wallclock secs ( 0.40 cusr + 0.01 csys = 0.41 CPU)
1943
b1a08fe1 1944C<CATALYST_SERVER=http://localhost:3000/> is the absolute deployment URI of
1945your application. In C<CGI> or C<FastCGI> it should be the host and path
cb93c9d7 1946to the script.
1947
1948=head3 C<Test::WWW::Mechanize> and Catalyst
1949
1950Be sure to check out C<Test::WWW::Mechanize::Catalyst>. It makes it easy to
1951test HTML, forms and links. A short example of usage:
1952
1953 use Test::More tests => 6;
e5415384 1954 BEGIN { use_ok( Test::WWW::Mechanize::Catalyst, 'MyApp' ) }
cb93c9d7 1955
1956 my $mech = Test::WWW::Mechanize::Catalyst->new;
1957 $mech->get_ok("http://localhost/", 'Got index page');
1958 $mech->title_like( qr/^MyApp on Catalyst/, 'Got right index title' );
1959 ok( $mech->find_link( text_regex => qr/^Wiki/i ), 'Found link to Wiki' );
1960 ok( $mech->find_link( text_regex => qr/^Mailing-List/i ), 'Found link to Mailing-List' );
1961 ok( $mech->find_link( text_regex => qr/^IRC channel/i ), 'Found link to IRC channel' );
1962
1963=head3 Further Reading
1964
1965=over 4
1966
1967=item Catalyst::Test
1968
e5415384 1969L<Catalyst::Test>
cb93c9d7 1970
1971=item Test::WWW::Mechanize::Catalyst
1972
1973L<http://search.cpan.org/dist/Test-WWW-Mechanize-Catalyst/lib/Test/WWW/Mechanize/Catalyst.pm>
1974
1975=item Test::WWW::Mechanize
1976
1977L<http://search.cpan.org/dist/Test-WWW-Mechanize/Mechanize.pm>
1978
1979=item WWW::Mechanize
1980
1981L<http://search.cpan.org/dist/WWW-Mechanize/lib/WWW/Mechanize.pm>
1982
1983=item LWP::UserAgent
1984
1985L<http://search.cpan.org/dist/libwww-perl/lib/LWP/UserAgent.pm>
1986
1987=item HTML::Form
1988
1989L<http://search.cpan.org/dist/libwww-perl/lib/HTML/Form.pm>
1990
1991=item HTTP::Message
1992
1993L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Message.pm>
1994
1995=item HTTP::Request
1996
1997L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Request.pm>
1998
1999=item HTTP::Request::Common
2000
2001L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Request/Common.pm>
2002
2003=item HTTP::Response
2004
2005L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Response.pm>
2006
2007=item HTTP::Status
2008
2009L<http://search.cpan.org/dist/libwww-perl/lib/HTTP/Status.pm>
2010
2011=item URI
2012
2013L<http://search.cpan.org/dist/URI/URI.pm>
2014
2015=item Test::More
2016
2017L<http://search.cpan.org/dist/Test-Simple/lib/Test/More.pm>
2018
2019=item Test::Pod
2020
2021L<http://search.cpan.org/dist/Test-Pod/Pod.pm>
2022
2023=item Test::Pod::Coverage
2024
2025L<http://search.cpan.org/dist/Test-Pod-Coverage/Coverage.pm>
2026
2027=item prove (Test::Harness)
2028
2029L<http://search.cpan.org/dist/Test-Harness/bin/prove>
2030
2031=back
2032
2033=head3 More Information
2034
2035L<http://search.cpan.org/perldoc?Catalyst::Plugin::Authorization::Roles>
2036L<http://search.cpan.org/perldoc?Catalyst::Plugin::Authorization::ACL>
2037
2038=head1 AUTHORS
2039
bbddff00 2040Catalyst Contributors, see Catalyst.pm
cb93c9d7 2041
2042=head1 COPYRIGHT
2043
bbddff00 2044This library is free software. You can redistribute it and/or modify it under
2045the same terms as Perl itself.
cb93c9d7 2046
bbddff00 2047=cut