update Cookbook
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Manual / Cookbook.pod
CommitLineData
fc7ec1d9 1=head1 NAME
2
3Catalyst::Manual::Cookbook - Cooking with Catalyst
4
5=head1 DESCRIPTION
6
aba94964 7Yummy code like your mum used to bake!
fc7ec1d9 8
9=head1 RECIPES
10
11=head2 Force debug screen
12
13You can force Catalyst to display the debug screen at the end of the request by
14placing a die() call in the _end action.
15
61b1e958 16 sub end : Private {
17 my ( $self, $c ) = @_;
18 die "testing";
19 }
fc7ec1d9 20
aff93052 21If you're tired of removing and adding this all the time, you
22can easily add a condition. for example:
23
24 die "Testing" if $c->param->{dump_info};
25
fc7ec1d9 26=head2 Disable statistics
27
28Just add this line to your application class if you don't want those nifty
29statistics in your debug messages.
30
31 sub Catalyst::Log::info { }
32
33=head2 Scaffolding
34
35Scaffolding is very simple with Catalyst.
36Just 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
61b1e958 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 }
fc7ec1d9 66
67 1;
68
69Modify the $c->form() parameters to match your needs, and don't forget to copy
70the templates. ;)
71
5c0ff128 72=head2 Single file upload with Catalyst
aba94964 73
74To implement uploads in Catalyst you need to have a HTML form similiar to
75this:
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
83It's very important not to forget enctype="multipart/form-data" in form,
84if it's not there, uploads just don't work.
85
86Catalyst Controller module 'upload' action:
87
5c0ff128 88 sub upload : Global {
89 my ($self, $c) = @_;
4d89569d 90
91 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
92
93 if ( my $upload = $c->request->upload('my_file') ) {
94
5c0ff128 95 my $filename = $upload->filename;
4d89569d 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 ) ) {
5c0ff128 102 print NEW_FILE $buf;
103 }
4d89569d 104
5c0ff128 105 close(NEW_FILE);
106 }
107 }
4d89569d 108
5c0ff128 109 $c->stash->{template} = 'file_upload.html';
110 }
111
112=head2 Multiple file upload with Catalyst
113
114Code for uploading multiple files from one form needs little changes compared
115to single file upload.
116
117Form 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
127Controller:
128
129 sub upload : Local {
130 my ($self, $c) = @_;
4d89569d 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;
aba94964 145 }
4d89569d 146
147 close(NEW_FILE);
aba94964 148 }
61b1e958 149 }
4d89569d 150
5c0ff128 151 $c->stash->{template} = 'file_upload.html';
152 }
153
154for my $field ($c->req->upload) loops automatically over all file input
155fields and gets input names. After that is basic file saving code, just like in
156single file upload.
aba94964 157
5c0ff128 158Notice: die'ing might not be what you want to do, when error occurs, but
159it works as an example. Better idea would be to store error $! in
160$c->stash->{error} and show custom error template displaying this message.
aba94964 161
5c0ff128 162For more information about uploads and usable methods look at
163C<Catalyst::Request::Upload> and C<Catalyst::Request>.
aba94964 164
deb90705 165=head2 Authentication with Catalyst::Plugin::Authentication::CDBI
166
167There are (at least) two ways to implement authentication with this plugin:
1681) only checking username and password
1692) checking username, password and the roles the user has
170
171For 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
181email, first name, surname etc).
182'password_field' is, well, password field in your table and by default
183password is stored in plain text. Authentication::CDBI looks for 'user'
184and 'password' fields in table, if they're not defined in the config.
185
186In PostgreSQL users table might be something like:
187
188CREATE 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
197We'll discuss the first variant for now:
1981. user:password login / auth without roles
199
200To log in a user you might use a action like this:
201
61b1e958 202 sub 'login' : Local {
deb90705 203 my ($self, $c) = @_;
204 if ($c->req->params->{username}) {
205 $c->session_login($c->req->params->{username},
61b1e958 206 $c->req->params->{password} );
deb90705 207 if ($c->req->{user}) {
208 $c->forward('?restricted_area');
209 }
210 }
61b1e958 211 }
deb90705 212
213$c->req->params->{username} and $c->req->params->{password} are html
61b1e958 214form parameters from a login form. If login succeeds, then
215$c->req->{user} contains the username of the authenticated user.
deb90705 216
61b1e958 217If you want to remember the users login status inbetween further
218requests, then just use the $c->session_login method, Catalyst will
219create a session id, session cookie and automatically append session
220id to all urls. So all you have to do, is just check $c->req->{user}
221where needed.
deb90705 222
223To log out user, just call $c->session_logout.
224
225Now lets take a look at the second variant:
2262. user:password login / auth with roles
227
228To use roles you need to add to MyApp->config in the 'authentication'
229section 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
236Corresponding tables in PostgreSQL could look like this:
237
238CREATE TABLE roles (
239 role_id serial,
240 name varchar(100),
241 primary key(role_id)
242);
243
244CREATE 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
61b1e958 253The 'roles' table is a list of role names and the 'user_role' table is
254used for the user -> role lookup.
deb90705 255
61b1e958 256Now if a logged in user wants to see a location which is allowed only
257for people with 'admin' role then in you controller you can check it
258with:
deb90705 259
61b1e958 260 sub add : Local {
deb90705 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 }
61b1e958 267 }
deb90705 268
269One thing you might need is to forward non-authenticated users to login
270form, if they try to access restricted areas. If you want to do this
271controller-wide (if you have one controller for admin section) then it's
272best to add user check to '!begin' action:
273
61b1e958 274 sub begin : Private {
deb90705 275 my ($self, $c) = @_;
276 unless ($c->req->{user}) {
277 $c->req->action(undef); ## notice this!!
278 $c->forward('?login');
279 }
61b1e958 280 }
deb90705 281
282Pay attention to $c->req->action(undef). This is needed, because of the
283way $c->forward works - forward to login gets called, but after that
61b1e958 284Catalyst executes anyway the action defined in the uri (eg. if you
285tried to watch /add, then first 'begin' forwards to 'login', but after
286that anyway 'add' is executed). So $c->req->action(undef) undefines any
deb90705 287actions that were to be called and forwards user where we want him/her
288to be.
289
290And this is all you need to do, isn't Catalyst wonderful?
291
145074c2 292
293=head2 How to use Catalyst without mod_perl
294
295Catalyst applications give optimum performance when run under mod_perl.
61b1e958 296However sometimes mod_perl is not an option, and running under CGI is
297just too slow. There are two alternatives to mod_perl that give
298reasonable performance: FastCGI and PersistentPerl.
145074c2 299
300B<Using FastCGI>
301
61b1e958 302To quote from L<http://www.fastcgi.com/>: "FastCGI is a language
303independent, scalable, extension to CGI that provides high performance
304without the limitations of specific server APIs." Web server support
305is provided for Apache in the form of C<mod_fastcgi> and there is Perl
306support in the C<FCGI> module. To convert a CGI Catalyst application
307to FastCGI one needs to initialize an C<FCGI::Request> object and loop
308while the C<Accept> method returns zero. The following code shows how
309it is done - and it also works as a normal, single-shot CGI script.
145074c2 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) {
1c61c726 318 MyApp->run;
145074c2 319 }
320
61b1e958 321Any initialization code should be included outside the request-accept
322loop.
145074c2 323
324There is one little complication, which is that C<MyApp->run> outputs a
61b1e958 325complete HTTP response including the status line (e.g.:
326"C<HTTP/1.1 200>").
327FastCGI just wants a set of headers, so the sample code captures the
328output and drops the first line if it is an HTTP status line (note:
329this may change).
330
331The Apache C<mod_fastcgi> module is provided by a number of Linux
332distros and is straightforward to compile for most Unix-like systems.
333The module provides a FastCGI Process Manager, which manages FastCGI
334scripts. You configure your script as a FastCGI script with the
335following Apache configuration directives:
145074c2 336
337 <Location /fcgi-bin>
338 AddHandler fastcgi-script fcgi
339 </Location>
340
341or:
342
343 <Location /fcgi-bin>
344 SetHandler fastcgi-script
345 Action fastcgi-script /path/to/fcgi-bin/fcgi-script
346 </Location>
347
348C<mod_fastcgi> provides a number of options for controlling the FastCGI
349scripts spawned; it also allows scripts to be run to handle the
350authentication, authorization and access check phases.
351
61b1e958 352For more information see the FastCGI documentation, the C<FCGI> module
353and L<http://www.fastcgi.com/>.
145074c2 354
355
356B<PersistentPerl>
357
61b1e958 358PersistentPerl (previously known as C<CGI::SpeedyCGI>) is a persistent
359Perl interpreter. After the script is initially run, instead of
360exiting, the perl interpreter is kept running. During subsequent runs,
361this interpreter is used to handle new executions instead of starting
362a new perl interpreter each time. A very fast frontend program contacts
363the persistent Perl process, which is usually already running, to do
364the work and return the results.
365PersistentPerl can be used to speed up perl CGI scripts. It also
366provides an Apache module so that scripts can be run without the
367overhead of doing a fork/exec for each request.
145074c2 368
61b1e958 369The code for PersistentPerl is simpler than for FastCGI; rather than
370waiting in an accept loop the script runs to completion, however
371variables are not reinitialized on subsequent runs but maintain their
372values from the previous run.
145074c2 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 }
1c61c726 387
388 MyApp->run;
145074c2 389
390For more information see the C<PersistentPerl> documentation.
391
392
fc7ec1d9 393=head1 AUTHOR
394
395Sebastian Riedel, C<sri@oook.de>
deb90705 396Danijel Milicevic C<me@danijel.de>
397Viljo Marrandi C<vilts@yahoo.com>
61b1e958 398Marcus Ramberg C<mramberg@cpan.org>
fc7ec1d9 399
400=head1 COPYRIGHT
401
61b1e958 402This program is free software, you can redistribute it and/or modify it
403under the same terms as Perl itself.