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