update @INC
[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) = @_;
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
108Code for uploading multiple files from one form needs little changes compared
109to single file upload.
110
111Form 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
121Controller:
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
aba94964 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 }
61b1e958 139 }
5c0ff128 140 $c->stash->{template} = 'file_upload.html';
141 }
142
143for my $field ($c->req->upload) loops automatically over all file input
144fields and gets input names. After that is basic file saving code, just like in
145single file upload.
aba94964 146
5c0ff128 147Notice: die'ing might not be what you want to do, when error occurs, but
148it works as an example. Better idea would be to store error $! in
149$c->stash->{error} and show custom error template displaying this message.
aba94964 150
5c0ff128 151For more information about uploads and usable methods look at
152C<Catalyst::Request::Upload> and C<Catalyst::Request>.
aba94964 153
deb90705 154=head2 Authentication with Catalyst::Plugin::Authentication::CDBI
155
156There are (at least) two ways to implement authentication with this plugin:
1571) only checking username and password
1582) checking username, password and the roles the user has
159
160For 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
170email, first name, surname etc).
171'password_field' is, well, password field in your table and by default
172password is stored in plain text. Authentication::CDBI looks for 'user'
173and 'password' fields in table, if they're not defined in the config.
174
175In PostgreSQL users table might be something like:
176
177CREATE 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
186We'll discuss the first variant for now:
1871. user:password login / auth without roles
188
189To log in a user you might use a action like this:
190
61b1e958 191 sub 'login' : Local {
deb90705 192 my ($self, $c) = @_;
193 if ($c->req->params->{username}) {
194 $c->session_login($c->req->params->{username},
61b1e958 195 $c->req->params->{password} );
deb90705 196 if ($c->req->{user}) {
197 $c->forward('?restricted_area');
198 }
199 }
61b1e958 200 }
deb90705 201
202$c->req->params->{username} and $c->req->params->{password} are html
61b1e958 203form parameters from a login form. If login succeeds, then
204$c->req->{user} contains the username of the authenticated user.
deb90705 205
61b1e958 206If you want to remember the users login status inbetween further
207requests, then just use the $c->session_login method, Catalyst will
208create a session id, session cookie and automatically append session
209id to all urls. So all you have to do, is just check $c->req->{user}
210where needed.
deb90705 211
212To log out user, just call $c->session_logout.
213
214Now lets take a look at the second variant:
2152. user:password login / auth with roles
216
217To use roles you need to add to MyApp->config in the 'authentication'
218section 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
225Corresponding tables in PostgreSQL could look like this:
226
227CREATE TABLE roles (
228 role_id serial,
229 name varchar(100),
230 primary key(role_id)
231);
232
233CREATE 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
61b1e958 242The 'roles' table is a list of role names and the 'user_role' table is
243used for the user -> role lookup.
deb90705 244
61b1e958 245Now if a logged in user wants to see a location which is allowed only
246for people with 'admin' role then in you controller you can check it
247with:
deb90705 248
61b1e958 249 sub add : Local {
deb90705 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 }
61b1e958 256 }
deb90705 257
258One thing you might need is to forward non-authenticated users to login
259form, if they try to access restricted areas. If you want to do this
260controller-wide (if you have one controller for admin section) then it's
261best to add user check to '!begin' action:
262
61b1e958 263 sub begin : Private {
deb90705 264 my ($self, $c) = @_;
265 unless ($c->req->{user}) {
266 $c->req->action(undef); ## notice this!!
267 $c->forward('?login');
268 }
61b1e958 269 }
deb90705 270
271Pay attention to $c->req->action(undef). This is needed, because of the
272way $c->forward works - forward to login gets called, but after that
61b1e958 273Catalyst executes anyway the action defined in the uri (eg. if you
274tried to watch /add, then first 'begin' forwards to 'login', but after
275that anyway 'add' is executed). So $c->req->action(undef) undefines any
deb90705 276actions that were to be called and forwards user where we want him/her
277to be.
278
279And this is all you need to do, isn't Catalyst wonderful?
280
145074c2 281
282=head2 How to use Catalyst without mod_perl
283
284Catalyst applications give optimum performance when run under mod_perl.
61b1e958 285However sometimes mod_perl is not an option, and running under CGI is
286just too slow. There are two alternatives to mod_perl that give
287reasonable performance: FastCGI and PersistentPerl.
145074c2 288
289B<Using FastCGI>
290
61b1e958 291To quote from L<http://www.fastcgi.com/>: "FastCGI is a language
292independent, scalable, extension to CGI that provides high performance
293without the limitations of specific server APIs." Web server support
294is provided for Apache in the form of C<mod_fastcgi> and there is Perl
295support in the C<FCGI> module. To convert a CGI Catalyst application
296to FastCGI one needs to initialize an C<FCGI::Request> object and loop
297while the C<Accept> method returns zero. The following code shows how
298it is done - and it also works as a normal, single-shot CGI script.
145074c2 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) {
1c61c726 307 MyApp->run;
145074c2 308 }
309
61b1e958 310Any initialization code should be included outside the request-accept
311loop.
145074c2 312
313There is one little complication, which is that C<MyApp->run> outputs a
61b1e958 314complete HTTP response including the status line (e.g.:
315"C<HTTP/1.1 200>").
316FastCGI just wants a set of headers, so the sample code captures the
317output and drops the first line if it is an HTTP status line (note:
318this may change).
319
320The Apache C<mod_fastcgi> module is provided by a number of Linux
321distros and is straightforward to compile for most Unix-like systems.
322The module provides a FastCGI Process Manager, which manages FastCGI
323scripts. You configure your script as a FastCGI script with the
324following Apache configuration directives:
145074c2 325
326 <Location /fcgi-bin>
327 AddHandler fastcgi-script fcgi
328 </Location>
329
330or:
331
332 <Location /fcgi-bin>
333 SetHandler fastcgi-script
334 Action fastcgi-script /path/to/fcgi-bin/fcgi-script
335 </Location>
336
337C<mod_fastcgi> provides a number of options for controlling the FastCGI
338scripts spawned; it also allows scripts to be run to handle the
339authentication, authorization and access check phases.
340
61b1e958 341For more information see the FastCGI documentation, the C<FCGI> module
342and L<http://www.fastcgi.com/>.
145074c2 343
344
345B<PersistentPerl>
346
61b1e958 347PersistentPerl (previously known as C<CGI::SpeedyCGI>) is a persistent
348Perl interpreter. After the script is initially run, instead of
349exiting, the perl interpreter is kept running. During subsequent runs,
350this interpreter is used to handle new executions instead of starting
351a new perl interpreter each time. A very fast frontend program contacts
352the persistent Perl process, which is usually already running, to do
353the work and return the results.
354PersistentPerl can be used to speed up perl CGI scripts. It also
355provides an Apache module so that scripts can be run without the
356overhead of doing a fork/exec for each request.
145074c2 357
61b1e958 358The code for PersistentPerl is simpler than for FastCGI; rather than
359waiting in an accept loop the script runs to completion, however
360variables are not reinitialized on subsequent runs but maintain their
361values from the previous run.
145074c2 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 }
1c61c726 376
377 MyApp->run;
145074c2 378
379For more information see the C<PersistentPerl> documentation.
380
381
fc7ec1d9 382=head1 AUTHOR
383
384Sebastian Riedel, C<sri@oook.de>
deb90705 385Danijel Milicevic C<me@danijel.de>
386Viljo Marrandi C<vilts@yahoo.com>
61b1e958 387Marcus Ramberg C<mramberg@cpan.org>
fc7ec1d9 388
389=head1 COPYRIGHT
390
61b1e958 391This program is free software, you can redistribute it and/or modify it
392under the same terms as Perl itself.