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