update Cookbook
[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 request by
14 placing a die() call in the _end action.
15
16      sub end : Private {
17          my ( $self, $c ) = @_;
18          die "testing";
19      }
20
21 If you're tired of removing and adding this all the time, you
22 can easily add a condition. for example:
23
24   die "Testing" if $c->param->{dump_info};
25
26 =head2 Disable statistics
27
28 Just add this line to your application class if you don't want those nifty
29 statistics in your debug messages.
30
31     sub Catalyst::Log::info { }
32
33 =head2 Scaffolding
34
35 Scaffolding is very simple with Catalyst.
36 Just use Catalyst::Model::CDBI::CRUD as baseclass.
37
38     # lib/MyApp/Model/CDBI.pm
39     package MyApp::Model::CDBI;
40
41     use strict;
42     use base 'Catalyst::Model::CDBI::CRUD';
43
44     __PACKAGE__->config(
45         dsn           => 'dbi:SQLite:/tmp/myapp.db',
46         relationships => 1
47     );
48
49     1;
50
51     # lib/MyApp.pm
52     package MyApp;
53
54     use Catalyst 'FormValidator';
55
56     __PACKAGE__->config(
57         name => 'My Application',
58         root => '/home/joeuser/myapp/root'
59     );
60
61     sub my_table : Global {
62         my ( $self, $c ) = @_;
63         $c->form( optional => [ MyApp::Model::CDBI::Table->columns ] );
64         $c->forward('MyApp::Model::CDBI::Table');
65     }
66
67     1;
68
69 Modify the $c->form() parameters to match your needs, and don't forget to copy
70 the templates. ;)
71
72 =head2 Single file upload with Catalyst
73
74 To implement uploads in Catalyst you need to have a HTML form similiar to
75 this:
76
77     <form action="/upload" method="post" enctype="multipart/form-data">
78       <input type="hidden" name="form_submit" value="yes">
79       <input type="file" name="my_file">
80       <input type="submit" value="Send">
81     </form>
82
83 It's very important not to forget enctype="multipart/form-data" in form, 
84 if it's not there, uploads just don't work.
85
86 Catalyst Controller module 'upload' action:
87
88     sub upload : Global {
89         my ($self, $c) = @_;
90
91         if ( $c->request->parameters->{form_submit} eq 'yes' ) {
92
93             if ( my $upload = $c->request->upload('my_file') ) {
94
95                 my $filename = $upload->filename;
96                 my $fh       = $upload->fh;
97
98                 open( NEW_FILE, ">/tmp/upload/$filename" ) 
99                   or die( "Can't open file for writing: $!" );
100
101                 while ( $fh->read( my $buf, 32768 ) ) {
102                     print NEW_FILE $buf;
103                 }
104
105                 close(NEW_FILE);
106             }
107         }
108         
109         $c->stash->{template} = 'file_upload.html';
110     }
111
112 =head2 Multiple file upload with Catalyst
113
114 Code for uploading multiple files from one form needs little changes compared
115 to single file upload.
116
117 Form goes like this:
118
119     <form action="/upload" method="post" enctype="multipart/form-data">
120       <input type="hidden" name="form_submit" value="yes">
121       <input type="file" name="file1" size="50"><br>
122       <input type="file" name="file2" size="50"><br>
123       <input type="file" name="file3" size="50"><br>
124       <input type="submit" value="Send">
125     </form>
126
127 Controller:
128
129     sub upload : Local {
130         my ($self, $c) = @_;
131
132         if ( $c->request->parameters->{form_submit} eq 'yes' ) {
133
134             for my $field ( $c->req->upload ) {
135
136                 my $upload   = $c->request->upload($field);
137                 my $filename = $upload->filename;
138                 my $fh       = $upload->fh;
139
140                 open( NEW_FILE, ">/tmp/upload/$filename" ) 
141                   or die ("Can't open file for writing: $!");
142
143                 while ( $fh->read( my $buf, 32768 ) ) {
144                     print NEW_FILE $buf;
145                 }
146
147                 close(NEW_FILE);
148             }
149         }
150
151         $c->stash->{template} = 'file_upload.html';
152     }
153
154 for my $field ($c->req->upload) loops automatically over all file input
155 fields and gets input names. After that is basic file saving code, just like in
156 single file upload.
157
158 Notice: die'ing might not be what you want to do, when error occurs, but
159 it works as an example. Better idea would be to store error $! in
160 $c->stash->{error} and show custom error template displaying this message.
161
162 For more information about uploads and usable methods look at
163 C<Catalyst::Request::Upload> and C<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 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 a 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 $c->req->params->{username} and $c->req->params->{password} are html 
214 form parameters from a login form. If login succeeds, then 
215 $c->req->{user} contains the username of the authenticated user.
216
217 If you want to remember the users login status inbetween further 
218 requests, then just use the $c->session_login method, Catalyst will 
219 create a session id, session cookie and automatically append session 
220 id to all urls. So all you have to do, is just check $c->req->{user} 
221 where needed.
222
223 To log out user, just call $c->session_logout.
224
225 Now lets take a look at the second variant:
226 2. user:password login / auth with roles
227
228 To use roles you need to add to MyApp->config in the 'authentication' 
229 section following parameters:
230
231     role_class      => 'MyApp::M::MyApp::Roles',
232     user_role_class => 'MyApp::M::MyApp::UserRoles',
233     user_role_user_field => 'user_id',
234     user_role_role_field => 'role_id',
235
236 Corresponding tables in PostgreSQL could look like this:
237
238 CREATE TABLE roles (
239   role_id  serial,
240   name     varchar(100),
241   primary key(role_id)
242 );
243
244 CREATE TABLE user_roles (
245   user_role_id  serial,
246   user_id       int,
247   role_id       int,
248   primary key(user_role_id),
249   foreign key(user_id) references users(user_id),
250   foreign key(role_id) references roles(role_id)
251 );
252
253 The 'roles' table is a list of role names and the 'user_role' table is 
254 used for the user -> role lookup.
255
256 Now if a logged in user wants to see a location which is allowed only 
257 for people with 'admin' role then in you controller you can check it 
258 with:
259
260     sub add : Local {
261         my ($self, $c) = @_;
262         if ($c->roles(qw/admin/)) {
263             $c->req->output("Your account has the role 'admin.'");
264         } else {
265             $c->req->output("You're not allowed to be here");
266         }
267     }
268
269 One thing you might need is to forward non-authenticated users to login 
270 form, if they try to access restricted areas. If you want to do this 
271 controller-wide (if you have one controller for admin section) then it's 
272 best to add user check to '!begin' action:
273
274     sub begin : Private {
275         my ($self, $c) = @_;
276         unless ($c->req->{user}) {
277             $c->req->action(undef);  ## notice this!!
278             $c->forward('?login');
279         }
280     }
281
282 Pay attention to $c->req->action(undef). This is needed, because of the 
283 way $c->forward works - forward to login gets called, but after that 
284 Catalyst executes anyway the action defined in the uri (eg. if you 
285 tried to watch /add, then first 'begin' forwards to 'login', but after
286 that anyway 'add' is executed). So $c->req->action(undef) undefines any 
287 actions that were to be called and forwards user where we want him/her 
288 to be.
289
290 And this is all you need to do, isn't Catalyst wonderful?
291
292
293 =head2 How to use Catalyst without mod_perl
294
295 Catalyst applications give optimum performance when run under mod_perl.
296 However sometimes mod_perl is not an option, and running under CGI is 
297 just too slow.  There are two alternatives to mod_perl that give 
298 reasonable performance: FastCGI and PersistentPerl.
299
300 B<Using FastCGI>
301
302 To quote from L<http://www.fastcgi.com/>: "FastCGI is a language 
303 independent, scalable, extension to CGI that provides high performance 
304 without the limitations of specific server APIs."  Web server support 
305 is provided for Apache in the form of C<mod_fastcgi> and there is Perl
306 support in the C<FCGI> module.  To convert a CGI Catalyst application 
307 to FastCGI one needs to initialize an C<FCGI::Request> object and loop 
308 while the C<Accept> method returns zero.  The following code shows how 
309 it is done - and it also works as a normal, single-shot CGI script.
310
311     #!/usr/bin/perl
312     use strict;
313     use FCGI;
314     use MyApp;
315
316     my $request = FCGI::Request();
317     while ($request->Accept() >= 0) {
318         MyApp->run;
319     }
320
321 Any initialization code should be included outside the request-accept 
322 loop.
323
324 There is one little complication, which is that C<MyApp->run> outputs a
325 complete HTTP response including the status line (e.g.: 
326 "C<HTTP/1.1 200>").
327 FastCGI just wants a set of headers, so the sample code captures the 
328 output and  drops the first line if it is an HTTP status line (note: 
329 this may change).
330
331 The Apache C<mod_fastcgi> module is provided by a number of Linux 
332 distros and is straightforward to compile for most Unix-like systems.  
333 The module provides a FastCGI Process Manager, which manages FastCGI 
334 scripts.  You configure your script as a FastCGI script with the 
335 following Apache configuration directives:
336
337     <Location /fcgi-bin>
338        AddHandler fastcgi-script fcgi
339     </Location>
340
341 or:
342
343     <Location /fcgi-bin>
344        SetHandler fastcgi-script
345        Action fastcgi-script /path/to/fcgi-bin/fcgi-script
346     </Location>
347
348 C<mod_fastcgi> provides a number of options for controlling the FastCGI
349 scripts spawned; it also allows scripts to be run to handle the
350 authentication, authorization and access check phases.
351
352 For more information see the FastCGI documentation, the C<FCGI> module 
353 and L<http://www.fastcgi.com/>.
354
355
356 B<PersistentPerl>
357
358 PersistentPerl (previously known as C<CGI::SpeedyCGI>) is a persistent 
359 Perl interpreter.  After the script is initially run, instead of 
360 exiting, the perl interpreter is kept running. During subsequent runs, 
361 this interpreter is used to handle new executions instead of starting 
362 a new perl interpreter each time. A very fast frontend program contacts
363 the persistent Perl process, which is usually already running, to do 
364 the work and return the results.
365 PersistentPerl can be used to speed up perl CGI scripts.  It also 
366 provides an Apache module so that scripts can be run without the 
367 overhead of doing a fork/exec for each request.
368
369 The code for PersistentPerl is simpler than for FastCGI; rather than 
370 waiting in an accept loop the script runs to completion, however 
371 variables are not reinitialized on subsequent runs but maintain their 
372 values from the previous run.
373
374
375     #!/usr/bin/perperl
376     use strict;
377     use vars qw($output $initialized);
378     use PersistentPerl;
379     use MyApp;
380
381     if (!$initialized++) {
382         # initialization code - set up database, etc
383         if ($PersistentPerl::i_am_per_perl) {
384             # PP-specific initialization code
385         }
386     }
387
388     MyApp->run;
389
390 For more information see the C<PersistentPerl> documentation.
391
392
393 =head1 AUTHOR
394
395 Sebastian Riedel, C<sri@oook.de>
396 Danijel Milicevic C<me@danijel.de>
397 Viljo Marrandi C<vilts@yahoo.com>
398 Marcus Ramberg C<mramberg@cpan.org>
399
400 =head1 COPYRIGHT
401
402 This program is free software, you can redistribute it and/or modify it
403 under the same terms as Perl itself.