apply docs patch from Carl Franks (thanks!) with a couple minor changes
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Manual / Cookbook.pod
1 =head1 NAME
2
3 Catalyst::Manual::Cookbook - Cooking with Catalyst
4
5 =head1 DESCRIPTION
6
7 Yummy code like your mum used to bake!
8
9 =head1 RECIPES
10
11 =head2 Force debug screen
12
13 You can force Catalyst to display the debug screen at the end of the request by
14 placing a C<die()> call in the C<end> action.
15
16      sub end : Private {
17          my ( $self, $c ) = @_;
18          die "forced debug";
19      }
20
21 If you're tired of removing and adding this all the time, you can add a
22 condition in the C<end> action. For example:
23
24     sub end : Private {  
25         my ( $self, $c ) = @_;  
26         die "forced debug" if $c->req->params->{dump_info};  
27     }  
28
29 Then just add to your query string C<"&dump_info=1">, or the like, to
30 force debug output.
31
32
33 =head2 Disable statistics
34
35 Just add this line to your application class if you don't want those nifty
36 statistics in your debug messages.
37
38     sub Catalyst::Log::info { }
39
40 =head2 Scaffolding
41
42 Scaffolding is very simple with Catalyst.
43
44 The recommended way is to use Catalyst::Helper::Controller::Scaffold.
45
46 Just install this module, and to scaffold a Class::DBI Model class, do the following:
47
48 ./script/myapp_create controller <name> Scaffold <CDBI::Class>Scaffolding
49
50
51
52
53 =head2 File uploads
54
55 =head3 Single file upload with Catalyst
56
57 To implement uploads in Catalyst, you need to have a HTML form similar to
58 this:
59
60     <form action="/upload" method="post" enctype="multipart/form-data">
61       <input type="hidden" name="form_submit" value="yes">
62       <input type="file" name="my_file">
63       <input type="submit" value="Send">
64     </form>
65
66 It's very important not to forget C<enctype="multipart/form-data"> in
67 the form.
68
69 Catalyst Controller module 'upload' action:
70
71     sub upload : Global {
72         my ($self, $c) = @_;
73
74         if ( $c->request->parameters->{form_submit} eq 'yes' ) {
75
76             if ( my $upload = $c->request->upload('my_file') ) {
77
78                 my $filename = $upload->filename;
79                 my $target   = "/tmp/upload/$filename";
80
81                 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
82                     die( "Failed to copy '$filename' to '$target': $!" );
83                 }
84             }
85         }
86
87         $c->stash->{template} = 'file_upload.html';
88     }
89
90 =head3 Multiple file upload with Catalyst
91
92 Code for uploading multiple files from one form needs a few changes:
93
94 The form should have this basic structure:
95
96     <form action="/upload" method="post" enctype="multipart/form-data">
97       <input type="hidden" name="form_submit" value="yes">
98       <input type="file" name="file1" size="50"><br>
99       <input type="file" name="file2" size="50"><br>
100       <input type="file" name="file3" size="50"><br>
101       <input type="submit" value="Send">
102     </form>
103
104 And in the controller:
105
106     sub upload : Local {
107         my ($self, $c) = @_;
108
109         if ( $c->request->parameters->{form_submit} eq 'yes' ) {
110
111             for my $field ( $c->req->upload ) {
112
113                 my $upload   = $c->req->upload($field);
114                 my $filename = $upload->filename;
115                 my $target   = "/tmp/upload/$filename";
116
117                 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
118                     die( "Failed to copy '$filename' to '$target': $!" );
119                 }
120             }
121         }
122
123         $c->stash->{template} = 'file_upload.html';
124     }
125
126 C<for my $field ($c-E<gt>req->upload)> loops automatically over all file
127 input fields and gets input names. After that is basic file saving code,
128 just like in single file upload.
129
130 Notice: C<die>ing might not be what you want to do, when an error
131 occurs, but it works as an example. A better idea would be to store
132 error C<$!> in $c->stash->{error} and show a custom error template
133 displaying this message.
134
135 For more information about uploads and usable methods look at
136 L<Catalyst::Request::Upload> and L<Catalyst::Request>.
137
138 =head2 Authentication with Catalyst::Plugin::Authentication
139
140 In this example, we'll use the
141 L<Catalyst::Plugin::Authentication::Store::DBIC> store and the
142 L<Catalyst::Plugin::Authentication::Credential::Password> credentials.
143
144 In the lib/MyApp.pm package, we'll need to change the C<use Catalyst;>
145 line to include the following modules:
146
147     use Catalyst qw/
148         ConfigLoader 
149         Authentication
150         Authentication::Store::DBIC
151         Authentication::Credential::Password
152         Session
153         Session::Store::FastMmap
154         Session::State::Cookie
155         HTML::Widget
156         Static::Simple
157         /;
158
159 The Session, Session::Store::* and Session::State::* modules listed above
160 ensure that we stay logged-in across multiple page-views.
161
162 In our MyApp.yml configuration file, we'll need to add:
163
164     authentication:
165       dbic:
166         user_class: MyApp::Model::DBIC::User
167         user_field: username
168         password_field: password
169         password_type: hashed
170         password_hash_type: SHA-1
171
172 'user_class' is a DBIx::Class package for your users table.
173 'user_field' tells which field (column) is used for username lookup.
174 'password_field' is the password field in your table.
175 The above settings for 'password_type' and 'password_hash_type' ensure that
176 the password won't be stored in the database in clear text.
177
178 In SQLite, the users table might be something like:
179
180     CREATE TABLE user (
181         id       INTEGER PRIMARY KEY,
182         username VARCHAR(100),
183         password VARCHAR(100)
184     );
185
186 Now we need to create a DBIC::SchemaLoader component for this database
187 (changing "myapp.db" to wherever your SQLite database is).
188
189     script/myapp_create.pl model DBIC DBIC::SchemaLoader 'dbi:SQLite:myapp.db'
190
191 Now we can start creating our page controllers and templates.
192 For our homepage, we create the file "root/index.tt" containing:
193
194     <html>
195     <body>
196     [% IF c.user %]
197         <p>hello [% c.user.username %]</p>
198         <p><a href="[% c.uri_for( '/logout' ) %]">logout</a></p>
199     [% ELSE %]
200         <p><a href="[% c.uri_for( '/login' ) %]">login</a></p>
201     [% END %]
202     </body>
203     </html>
204
205 If the user is logged in, they will be shown their name, and a logout link.
206 Otherwise, they will be shown a login link.
207
208 To display the homepage, we can uncomment the C<default> and C<end>
209 subroutines in lib/MyApp/Controller/Root.pm and populate them as so:
210
211     sub default : Private {
212         my ( $self, $c ) = @_;
213     
214         $c->stash->{template} = 'index.tt';
215     }
216
217     sub end : Private {
218         my ( $self, $c ) = @_;
219     
220         $c->forward( $c->view('TT') )
221             unless $c->response->body || $c->response->redirect;
222     }
223
224 The login template is very simple, as L<HTML::Widget> will handle the
225 HTML form creation for use. This is saved as "root/login.tt".
226
227     <html>
228     <head>
229     <link href="[% c.uri_for('/static/simple.css') %]" rel="stylesheet" type="text/css">
230     </head>
231     <body>
232     [% result %]
233     </body>
234     </html>
235
236 For the HTML form to look correct, we also copy the C<simple.css> file
237 from the L<HTML::Widget> distribution into our "root/static" folder.
238 This file is automatically server by the L<Catalyst::Plugin::Static::Simple>
239 module which we loaded in our lib/MyApp.pm package.
240
241 To handle login requests, we first create a controller, like so:
242
243     script/myapp_create.pl controller Login
244
245 In the lib/MyApp/Controller/Login.pm package, we can then uncomment the
246 C<default> subroutine, and populate it, as below.
247
248 First the widget is created, it needs the 'action' set, and 'username' and
249 'password' fields and a submit button added.
250
251 Then, if we've received a username and password in the request, we attempt
252 to login. If successful, we redirect to the homepage; if not the login form
253 will be displayed again.
254
255     sub default : Private {
256         my ( $self, $c ) = @_;
257     
258         $c->widget->method('POST')->action( $c->uri_for('/login') );
259         $c->widget->element( 'Textfield', 'username' )->label( 'Username' );
260         $c->widget->element( 'Password', 'password' )->label( 'Password' );
261         $c->widget->element( 'Submit' )->value( 'Login' );
262         
263         my $result = $c->widget->process( $c->req );
264         
265         if ( my $user = $result->param('username')
266             and my $pass = $result->param('password') )
267         {    
268             if ( $c->login( $user, $pass ) ) {
269                 $c->response->redirect( $c->uri_for( "/" ) );
270                 return;
271             }
272         }
273         
274         $c->stash->{template} = 'login.tt';
275         $c->stash->{result}   = $result;
276     }
277
278 To handle logout's, we create a new controller:
279
280     script/myapp_create.pl controller Logout
281
282 Then in the lib/MyApp/Controller/Logout.pm package, we change the
283 C<default> subroutine, to logout and then redirect back to the
284 homepage.
285
286     sub default : Private {
287         my ( $self, $c ) = @_;
288     
289         $c->logout;
290         
291         $c->response->redirect( $c->uri_for( "/" ) );
292     }
293
294 Remember that to test this, we would first need to add a user to the
295 database, ensuring that the password field is saved as the SHA1 hash
296 of our desired password.
297
298
299 =head2 Pass-through login (and other actions)
300
301 An easy way of having assorted actions that occur during the processing
302 of a request that are orthogonal to its actual purpose - logins, silent
303 commands etc. Provide actions for these, but when they're required for
304 something else fill e.g. a form variable __login and have a sub begin
305 like so:
306
307     sub begin : Private {
308       my ($self, $c) = @_;
309       foreach my $action (qw/login docommand foo bar whatever/) {
310         if ($c->req->params->{"__${action}"}) {
311           $c->forward($action);
312         }
313       }
314     }
315
316 =head2 How to use Catalyst without mod_perl
317
318 Catalyst applications give optimum performance when run under mod_perl.
319 However sometimes mod_perl is not an option, and running under CGI is 
320 just too slow.  There's also an alternative to mod_perl that gives
321 reasonable performance named FastCGI.
322
323 =head3 Using FastCGI
324
325 To quote from L<http://www.fastcgi.com/>: "FastCGI is a language 
326 independent, scalable, extension to CGI that provides high performance 
327 without the limitations of specific server APIs."  Web server support 
328 is provided for Apache in the form of C<mod_fastcgi> and there is Perl
329 support in the C<FCGI> module.  To convert a CGI Catalyst application 
330 to FastCGI one needs to initialize an C<FCGI::Request> object and loop 
331 while the C<Accept> method returns zero.  The following code shows how 
332 it is done - and it also works as a normal, single-shot CGI script.
333
334     #!/usr/bin/perl
335     use strict;
336     use FCGI;
337     use MyApp;
338
339     my $request = FCGI::Request();
340     while ($request->Accept() >= 0) {
341         MyApp->run;
342     }
343
344 Any initialization code should be included outside the request-accept 
345 loop.
346
347 There is one little complication, which is that C<MyApp-E<gt>run> outputs a
348 complete HTTP response including the status line (e.g.: 
349 "C<HTTP/1.1 200>").
350 FastCGI just wants a set of headers, so the sample code captures the 
351 output and  drops the first line if it is an HTTP status line (note: 
352 this may change).
353
354 The Apache C<mod_fastcgi> module is provided by a number of Linux 
355 distro's and is straightforward to compile for most Unix-like systems.  
356 The module provides a FastCGI Process Manager, which manages FastCGI 
357 scripts.  You configure your script as a FastCGI script with the 
358 following Apache configuration directives:
359
360     <Location /fcgi-bin>
361        AddHandler fastcgi-script fcgi
362     </Location>
363
364 or:
365
366     <Location /fcgi-bin>
367        SetHandler fastcgi-script
368        Action fastcgi-script /path/to/fcgi-bin/fcgi-script
369     </Location>
370
371 C<mod_fastcgi> provides a number of options for controlling the FastCGI
372 scripts spawned; it also allows scripts to be run to handle the
373 authentication, authorization, and access check phases.
374
375 For more information see the FastCGI documentation, the C<FCGI> module 
376 and L<http://www.fastcgi.com/>.
377
378 =head2 Serving static content
379
380 Serving static content in Catalyst can be somewhat tricky; this recipe
381 shows one possible solution. Using this recipe will serve all static
382 content through Catalyst when developing with the built-in HTTP::Daemon
383 server, and will make it easy to use Apache to serve the content when
384 your app goes into production.
385
386 Static content is best served from a single directory within your root
387 directory. Having many different directories such as C<root/css> and
388 C<root/images> requires more code to manage, because you must separately
389 identify each static directory--if you decide to add a C<root/js>
390 directory, you'll need to change your code to account for it. In
391 contrast, keeping all static directories as subdirectories of a main
392 C<root/static> directory makes things much easier to manager. Here's an
393 example of a typical root directory structure:
394
395     root/
396     root/content.tt
397     root/controller/stuff.tt
398     root/header.tt
399     root/static/
400     root/static/css/main.css
401     root/static/images/logo.jpg
402     root/static/js/code.js
403
404
405 All static content lives under C<root/static> with everything else being
406 Template Toolkit files. Now you can identify the static content by
407 matching C<static> from within Catalyst.
408
409 =head3 Serving with HTTP::Daemon (myapp_server.pl)
410
411 To serve these files under the standalone server, we first must load the
412 Static plugin. Install L<Catalyst::Plugin::Static> if it's not already
413 installed.
414
415 In your main application class (MyApp.pm), load the plugin:
416
417     use Catalyst qw/-Debug FormValidator Static OtherPlugin/;
418
419 You will also need to make sure your end method does I<not> forward
420 static content to the view, perhaps like this:
421
422     sub end : Private {
423         my ( $self, $c ) = @_;
424
425         $c->forward( 'MyApp::V::TT' ) 
426           unless ( $c->res->body || !$c->stash->{template} );
427     }
428
429 This code will only forward to the view if a template has been
430 previously defined by a controller and if there is not already data in
431 C<$c-E<gt>res-E<gt>body>.
432
433 Next, create a controller to handle requests for the /static path. Use
434 the Helper to save time. This command will create a stub controller as
435 C<lib/MyApp/C/Static.pm>.
436
437     $ script/myapp_create.pl controller Static
438
439 Edit the file and add the following methods:
440
441     # serve all files under /static as static files
442     sub default : Path('/static') {
443         my ( $self, $c ) = @_;
444
445         # Optional, allow the browser to cache the content
446         $c->res->headers->header( 'Cache-Control' => 'max-age=86400' );
447
448         $c->serve_static; # from Catalyst::Plugin::Static
449     }
450
451     # also handle requests for /favicon.ico
452     sub favicon : Path('/favicon.ico') {
453         my ( $self, $c ) = @_;
454
455         $c->serve_static;
456     }
457
458 You can also define a different icon for the browser to use instead of
459 favicon.ico by using this in your HTML header:
460
461     <link rel="icon" href="/static/myapp.ico" type="image/x-icon" />
462
463 =head3 Common problems
464
465 The Static plugin makes use of the C<shared-mime-info> package to
466 automatically determine MIME types. This package is notoriously
467 difficult to install, especially on win32 and OS X. For OS X the easiest
468 path might be to install Fink, then use C<apt-get install
469 shared-mime-info>. Restart the server, and everything should be fine.
470
471 Make sure you are using the latest version (>= 0.16) for best
472 results. If you are having errors serving CSS files, or if they get
473 served as text/plain instead of text/css, you may have an outdated
474 shared-mime-info version. You may also wish to simply use the following
475 code in your Static controller:
476
477     if ($c->req->path =~ /css$/i) {
478         $c->serve_static( "text/css" );
479     } else {
480         $c->serve_static;
481     }
482
483 =head3 Serving with Apache
484
485 When using Apache, you can completely bypass Catalyst and the Static
486 controller by intercepting requests for the C<root/static> path at the
487 server level. All that is required is to define a DocumentRoot and add a
488 separate Location block for your static content. Here is a complete
489 config for this application under mod_perl 1.x:
490
491     <Perl>
492         use lib qw(/var/www/MyApp/lib);
493     </Perl>
494     PerlModule MyApp
495
496     <VirtualHost *>
497         ServerName myapp.example.com
498         DocumentRoot /var/www/MyApp/root
499         <Location />
500             SetHandler perl-script
501             PerlHandler MyApp
502         </Location>
503         <LocationMatch "/(static|favicon.ico)">
504             SetHandler default-handler
505         </LocationMatch>
506     </VirtualHost>
507
508 And here's a simpler example that'll get you started:
509
510     Alias /static/ "/my/static/files/"
511     <Location "/static">
512         SetHandler none
513     </Location>
514
515 =head2 Forwarding with arguments
516
517 Sometimes you want to pass along arguments when forwarding to another
518 action. As of version 5.30, arguments can be passed in the call to
519 C<forward>; in earlier versions, you can manually set the arguments in
520 the Catalyst Request object:
521
522   # version 5.30 and later:
523   $c->forward('/wherever', [qw/arg1 arg2 arg3/]);
524
525   # pre-5.30
526   $c->req->args([qw/arg1 arg2 arg3/]);
527   $c->forward('/wherever');
528
529 (See the L<Catalyst::Manual::Intro> Flow_Control section for more 
530 information on passing arguments via C<forward>.)
531
532 =head2 Configure your application
533
534 You configure your application with the C<config> method in your
535 application class. This can be hard-coded, or brought in from a
536 separate configuration file.
537
538 =head3 Using YAML
539
540 YAML is a method for creating flexible and readable configuration
541 files. It's a great way to keep your Catalyst application configuration
542 in one easy-to-understand location.
543
544 In your application class (e.g. C<lib/MyApp.pm>):
545
546   use YAML;
547   # application setup
548   __PACKAGE__->config( YAML::LoadFile(__PACKAGE__->config->{'home'} . '/myapp.yml') );
549   __PACKAGE__->setup;
550
551 Now create C<myapp.yml> in your application home:
552
553   --- #YAML:1.0
554   # DO NOT USE TABS FOR INDENTATION OR label/value SEPARATION!!!
555   name:     MyApp
556
557   # session; perldoc Catalyst::Plugin::Session::FastMmap
558   session:
559     expires:        '3600'
560     rewrite:        '0'
561     storage:        '/tmp/myapp.session'
562
563   # emails; perldoc Catalyst::Plugin::Email
564   # this passes options as an array :(
565   email:
566     - SMTP
567     - localhost
568
569 This is equivalent to:
570
571   # configure base package
572   __PACKAGE__->config( name => MyApp );
573   # configure authentication
574   __PACKAGE__->config->{authentication} = {
575     user_class => 'MyApp::M::MyDB::Customer',
576     ...
577   };
578   # configure sessions
579   __PACKAGE__->config->{session} = {
580     expires => 3600,
581     ...
582   };
583   # configure email sending
584   __PACKAGE__->config->{email} = [qw/SMTP localhost/];
585
586 See also L<YAML>.
587
588 =head2 Using existing DBIC (etc.) classes with Catalyst
589
590 Many people have existing Model classes that they would like to use with
591 Catalyst (or, conversely, they want to write Catalyst models that can be
592 used outside of Catalyst, e.g.  in a cron job). It's trivial to write a
593 simple component in Catalyst that slurps in an outside Model:
594
595     package MyApp::Model::DB;
596     use base qw/Catalyst::Model::DBIC::Schema/;
597     __PACKAGE__->config(
598         schema_class => 'Some::DBIC::Schema',
599         connect_info => ['dbi:SQLite:foo.db', '', '', {AutoCommit=>1}];
600     );
601     1;
602
603 and that's it! Now C<Some::DBIC::Schema> is part of your
604 Cat app as C<MyApp::Model::DB>.
605
606 =head2 Delivering a Custom Error Page
607
608 By default, Catalyst will display its own error page whenever it
609 encounters an error in your application. When running under C<-Debug>
610 mode, the error page is a useful screen including the error message and
611 a full Data::Dumper output of the C<$c> context object. When not in
612 C<-Debug>, users see a simple "Please come back later" screen.
613
614 To use a custom error page, use a special C<end> method to short-circuit
615 the error processing. The following is an example; you might want to
616 adjust it further depending on the needs of your application (for
617 example, any calls to C<fillform> will probably need to go into this
618 C<end> method; see L<Catalyst::Plugin::FillInForm>).
619
620     sub end : Private {
621         my ( $self, $c ) = @_;
622
623         if ( scalar @{ $c->error } ) {
624             $c->stash->{errors}   = $c->error;
625             $c->stash->{template} = 'errors.tt';
626             $c->forward('MyApp::View::TT');
627             $c->error(0);
628         }
629
630         return 1 if $c->response->status =~ /^3\d\d$/;
631         return 1 if $c->response->body;
632
633         unless ( $c->response->content_type ) {
634             $c->response->content_type('text/html; charset=utf-8');
635         }
636
637         $c->forward('MyApp::View::TT');
638     }
639
640 You can manually set errors in your code to trigger this page by calling
641
642     $c->error( 'You broke me!' );
643
644 =head2 Require user logins
645
646 It's often useful to restrict access to your application to a set of
647 registered users, forcing everyone else to the login page until they're
648 signed in.
649
650 To implement this in your application make sure you have a customer
651 table with username and password fields and a corresponding Model class
652 in your Catalyst application, then make the following changes:
653
654 =head3 lib/MyApp.pm
655
656   use Catalyst qw/
657       Authentication
658       Authentication::Store::DBIC
659       Authentication::Credential::Password
660   /;
661
662   __PACKAGE__->config->{authentication}->{dbic} = {
663     'user_class'        => 'My::Model::DBIC::User',
664     'user_field'        => 'username',
665     'password_field'    => 'password'
666     'password_type'     => 'hashed',
667     'password_hash_type'=> 'SHA-1'
668   };
669
670   sub auto : Private {
671     my ($self, $c) = @_;
672     my $login_path = 'user/login';
673
674     # allow people to actually reach the login page!
675     if ($c->request->path eq $login_path) {
676       return 1;
677     }
678
679     # if a user doesn't exist, force login
680     if ( !$c->user_exists ) {
681       # force the login screen to be shown
682       $c->response->redirect($c->request->base . $login_path);
683     }
684
685     # otherwise, we have a user - continue with the processing chain
686     return 1;
687   }
688
689 =head3 lib/MyApp/C/User.pm
690
691   sub login : Path('/user/login') {
692     my ($self, $c) = @_;
693
694     # default template
695     $c->stash->{'template'} = "user/login.tt";
696     # default form message
697     $c->stash->{'message'} = 'Please enter your username and password';
698
699     if ( $c->request->param('username') ) {
700       # try to log the user in
701       # login() is provided by ::Authentication::Credential::Password
702       if( $c->login(
703         $c->request->param('username'),
704         $c->request->param('password'),
705         );
706
707         # if login() returns 1, user is now logged in
708         $c->response->redirect('/some/page');
709       }
710
711       # otherwise we failed to login, try again!
712       $c->stash->{'message'} = 
713          'Unable to authenticate the login details supplied';
714     }
715   }
716
717   sub logout : Path('/user/logout') {
718     my ($self, $c) = @_;
719     # log the user out
720     $c->logout;
721
722     # do the 'default' action
723     $c->response->redirect($c->request->base);
724   }
725
726
727 =head3 root/base/user/login.tt
728
729  [% INCLUDE header.tt %]
730  <form action="/user/login" method="POST" name="login_form">
731     [% message %]<br />
732     <label for="username">username:</label><br />
733     <input type="text" id="username" name="username" /><br />
734
735     <label for="password">password:</label><br />
736     <input type="password" id="password" name="password" /><br />
737
738     <input type="submit" value="log in" name="form_submit" />
739   </form>
740   [% INCLUDE footer.tt %]
741
742 =head2 Role-based Authorization
743
744 For more advanced access control, you may want to consider using role-based
745 authorization. This means you can assign different roles to each user, e.g.
746 "user", "admin", etc.
747
748 The C<login> and C<logout> methods and view template are exactly the same as
749 in the previous example.
750
751 The L<Catalyst::Plugin::Authorization::Roles> plugin is required when
752 implementing roles:
753
754  use Catalyst qw/
755     Authentication
756     Authentication::Credential::Password
757     Authentication::Store::Htpasswd
758     Authorization::Roles
759   /;
760
761 Roles are implemented automatically when using
762 L<Catalyst::Authentication::Store::Htpasswd>:
763
764   # no additional role configuration required
765   __PACKAGE__->config->{authentication}{htpasswd} = "passwdfile";
766
767 Or can be set up manually when using L<Catalyst::Authentication::Store::DBIC>:
768
769   # Authorization using a many-to-many role relationship
770   __PACKAGE__->config->{authorization}{dbic} = {
771     'role_class'           => 'My::Model::DBIC::Role',
772     'role_field'           => 'name',
773     'user_role_user_field' => 'user',
774
775     # DBIx::Class only (omit if using Class::DBI)
776     'role_rel'             => 'user_role',
777
778     # Class::DBI only, (omit if using DBIx::Class)
779     'user_role_class'      => 'My::Model::CDBI::UserRole'
780     'user_role_role_field' => 'role',
781   };
782
783 To restrict access to any action, you can use the C<check_user_roles> method:
784
785   sub restricted : Local {
786      my ( $self, $c ) = @_;
787
788      $c->detach("unauthorized")
789        unless $c->check_user_roles( "admin" );
790
791      # do something restricted here
792   }
793
794 You can also use the C<assert_user_roles> method. This just gives an error if
795 the current user does not have one of the required roles:
796
797   sub also_restricted : Global {
798     my ( $self, $c ) = @_;
799     $c->assert_user_roles( qw/ user admin / );
800   }
801
802 =head1 AUTHOR
803
804 Sebastian Riedel, C<sri@oook.de>
805 Danijel Milicevic, C<me@danijel.de>
806 Viljo Marrandi, C<vilts@yahoo.com>  
807 Marcus Ramberg, C<mramberg@cpan.org>
808 Jesse Sheidlower, C<jester@panix.com>
809 Andy Grundman, C<andy@hybridized.org> 
810 Chisel Wright, C<pause@herlpacker.co.uk>
811 Will Hawes, C<info@whawes.co.uk>
812 Gavin Henry, C<ghenry@cpan.org> (Spell checking)
813
814
815 =head1 COPYRIGHT
816
817 This program is free software, you can redistribute it and/or modify it
818 under the same terms as Perl itself.