changed examples in Cookbook.pod
[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) {
1c61c726 294 MyApp->run;
145074c2 295 }
296
297Any initialization code should be included outside the request-accept loop.
298
299There is one little complication, which is that C<MyApp->run> outputs a
300complete HTTP response including the status line (e.g.: "C<HTTP/1.1 200>").
301FastCGI just wants a set of headers, so the sample code captures the output
302and drops the first line if it is an HTTP status line (note: this may change).
303
304The Apache C<mod_fastcgi> module is provided by a number of Linux distros and
305is straightforward to compile for most Unix-like systems. The module provides
306a FastCGI Process Manager, which manages FastCGI scripts. You configure your
307script as a FastCGI script with the following Apache configuration directives:
308
309 <Location /fcgi-bin>
310 AddHandler fastcgi-script fcgi
311 </Location>
312
313or:
314
315 <Location /fcgi-bin>
316 SetHandler fastcgi-script
317 Action fastcgi-script /path/to/fcgi-bin/fcgi-script
318 </Location>
319
320C<mod_fastcgi> provides a number of options for controlling the FastCGI
321scripts spawned; it also allows scripts to be run to handle the
322authentication, authorization and access check phases.
323
324For more information see the FastCGI documentation, the C<FCGI> module and
325L<http://www.fastcgi.com/>.
326
327
328B<PersistentPerl>
329
330PersistentPerl (previously known as C<CGI::SpeedyCGI>) is a persistent Perl
331interpreter. After the script is initially run, instead of exiting, the perl
332interpreter is kept running. During subsequent runs, this interpreter is used
333to handle new executions instead of starting a new perl interpreter each
334time. A very fast frontend program contacts the persistent Perl process, which
335is usually already running, to do the work and return the results.
336PersistentPerl can be used to speed up perl CGI scripts. It also provides an
337Apache module so that scripts can be run without the overhead of doing a
338fork/exec for each request.
339
340The code for PersistentPerl is simpler than for FastCGI; rather than waiting
341in an accept loop the script runs to completion, however variables are not
342reinitialized on subsequent runs but maintain their values from the previous
343run.
344
345
346 #!/usr/bin/perperl
347 use strict;
348 use vars qw($output $initialized);
349 use PersistentPerl;
350 use MyApp;
351
352 if (!$initialized++) {
353 # initialization code - set up database, etc
354 if ($PersistentPerl::i_am_per_perl) {
355 # PP-specific initialization code
356 }
357 }
1c61c726 358
359 MyApp->run;
145074c2 360
361For more information see the C<PersistentPerl> documentation.
362
363
fc7ec1d9 364=head1 AUTHOR
365
366Sebastian Riedel, C<sri@oook.de>
deb90705 367Danijel Milicevic C<me@danijel.de>
368Viljo Marrandi C<vilts@yahoo.com>
fc7ec1d9 369
370=head1 COPYRIGHT
371
372This program is free software, you can redistribute it and/or modify it under
373the same terms as Perl itself.