Cookbook patch from vilts
[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         if ($c->req->parameters->{form_submit} eq 'yes') {
91             my $upload = $c->req->upload('my_file');
92             if ($upload->filename) {
93                 my $filename = $upload->filename;
94                 my $fh = $upload->fh;
95                 open(NEW_FILE, ">/tmp/upload/$filename") or die
96                     "Can't open file for writing: $!";
97                 while ($fh->read(my $buf, 32768)) {
98                     print NEW_FILE $buf;
99                 }
100                 close(NEW_FILE);
101             }
102         }
103         $c->stash->{template} = 'file_upload.html';
104     }
105
106 =head2 Multiple file upload with Catalyst
107
108 Code for uploading multiple files from one form needs little changes compared
109 to single file upload.
110
111 Form goes like this:
112
113     <form action="/upload" method="post" enctype="multipart/form-data">
114       <input type="hidden" name="form_submit" value="yes">
115       <input type="file" name="file1" size="50"><br>
116       <input type="file" name="file2" size="50"><br>
117       <input type="file" name="file3" size="50"><br>
118       <input type="submit" value="Send">
119     </form>
120
121 Controller:
122
123     sub upload : Local {
124         my ($self, $c) = @_;
125         if ($c->req->parameters->{form_submit} eq 'yes') {
126             for my $field ($c->req->upload) {
127                 my $upload = $c->req->upload($field);
128                 if ($upload->filename) {
129                     my $filename = $upload->filename;
130                     my $fh = $upload->fh;
131                     open(NEW_FILE, ">/tmp/upload/$filename") or die
132                         "Can't open file for writing: $!";
133                     while ($fh->read(my $buf, 32768)) {
134                         print NEW_FILE $buf;
135                     }
136                     close(NEW_FILE);
137                 }
138             }
139         }
140         $c->stash->{template} = 'file_upload.html';
141     }
142
143 for my $field ($c->req->upload) loops automatically over all file input
144 fields and gets input names. After that is basic file saving code, just like in
145 single file upload.
146
147 Notice: die'ing might not be what you want to do, when error occurs, but
148 it works as an example. Better idea would be to store error $! in
149 $c->stash->{error} and show custom error template displaying this message.
150
151 For more information about uploads and usable methods look at
152 C<Catalyst::Request::Upload> and C<Catalyst::Request>.
153
154 =head2 Authentication with Catalyst::Plugin::Authentication::CDBI
155
156 There are (at least) two ways to implement authentication with this plugin:
157 1) only checking username and password
158 2) checking username, password and the roles the user has
159
160 For both variants you'll need the following code in your MyApp package:
161
162     use Catalyst qw/Session::FastMmap Static Authentication::CDBI/;
163
164     MyApp->config( authentication => { user_class => 'MyApp::M::MyApp::Users',
165                                        user_field => 'email',
166                                        password_field => 'password' });
167
168 'user_class' is a Class::DBI class for your users table.
169 'user_field' tells which field is used for username lookup (might be 
170 email, first name, surname etc).
171 'password_field' is, well, password field in your table and by default 
172 password is stored in plain text. Authentication::CDBI looks for 'user' 
173 and 'password' fields in table, if they're not defined in the config.
174
175 In PostgreSQL users table might be something like:
176
177 CREATE TABLE users (
178   user_id   serial,
179   name      varchar(100),
180   surname   varchar(100),
181   password  varchar(100),
182   email     varchar(100),
183   primary key(user_id)
184 );
185
186 We'll discuss the first variant for now:
187 1. user:password login / auth without roles
188
189 To log in a user you might use a action like this:
190
191     sub 'login' : Local {
192         my ($self, $c) = @_;
193         if ($c->req->params->{username}) {
194             $c->session_login($c->req->params->{username}, 
195                               $c->req->params->{password} );
196             if ($c->req->{user}) {
197                 $c->forward('?restricted_area');
198             }
199         }
200     }
201
202 $c->req->params->{username} and $c->req->params->{password} are html 
203 form parameters from a login form. If login succeeds, then 
204 $c->req->{user} contains the username of the authenticated user.
205
206 If you want to remember the users login status inbetween further 
207 requests, then just use the $c->session_login method, Catalyst will 
208 create a session id, session cookie and automatically append session 
209 id to all urls. So all you have to do, is just check $c->req->{user} 
210 where needed.
211
212 To log out user, just call $c->session_logout.
213
214 Now lets take a look at the second variant:
215 2. user:password login / auth with roles
216
217 To use roles you need to add to MyApp->config in the 'authentication' 
218 section following parameters:
219
220     role_class      => 'MyApp::M::MyApp::Roles',
221     user_role_class => 'MyApp::M::MyApp::UserRoles',
222     user_role_user_field => 'user_id',
223     user_role_role_field => 'role_id',
224
225 Corresponding tables in PostgreSQL could look like this:
226
227 CREATE TABLE roles (
228   role_id  serial,
229   name     varchar(100),
230   primary key(role_id)
231 );
232
233 CREATE TABLE user_roles (
234   user_role_id  serial,
235   user_id       int,
236   role_id       int,
237   primary key(user_role_id),
238   foreign key(user_id) references users(user_id),
239   foreign key(role_id) references roles(role_id)
240 );
241
242 The 'roles' table is a list of role names and the 'user_role' table is 
243 used for the user -> role lookup.
244
245 Now if a logged in user wants to see a location which is allowed only 
246 for people with 'admin' role then in you controller you can check it 
247 with:
248
249     sub add : Local {
250         my ($self, $c) = @_;
251         if ($c->roles(qw/admin/)) {
252             $c->req->output("Your account has the role 'admin.'");
253         } else {
254             $c->req->output("You're not allowed to be here");
255         }
256     }
257
258 One thing you might need is to forward non-authenticated users to login 
259 form, if they try to access restricted areas. If you want to do this 
260 controller-wide (if you have one controller for admin section) then it's 
261 best to add user check to '!begin' action:
262
263     sub begin : Private {
264         my ($self, $c) = @_;
265         unless ($c->req->{user}) {
266             $c->req->action(undef);  ## notice this!!
267             $c->forward('?login');
268         }
269     }
270
271 Pay attention to $c->req->action(undef). This is needed, because of the 
272 way $c->forward works - forward to login gets called, but after that 
273 Catalyst executes anyway the action defined in the uri (eg. if you 
274 tried to watch /add, then first 'begin' forwards to 'login', but after
275 that anyway 'add' is executed). So $c->req->action(undef) undefines any 
276 actions that were to be called and forwards user where we want him/her 
277 to be.
278
279 And this is all you need to do, isn't Catalyst wonderful?
280
281
282 =head2 How to use Catalyst without mod_perl
283
284 Catalyst applications give optimum performance when run under mod_perl.
285 However sometimes mod_perl is not an option, and running under CGI is 
286 just too slow.  There are two alternatives to mod_perl that give 
287 reasonable performance: FastCGI and PersistentPerl.
288
289 B<Using FastCGI>
290
291 To quote from L<http://www.fastcgi.com/>: "FastCGI is a language 
292 independent, scalable, extension to CGI that provides high performance 
293 without the limitations of specific server APIs."  Web server support 
294 is provided for Apache in the form of C<mod_fastcgi> and there is Perl
295 support in the C<FCGI> module.  To convert a CGI Catalyst application 
296 to FastCGI one needs to initialize an C<FCGI::Request> object and loop 
297 while the C<Accept> method returns zero.  The following code shows how 
298 it is done - and it also works as a normal, single-shot CGI script.
299
300     #!/usr/bin/perl
301     use strict;
302     use FCGI;
303     use MyApp;
304
305     my $request = FCGI::Request();
306     while ($request->Accept() >= 0) {
307         MyApp->run;
308     }
309
310 Any initialization code should be included outside the request-accept 
311 loop.
312
313 There is one little complication, which is that C<MyApp->run> outputs a
314 complete HTTP response including the status line (e.g.: 
315 "C<HTTP/1.1 200>").
316 FastCGI just wants a set of headers, so the sample code captures the 
317 output and  drops the first line if it is an HTTP status line (note: 
318 this may change).
319
320 The Apache C<mod_fastcgi> module is provided by a number of Linux 
321 distros and is straightforward to compile for most Unix-like systems.  
322 The module provides a FastCGI Process Manager, which manages FastCGI 
323 scripts.  You configure your script as a FastCGI script with the 
324 following Apache configuration directives:
325
326     <Location /fcgi-bin>
327        AddHandler fastcgi-script fcgi
328     </Location>
329
330 or:
331
332     <Location /fcgi-bin>
333        SetHandler fastcgi-script
334        Action fastcgi-script /path/to/fcgi-bin/fcgi-script
335     </Location>
336
337 C<mod_fastcgi> provides a number of options for controlling the FastCGI
338 scripts spawned; it also allows scripts to be run to handle the
339 authentication, authorization and access check phases.
340
341 For more information see the FastCGI documentation, the C<FCGI> module 
342 and L<http://www.fastcgi.com/>.
343
344
345 B<PersistentPerl>
346
347 PersistentPerl (previously known as C<CGI::SpeedyCGI>) is a persistent 
348 Perl interpreter.  After the script is initially run, instead of 
349 exiting, the perl interpreter is kept running. During subsequent runs, 
350 this interpreter is used to handle new executions instead of starting 
351 a new perl interpreter each time. A very fast frontend program contacts
352 the persistent Perl process, which is usually already running, to do 
353 the work and return the results.
354 PersistentPerl can be used to speed up perl CGI scripts.  It also 
355 provides an Apache module so that scripts can be run without the 
356 overhead of doing a fork/exec for each request.
357
358 The code for PersistentPerl is simpler than for FastCGI; rather than 
359 waiting in an accept loop the script runs to completion, however 
360 variables are not reinitialized on subsequent runs but maintain their 
361 values from the previous run.
362
363
364     #!/usr/bin/perperl
365     use strict;
366     use vars qw($output $initialized);
367     use PersistentPerl;
368     use MyApp;
369
370     if (!$initialized++) {
371         # initialization code - set up database, etc
372         if ($PersistentPerl::i_am_per_perl) {
373             # PP-specific initialization code
374         }
375     }
376
377     MyApp->run;
378
379 For more information see the C<PersistentPerl> documentation.
380
381
382 =head1 AUTHOR
383
384 Sebastian Riedel, C<sri@oook.de>
385 Danijel Milicevic C<me@danijel.de>
386 Viljo Marrandi C<vilts@yahoo.com>
387 Marcus Ramberg C<mramberg@cpan.org>
388
389 =head1 COPYRIGHT
390
391 This program is free software, you can redistribute it and/or modify it
392 under the same terms as Perl itself.