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