incorporated static discussion from andyg into Cookbook.pod
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Manual / Cookbook.pod
1 =head1 NAME
2
3 Catalyst::Manual::Cookbook - Cooking with Catalyst
4
5 =head1 DESCRIPTION
6
7 Yummy code like your mum used to bake!
8
9 =head1 RECIPES
10
11 =head2 Force debug screen
12
13 You can force Catalyst to display the debug screen at the end of the
14 request by placing a C<die()> call in the C<end> action.
15
16      sub end : Private {
17          my ( $self, $c ) = @_;
18          die "forced debug";
19      }
20
21 If you're tired of removing and adding this all the time, you
22 can add a condition in the C<end> action:
23
24     sub end : Private {
25         my ( $self, $c ) = @_;
26         die "forced debug" if $c->req->params->{dump_info};
27     }
28
29 Then just add to your query string C<"&dump_info=1">, or the like,
30 to force debug output.
31
32 =head2 Disable statistics
33
34 Just add this line to your application class if you don't want those nifty
35 statistics in your debug messages.
36
37     sub Catalyst::Log::info { }
38
39 =head2 Scaffolding
40
41 Scaffolding is very simple with Catalyst.
42 Just use Catalyst::Model::CDBI::CRUD as your base class.
43
44     # lib/MyApp/Model/CDBI.pm
45     package MyApp::Model::CDBI;
46
47     use strict;
48     use base 'Catalyst::Model::CDBI::CRUD';
49
50     __PACKAGE__->config(
51         dsn           => 'dbi:SQLite:/tmp/myapp.db',
52         relationships => 1
53     );
54
55     1;
56
57     # lib/MyApp.pm
58     package MyApp;
59
60     use Catalyst 'FormValidator';
61
62     __PACKAGE__->config(
63         name => 'My Application',
64         root => '/home/joeuser/myapp/root'
65     );
66
67     sub my_table : Global {
68         my ( $self, $c ) = @_;
69         $c->form( optional => [ MyApp::Model::CDBI::Table->columns ] );
70         $c->forward('MyApp::Model::CDBI::Table');
71     }
72
73     1;
74
75 Modify the $c->form() parameters to match your needs, and don't forget
76 to copy the templates into the template root. Can't find the templates?
77 They were in the CRUD model distribution, so you can do B<look
78 Catalyst::Model::CDBI::CRUD> from the CPAN shell to find them.
79
80 Other Scaffolding modules are in development at the time of writing.
81
82 =head2 Single file upload with Catalyst
83
84 To implement uploads in Catalyst you need to have a HTML form similiar to
85 this:
86
87     <form action="/upload" method="post" enctype="multipart/form-data">
88       <input type="hidden" name="form_submit" value="yes">
89       <input type="file" name="my_file">
90       <input type="submit" value="Send">
91     </form>
92
93 It's very important not to forget C<enctype="multipart/form-data"> in
94 the form.
95
96 Catalyst Controller module 'upload' action:
97
98     sub upload : Global {
99         my ($self, $c) = @_;
100
101         if ( $c->request->parameters->{form_submit} eq 'yes' ) {
102
103             if ( my $upload = $c->request->upload('my_file') ) {
104             
105                 my $filename = $upload->filename;
106                 my $target   = "/tmp/upload/$filename";
107                 
108                 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
109                     die( "Failed to copy '$filename' to '$target': $!" );
110                 }
111             }
112         }
113         
114         $c->stash->{template} = 'file_upload.html';
115     }
116
117 =head2 Multiple file upload with Catalyst
118
119 Code for uploading multiple files from one form needs a few changes:
120
121 The form should have this basic structure:
122
123     <form action="/upload" method="post" enctype="multipart/form-data">
124       <input type="hidden" name="form_submit" value="yes">
125       <input type="file" name="file1" size="50"><br>
126       <input type="file" name="file2" size="50"><br>
127       <input type="file" name="file3" size="50"><br>
128       <input type="submit" value="Send">
129     </form>
130
131 And in the Controller:
132
133     sub upload : Local {
134         my ($self, $c) = @_;
135
136         if ( $c->request->parameters->{form_submit} eq 'yes' ) {
137
138             for my $field ( $c->req->upload ) {
139
140                 my $upload   = $c->req->upload($field);
141                 my $filename = $upload->filename;
142                 my $target   = "/tmp/upload/$filename";
143                 
144                 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
145                     die( "Failed to copy '$filename' to '$target': $!" );
146                 }
147             }
148         }
149
150         $c->stash->{template} = 'file_upload.html';
151     }
152
153 C<for my $field ($c-E<gt>req->upload)> loops automatically over all file
154 input fields and gets input names. After that is basic file saving code,
155 just like in single file upload.
156
157 Notice: C<die>ing might not be what you want to do, when an error
158 occurs, but it works as an example. A better idea would be to store
159 error C<$!> in $c->stash->{error} and show a custom error template
160 displaying this message.
161
162 For more information about uploads and usable methods look at
163 L<Catalyst::Request::Upload> and L<Catalyst::Request>.
164
165 =head2 Authentication with Catalyst::Plugin::Authentication::CDBI
166
167 There are (at least) two ways to implement authentication with this plugin:
168 1) only checking username and password
169 2) checking username, password, and the roles the user has
170
171 For both variants you'll need the following code in your MyApp package:
172
173     use Catalyst qw/Session::FastMmap Static Authentication::CDBI/;
174
175     MyApp->config( authentication => { user_class => 'MyApp::M::MyApp::Users',
176                                        user_field => 'email',
177                                        password_field => 'password' });
178
179 'user_class' is a Class::DBI class for your users table.
180 'user_field' tells which field is used for username lookup (might be 
181 email, first name, surname etc.).
182 'password_field' is, well, password field in your table and by default 
183 password is stored in plain text. Authentication::CDBI looks for 'user' 
184 and 'password' fields in table, if they're not defined in the config.
185
186 In PostgreSQL, the users table might be something like:
187
188  CREATE TABLE users (
189    user_id   serial,
190    name      varchar(100),
191    surname   varchar(100),
192    password  varchar(100),
193    email     varchar(100),
194    primary key(user_id)
195  );
196
197 We'll discuss the first variant for now:
198 1. user:password login/auth without roles
199
200 To log in a user you might use an action like this:
201
202     sub login : Local {
203         my ($self, $c) = @_;
204         if ($c->req->params->{username}) {
205             $c->session_login($c->req->params->{username}, 
206                               $c->req->params->{password} );
207             if ($c->req->{user}) {
208                 $c->forward('?restricted_area');
209             }
210         }
211     }
212
213 This action should not go in your MyApp class...if it does, it will
214 conflict with the built-in method of the same name.  Instead, put it
215 in a Controller class.
216
217 $c->req->params->{username} and $c->req->params->{password} are html 
218 form parameters from a login form. If login succeeds, then 
219 $c->req->{user} contains the username of the authenticated user.
220
221 If you want to remember the user's login status in between further 
222 requests, then just use the C<$c-E<gt>session_login> method. Catalyst will 
223 create a session id and session cookie and automatically append session 
224 id to all urls. So all you have to do is just check $c->req->{user} 
225 where needed.
226
227 To log out a user, just call $c->session_logout.
228
229 Now let's take a look at the second variant:
230 2. user:password login/auth with roles
231
232 To use roles you need to add the following parameters to  MyApp->config in the 'authentication' section:
233
234     role_class      => 'MyApp::M::MyApp::Roles',
235     user_role_class => 'MyApp::M::MyApp::UserRoles',
236     user_role_user_field => 'user_id',
237     user_role_role_field => 'role_id',
238
239 Corresponding tables in PostgreSQL could look like this:
240
241  CREATE TABLE roles (
242    role_id  serial,
243    name     varchar(100),
244    primary key(role_id)
245  );
246
247  CREATE TABLE user_roles (
248    user_role_id  serial,
249    user_id       int,
250    role_id       int,
251    primary key(user_role_id),
252    foreign key(user_id) references users(user_id),
253    foreign key(role_id) references roles(role_id)
254  );
255
256 The 'roles' table is a list of role names and the 'user_role' table is 
257 used for the user -> role lookup.
258
259 Now if a logged-in user wants to see a location which is allowed only 
260 for people with an 'admin' role, in your controller you can check it 
261 with:
262
263     sub add : Local {
264         my ($self, $c) = @_;
265         if ($c->roles(qw/admin/)) {
266             $c->req->output("Your account has the role 'admin.'");
267         } else {
268             $c->req->output("You're not allowed to be here.");
269         }
270     }
271
272 One thing you might need is to forward non-authenticated users to a login 
273 form if they try to access restricted areas. If you want to do this 
274 controller-wide (if you have one controller for your admin section) then it's 
275 best to add a user check to a '!begin' action:
276
277     sub begin : Private {
278         my ($self, $c) = @_;
279         unless ($c->req->{user}) {
280             $c->req->action(undef);  ## notice this!!
281             $c->forward('?login');
282         }
283     }
284
285 Pay attention to $c->req->action(undef). This is needed because of the 
286 way $c->forward works - C<forward> to C<login> gets called, but after that 
287 Catalyst will still execute the action defined in the URI (e.g. if you 
288 tried to go to C</add>, then first 'begin' will forward to 'login', but after
289 that 'add' will nonetheless be executed). So $c->req->action(undef) undefines any 
290 actions that were to be called and forwards the user where we want him/her 
291 to be.
292
293 And this is all you need to do. 
294
295 =head2 Pass-through login (and other actions)
296
297 An easy way of having assorted actions that occur during the processing of
298 a request that are orthogonal to its actual purpose - logins, silent
299 commands etc. Provide actions for these, but when they're required for
300 something else fill e.g. a form variable __login and have a sub begin like so:
301
302 sub begin : Private {
303   my ($self, $c) = @_;
304   foreach my $action (qw/login docommand foo bar whatever/) {
305     if ($c->req->params->{"__${action}"}) {
306       $c->forward($action);
307     }
308   }
309 }
310
311 =head2 How to use Catalyst without mod_perl
312
313 Catalyst applications give optimum performance when run under mod_perl.
314 However sometimes mod_perl is not an option, and running under CGI is 
315 just too slow.  There's also an alternative to mod_perl that gives
316 reasonable performance named FastCGI.
317
318 B<Using FastCGI>
319
320 To quote from L<http://www.fastcgi.com/>: "FastCGI is a language 
321 independent, scalable, extension to CGI that provides high performance 
322 without the limitations of specific server APIs."  Web server support 
323 is provided for Apache in the form of C<mod_fastcgi> and there is Perl
324 support in the C<FCGI> module.  To convert a CGI Catalyst application 
325 to FastCGI one needs to initialize an C<FCGI::Request> object and loop 
326 while the C<Accept> method returns zero.  The following code shows how 
327 it is done - and it also works as a normal, single-shot CGI script.
328
329     #!/usr/bin/perl
330     use strict;
331     use FCGI;
332     use MyApp;
333
334     my $request = FCGI::Request();
335     while ($request->Accept() >= 0) {
336         MyApp->run;
337     }
338
339 Any initialization code should be included outside the request-accept 
340 loop.
341
342 There is one little complication, which is that C<MyApp-E<gt>run> outputs a
343 complete HTTP response including the status line (e.g.: 
344 "C<HTTP/1.1 200>").
345 FastCGI just wants a set of headers, so the sample code captures the 
346 output and  drops the first line if it is an HTTP status line (note: 
347 this may change).
348
349 The Apache C<mod_fastcgi> module is provided by a number of Linux 
350 distros and is straightforward to compile for most Unix-like systems.  
351 The module provides a FastCGI Process Manager, which manages FastCGI 
352 scripts.  You configure your script as a FastCGI script with the 
353 following Apache configuration directives:
354
355     <Location /fcgi-bin>
356        AddHandler fastcgi-script fcgi
357     </Location>
358
359 or:
360
361     <Location /fcgi-bin>
362        SetHandler fastcgi-script
363        Action fastcgi-script /path/to/fcgi-bin/fcgi-script
364     </Location>
365
366 C<mod_fastcgi> provides a number of options for controlling the FastCGI
367 scripts spawned; it also allows scripts to be run to handle the
368 authentication, authorization, and access check phases.
369
370 For more information see the FastCGI documentation, the C<FCGI> module 
371 and L<http://www.fastcgi.com/>.
372
373 =head2 Serving static content
374
375 Serving static content in Catalyst can be somewhat tricky; this recipe
376 shows one possible solution. Using this recipe will serve all static
377 content through Catalyst when developing with the built-in HTTP::Daemon
378 server, and will make it easy to use Apache to serve the content when
379 your app goes into production.
380
381 Static content is best served from a single directory within your root
382 directory. Having many different directories such as C<root/css> and
383 C<root/images> requires more code to manage, because you must separately
384 identify each static directory--if you decide to add a C<root/js>
385 directory, you'll need to change your code to account for it. In
386 contrast, keeping all static directories as subdirectories of a main
387 C<root/static> directory makes things much easier to manager. Here's an
388 example of a typical root directory structure:
389
390     root/
391     root/content.tt
392     root/controller/stuff.tt
393     root/header.tt
394     root/static/
395     root/static/css/main.css
396     root/static/images/logo.jpg
397     root/static/js/code.js
398
399
400 All static content lives under C<root/static> with everything else being
401 Template Toolkit files. Now you can identify the static content by
402 matching C<static> from within Catalyst.
403
404 =head3 Serving with HTTP::Daemon (myapp_server.pl)
405
406 To serve these files under the standalone server, we first must load the
407 Static plugin. Install L<Catalyst::Plugin::Static> if it's not already
408 installed.
409
410 In your main application class (MyApp.pm), load the plugin:
411
412     use Catalyst qw/-Debug FormValidator Static OtherPlugin/;
413
414 You will also need to make sure your end method does I<not> forward
415 static content to the view, perhaps like this:
416
417     sub end : Private {
418         my ( $self, $c ) = @_;
419
420         $c->forward( 'MyApp::V::TT' ) 
421           unless ( $c->res->body || !$c->stash->{template} );
422     }
423
424 This code will only forward to the view if a template has been
425 previously defined by a controller and if there is not already data in
426 C<$c-E<gt>res-E<gt>body>.
427
428 Next, create a controller to handle requests for the /static path. Use
429 the Helper to save time. This command will create a stub controller as
430 C<lib/MyApp/C/Static.pm>.
431
432     $ script/myapp_create.pl controller Static
433
434 Edit the file and add the following methods:
435
436     # serve all files under /static as static files
437     sub default : Path('/static') {
438         my ( $self, $c ) = @_;
439     
440         # Optional, allow the browser to cache the content
441         $c->res->headers->header( 'Cache-Control' => 'max-age=86400' );
442
443         $c->serve_static; # from Catalyst::Plugin::Static
444     }
445
446     # also handle requests for /favicon.ico
447     sub favicon : Path('/favicon.ico') {
448         my ( $self, $c ) = @_;
449     
450         $c->serve_static;
451     }
452
453 You can also define a different icon for the browser to use instead of
454 favicon.ico by using this in your HTML header:
455
456     <link rel="icon" href="/static/myapp.ico" type="image/x-icon" />
457
458
459 =head3 Common problems
460
461 The Static plugin makes use of the C<shared-mime-info> package to
462 automatically determine MIME types. This package is notoriously
463 difficult to install, especially on win32 and OSX. For OSX the easiest
464 path might be to install Fink, then use C<apt-get install
465 shared-mime-info>. Restart the server, and everything should be fine.
466
467 Make sure you are using the latest version (>= 0.16) for best
468 results. If you are having errors serving CSS files, or if they get
469 served as text/plain instead of text/css, you may have an outdated
470 shared-mime-info version. You may also wish to simply use the following
471 code in your Static controller:
472
473     if ($c->req->path =~ /css$/i) {
474         $c->serve_static( "text/css" );
475     } else {
476         $c->serve_static;
477     }
478
479 =head3 Serving with Apache
480
481 When using Apache, you can completely bypass Catalyst and the Static
482 controller by intercepting requests for the C<root/static> path at the
483 server level. All that is required is to define a DocumentRoot and add a
484 separate Location block for your static content. Here is a complete
485 config for this application under mod_perl 1.x; variations, some of
486 which could be simpler, are left as an exercise for the reader:
487
488     <Perl>
489         use lib qw(/var/www/MyApp/lib);
490     </Perl>
491     PerlModule MyApp
492     
493     <VirtualHost *>
494         ServerName myapp.example.com
495         DocumentRoot /var/www/MyApp/root
496         <Location />
497             SetHandler perl-script
498             PerlHandler MyApp
499         </Location>
500         <LocationMatch "/(static|favicon.ico)">
501             SetHandler default-handler
502         </LocationMatch>
503     </VirtualHost>
504
505 =head2 Forwarding with arguments
506
507 Sometimes you want to pass along arguments when forwarding to another
508 action. This can be accomplished by simply setting the arguments before
509 the forward:
510  
511   $c->req->args([qw/arg1 arg2 arg3/]);
512   $c->forward('/wherever');
513
514 =head1 AUTHOR
515
516 Sebastian Riedel, C<sri@oook.de>
517 Danijel Milicevic, C<me@danijel.de>
518 Viljo Marrandi, C<vilts@yahoo.com>
519 Marcus Ramberg, C<mramberg@cpan.org>
520 Andy Grundman, C<andy@hybridized.org>
521
522 =head1 COPYRIGHT
523
524 This program is free software, you can redistribute it and/or modify it
525 under the same terms as Perl itself.