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