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