removing unnecessary code in Catalyst.pm
[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
16 __PACKAGE__->action(
17 '!end' => sub {
18 my ( $self, $c ) = @_;
19 die "testing";
20 }
21 );
22
aff93052 23If you're tired of removing and adding this all the time, you
24can easily add a condition. for example:
25
26 die "Testing" if $c->param->{dump_info};
27
fc7ec1d9 28=head2 Disable statistics
29
30Just add this line to your application class if you don't want those nifty
31statistics in your debug messages.
32
33 sub Catalyst::Log::info { }
34
35=head2 Scaffolding
36
37Scaffolding is very simple with Catalyst.
38Just 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
73Modify the $c->form() parameters to match your needs, and don't forget to copy
74the templates. ;)
75
deb90705 76=head2 Serving static files and CSS as text/css
aba94964 77
78If you want to serve static content (like images, txt or CSS) via Catalyst,
79then all you need is the plugin Catalyst::Plugin::Static as well as a small
80regex 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
deb90705 101=head2 Uploads with Catalyst
aba94964 102
103To implement uploads in Catalyst you need to have a HTML form similiar to
104this:
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
112It's very important not to forget enctype="multipart/form-data" in form,
113if it's not there, uploads just don't work.
114
115Catalyst 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
138If you want to upload bigger files than 1MB, then just add to your Controller
139module:
140
141 $CGI::Simple::POST_MAX = 1048576000;
142
deb90705 143=head2 Authentication with Catalyst::Plugin::Authentication::CDBI
144
145There are (at least) two ways to implement authentication with this plugin:
1461) only checking username and password
1472) checking username, password and the roles the user has
148
149For 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
159email, first name, surname etc).
160'password_field' is, well, password field in your table and by default
161password is stored in plain text. Authentication::CDBI looks for 'user'
162and 'password' fields in table, if they're not defined in the config.
163
164In PostgreSQL users table might be something like:
165
166CREATE 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
175We'll discuss the first variant for now:
1761. user:password login / auth without roles
177
178To 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
192form parameters from a login form. If login succeeds, then $c->req->{user}
193contains the username of the authenticated user.
194
195If you want to remember the users login status inbetween further requests,
196then just use the $c->session_login method, Catalyst will create a session
197id, session cookie and automatically append session id to all urls. So
198all you have to do, is just check $c->req->{user} where needed.
199
200To log out user, just call $c->session_logout.
201
202Now lets take a look at the second variant:
2032. user:password login / auth with roles
204
205To use roles you need to add to MyApp->config in the 'authentication'
206section 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
213Corresponding tables in PostgreSQL could look like this:
214
215CREATE TABLE roles (
216 role_id serial,
217 name varchar(100),
218 primary key(role_id)
219);
220
221CREATE 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
230The 'roles' table is a list of role names and the 'user_role' table is used for
231the user -> role lookup.
232
233Now if a logged in user wants to see a location which is allowed only for
234people 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
245One thing you might need is to forward non-authenticated users to login
246form, if they try to access restricted areas. If you want to do this
247controller-wide (if you have one controller for admin section) then it's
248best 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
258Pay attention to $c->req->action(undef). This is needed, because of the
259way $c->forward works - forward to login gets called, but after that
260Catalyst executes anyway the action defined in the uri (eg. if you tried to
261watch /add, then first '!begin' forwards to '?login', but after that
262anyway '?add' is executed). So $c->req->action(undef) undefines any
263actions that were to be called and forwards user where we want him/her
264to be.
265
266And this is all you need to do, isn't Catalyst wonderful?
267
145074c2 268
269=head2 How to use Catalyst without mod_perl
270
271Catalyst applications give optimum performance when run under mod_perl.
272However sometimes mod_perl is not an option, and running under CGI is just too
273slow. There are two alternatives to mod_perl that give reasonable
274performance: FastCGI and PersistentPerl.
275
276B<Using FastCGI>
277
278To quote from L<http://www.fastcgi.com/>: "FastCGI is a language independent,
279scalable, extension to CGI that provides high performance without the
280limitations of specific server APIs." Web server support is provided for
281Apache in the form of C<mod_fastcgi> and there is Perl support in the C<FCGI>
282module. To convert a CGI Catalyst application to FastCGI one needs to
283initialize an C<FCGI::Request> object and loop while the C<Accept> method
284returns zero. The following code shows how it is done - and it also works as
285a 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
304Any initialization code should be included outside the request-accept loop.
305
306There is one little complication, which is that C<MyApp->run> outputs a
307complete HTTP response including the status line (e.g.: "C<HTTP/1.1 200>").
308FastCGI just wants a set of headers, so the sample code captures the output
309and drops the first line if it is an HTTP status line (note: this may change).
310
311The Apache C<mod_fastcgi> module is provided by a number of Linux distros and
312is straightforward to compile for most Unix-like systems. The module provides
313a FastCGI Process Manager, which manages FastCGI scripts. You configure your
314script as a FastCGI script with the following Apache configuration directives:
315
316 <Location /fcgi-bin>
317 AddHandler fastcgi-script fcgi
318 </Location>
319
320or:
321
322 <Location /fcgi-bin>
323 SetHandler fastcgi-script
324 Action fastcgi-script /path/to/fcgi-bin/fcgi-script
325 </Location>
326
327C<mod_fastcgi> provides a number of options for controlling the FastCGI
328scripts spawned; it also allows scripts to be run to handle the
329authentication, authorization and access check phases.
330
331For more information see the FastCGI documentation, the C<FCGI> module and
332L<http://www.fastcgi.com/>.
333
334
335B<PersistentPerl>
336
337PersistentPerl (previously known as C<CGI::SpeedyCGI>) is a persistent Perl
338interpreter. After the script is initially run, instead of exiting, the perl
339interpreter is kept running. During subsequent runs, this interpreter is used
340to handle new executions instead of starting a new perl interpreter each
341time. A very fast frontend program contacts the persistent Perl process, which
342is usually already running, to do the work and return the results.
343PersistentPerl can be used to speed up perl CGI scripts. It also provides an
344Apache module so that scripts can be run without the overhead of doing a
345fork/exec for each request.
346
347The code for PersistentPerl is simpler than for FastCGI; rather than waiting
348in an accept loop the script runs to completion, however variables are not
349reinitialized on subsequent runs but maintain their values from the previous
350run.
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
373For more information see the C<PersistentPerl> documentation.
374
375
fc7ec1d9 376=head1 AUTHOR
377
378Sebastian Riedel, C<sri@oook.de>
deb90705 379Danijel Milicevic C<me@danijel.de>
380Viljo Marrandi C<vilts@yahoo.com>
fc7ec1d9 381
382=head1 COPYRIGHT
383
384This program is free software, you can redistribute it and/or modify it under
385the same terms as Perl itself.