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