Spell checking and formatting, with some grammar corrections.
[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::CDBI
139
140 There are (at least) two ways to implement authentication with this plugin:
141 1) only checking username and password;
142 2) checking username, password, and the roles the user has
143
144 For both variants you'll need the following code in your MyApp package:
145
146     use Catalyst qw/Session::FastMmap Static Authentication::CDBI/;
147
148     MyApp->config( authentication => { user_class => 'MyApp::M::MyApp::Users',
149                                        user_field => 'email',
150                                        password_field => 'password' });
151
152 'user_class' is a Class::DBI class for your users table.
153 'user_field' tells which field is used for username lookup (might be 
154 email, first name, surname etc.).
155 'password_field' is, well, password field in your table and by default 
156 password is stored in plain text. Authentication::CDBI looks for 'user' 
157 and 'password' fields in table, if they're not defined in the config.
158
159 In PostgreSQL, the users table might be something like:
160
161  CREATE TABLE users (
162    user_id   serial,
163    name      varchar(100),
164    surname   varchar(100),
165    password  varchar(100),
166    email     varchar(100),
167    primary key(user_id)
168  );
169
170 We'll discuss the first variant for now:
171 1. user:password login/auth without roles
172
173 To log in a user you might use an action like this:
174
175     sub login : Local {
176         my ($self, $c) = @_;
177         if ($c->req->params->{username}) {
178             $c->session_login($c->req->params->{username}, 
179                               $c->req->params->{password} );
180             if ($c->req->{user}) {
181                 $c->forward('/restricted_area');
182             }
183         }
184     }
185
186 This action should not go in your MyApp class...if it does, it will
187 conflict with the built-in method of the same name.  Instead, put it
188 in a Controller class.
189
190 $c->req->params->{username} and $c->req->params->{password} are html 
191 form parameters from a login form. If login succeeds, then 
192 $c->req->{user} contains the username of the authenticated user.
193
194 If you want to remember the user's login status in between further 
195 requests, then just use the C<$c-E<gt>session_login> method. Catalyst will 
196 create a session id and session cookie and automatically append session 
197 id to all urls. So all you have to do is just check $c->req->{user} 
198 where needed.
199
200 To log out a user, just call $c->session_logout.
201
202 Now let's take a look at the second variant:
203 2. user:password login/auth with roles
204
205 To use roles you need to add the following parameters to  MyApp->config in the 'authentication' section:
206
207     role_class      => 'MyApp::M::MyApp::Roles',
208     user_role_class => 'MyApp::M::MyApp::UserRoles',
209     user_role_user_field => 'user_id',
210     user_role_role_field => 'role_id',
211
212 Corresponding tables in PostgreSQL could look like this:
213
214  CREATE TABLE roles (
215    role_id  serial,
216    name     varchar(100),
217    primary key(role_id)
218  );
219
220  CREATE TABLE user_roles (
221    user_role_id  serial,
222    user_id       int,
223    role_id       int,
224    primary key(user_role_id),
225    foreign key(user_id) references users(user_id),
226    foreign key(role_id) references roles(role_id)
227  );
228
229 The 'roles' table is a list of role names and the 'user_role' table is 
230 used for the user -> role lookup.
231
232 Now if a logged-in user wants to see a location which is allowed only 
233 for people with an 'admin' role, in your controller you can check it 
234 with:
235
236     sub add : Local {
237         my ($self, $c) = @_;
238         if ($c->roles(qw/admin/)) {
239             $c->res->output("Your account has the role 'admin.'");
240         } else {
241             $c->res->output("You're not allowed to be here.");
242         }
243     }
244
245 One thing you might need is to forward non-authenticated users to a login 
246 form if they try to access restricted areas. If you want to do this 
247 controller-wide (if you have one controller for your admin section) then it's 
248 best to add a user check to a 'begin' action:
249
250     sub begin : Private {
251         my ($self, $c) = @_;
252         unless ($c->req->{user}) {
253             $c->req->action(undef);  ## notice this!!
254             $c->forward('/user/login');
255         }
256     }
257
258 Pay attention to $c->req->action(undef). This is needed because of the
259 way $c->forward works - C<forward> to C<login> gets called, but after
260 that Catalyst will still execute the action defined in the URI (e.g. if
261 you tried to go to C</add>, then first 'begin' will forward to 'login',
262 but after that 'add' will nonetheless be executed). So
263 $c->req->action(undef) undefines any actions that were to be called and
264 forwards the user where we want him/her to be.
265
266 And this is all you need to do. 
267
268 =head2 Pass-through login (and other actions)
269
270 An easy way of having assorted actions that occur during the processing
271 of a request that are orthogonal to its actual purpose - logins, silent
272 commands etc. Provide actions for these, but when they're required for
273 something else fill e.g. a form variable __login and have a sub begin
274 like so:
275
276     sub begin : Private {
277       my ($self, $c) = @_;
278       foreach my $action (qw/login docommand foo bar whatever/) {
279         if ($c->req->params->{"__${action}"}) {
280           $c->forward($action);
281         }
282       }
283     }
284
285 =head2 How to use Catalyst without mod_perl
286
287 Catalyst applications give optimum performance when run under mod_perl.
288 However sometimes mod_perl is not an option, and running under CGI is 
289 just too slow.  There's also an alternative to mod_perl that gives
290 reasonable performance named FastCGI.
291
292 =head3 Using FastCGI
293
294 To quote from L<http://www.fastcgi.com/>: "FastCGI is a language 
295 independent, scalable, extension to CGI that provides high performance 
296 without the limitations of specific server APIs."  Web server support 
297 is provided for Apache in the form of C<mod_fastcgi> and there is Perl
298 support in the C<FCGI> module.  To convert a CGI Catalyst application 
299 to FastCGI one needs to initialize an C<FCGI::Request> object and loop 
300 while the C<Accept> method returns zero.  The following code shows how 
301 it is done - and it also works as a normal, single-shot CGI script.
302
303     #!/usr/bin/perl
304     use strict;
305     use FCGI;
306     use MyApp;
307
308     my $request = FCGI::Request();
309     while ($request->Accept() >= 0) {
310         MyApp->run;
311     }
312
313 Any initialization code should be included outside the request-accept 
314 loop.
315
316 There is one little complication, which is that C<MyApp-E<gt>run> outputs a
317 complete HTTP response including the status line (e.g.: 
318 "C<HTTP/1.1 200>").
319 FastCGI just wants a set of headers, so the sample code captures the 
320 output and  drops the first line if it is an HTTP status line (note: 
321 this may change).
322
323 The Apache C<mod_fastcgi> module is provided by a number of Linux 
324 distro's and is straightforward to compile for most Unix-like systems.  
325 The module provides a FastCGI Process Manager, which manages FastCGI 
326 scripts.  You configure your script as a FastCGI script with the 
327 following Apache configuration directives:
328
329     <Location /fcgi-bin>
330        AddHandler fastcgi-script fcgi
331     </Location>
332
333 or:
334
335     <Location /fcgi-bin>
336        SetHandler fastcgi-script
337        Action fastcgi-script /path/to/fcgi-bin/fcgi-script
338     </Location>
339
340 C<mod_fastcgi> provides a number of options for controlling the FastCGI
341 scripts spawned; it also allows scripts to be run to handle the
342 authentication, authorization, and access check phases.
343
344 For more information see the FastCGI documentation, the C<FCGI> module 
345 and L<http://www.fastcgi.com/>.
346
347 =head2 Serving static content
348
349 Serving static content in Catalyst can be somewhat tricky; this recipe
350 shows one possible solution. Using this recipe will serve all static
351 content through Catalyst when developing with the built-in HTTP::Daemon
352 server, and will make it easy to use Apache to serve the content when
353 your app goes into production.
354
355 Static content is best served from a single directory within your root
356 directory. Having many different directories such as C<root/css> and
357 C<root/images> requires more code to manage, because you must separately
358 identify each static directory--if you decide to add a C<root/js>
359 directory, you'll need to change your code to account for it. In
360 contrast, keeping all static directories as subdirectories of a main
361 C<root/static> directory makes things much easier to manager. Here's an
362 example of a typical root directory structure:
363
364     root/
365     root/content.tt
366     root/controller/stuff.tt
367     root/header.tt
368     root/static/
369     root/static/css/main.css
370     root/static/images/logo.jpg
371     root/static/js/code.js
372
373
374 All static content lives under C<root/static> with everything else being
375 Template Toolkit files. Now you can identify the static content by
376 matching C<static> from within Catalyst.
377
378 =head3 Serving with HTTP::Daemon (myapp_server.pl)
379
380 To serve these files under the standalone server, we first must load the
381 Static plugin. Install L<Catalyst::Plugin::Static> if it's not already
382 installed.
383
384 In your main application class (MyApp.pm), load the plugin:
385
386     use Catalyst qw/-Debug FormValidator Static OtherPlugin/;
387
388 You will also need to make sure your end method does I<not> forward
389 static content to the view, perhaps like this:
390
391     sub end : Private {
392         my ( $self, $c ) = @_;
393
394         $c->forward( 'MyApp::V::TT' ) 
395           unless ( $c->res->body || !$c->stash->{template} );
396     }
397
398 This code will only forward to the view if a template has been
399 previously defined by a controller and if there is not already data in
400 C<$c-E<gt>res-E<gt>body>.
401
402 Next, create a controller to handle requests for the /static path. Use
403 the Helper to save time. This command will create a stub controller as
404 C<lib/MyApp/C/Static.pm>.
405
406     $ script/myapp_create.pl controller Static
407
408 Edit the file and add the following methods:
409
410     # serve all files under /static as static files
411     sub default : Path('/static') {
412         my ( $self, $c ) = @_;
413
414         # Optional, allow the browser to cache the content
415         $c->res->headers->header( 'Cache-Control' => 'max-age=86400' );
416
417         $c->serve_static; # from Catalyst::Plugin::Static
418     }
419
420     # also handle requests for /favicon.ico
421     sub favicon : Path('/favicon.ico') {
422         my ( $self, $c ) = @_;
423
424         $c->serve_static;
425     }
426
427 You can also define a different icon for the browser to use instead of
428 favicon.ico by using this in your HTML header:
429
430     <link rel="icon" href="/static/myapp.ico" type="image/x-icon" />
431
432 =head3 Common problems
433
434 The Static plugin makes use of the C<shared-mime-info> package to
435 automatically determine MIME types. This package is notoriously
436 difficult to install, especially on win32 and OS X. For OS X the easiest
437 path might be to install Fink, then use C<apt-get install
438 shared-mime-info>. Restart the server, and everything should be fine.
439
440 Make sure you are using the latest version (>= 0.16) for best
441 results. If you are having errors serving CSS files, or if they get
442 served as text/plain instead of text/css, you may have an outdated
443 shared-mime-info version. You may also wish to simply use the following
444 code in your Static controller:
445
446     if ($c->req->path =~ /css$/i) {
447         $c->serve_static( "text/css" );
448     } else {
449         $c->serve_static;
450     }
451
452 =head3 Serving with Apache
453
454 When using Apache, you can completely bypass Catalyst and the Static
455 controller by intercepting requests for the C<root/static> path at the
456 server level. All that is required is to define a DocumentRoot and add a
457 separate Location block for your static content. Here is a complete
458 config for this application under mod_perl 1.x:
459
460     <Perl>
461         use lib qw(/var/www/MyApp/lib);
462     </Perl>
463     PerlModule MyApp
464
465     <VirtualHost *>
466         ServerName myapp.example.com
467         DocumentRoot /var/www/MyApp/root
468         <Location />
469             SetHandler perl-script
470             PerlHandler MyApp
471         </Location>
472         <LocationMatch "/(static|favicon.ico)">
473             SetHandler default-handler
474         </LocationMatch>
475     </VirtualHost>
476
477 And here's a simpler example that'll get you started:
478
479     Alias /static/ "/my/static/files/"
480     <Location "/static">
481         SetHandler none
482     </Location>
483
484 =head2 Forwarding with arguments
485
486 Sometimes you want to pass along arguments when forwarding to another
487 action. As of version 5.30, arguments can be passed in the call to
488 C<forward>; in earlier versions, you can manually set the arguments in
489 the Catalyst Request object:
490
491   # version 5.30 and later:
492   $c->forward('/wherever', [qw/arg1 arg2 arg3/]);
493
494   # pre-5.30
495   $c->req->args([qw/arg1 arg2 arg3/]);
496   $c->forward('/wherever');
497
498 (See the L<Catalyst::Manual::Intro> Flow_Control section for more 
499 information on passing arguments via C<forward>.)
500
501 =head2 Configure your application
502
503 You configure your application with the C<config> method in your
504 application class. This can be hard-coded, or brought in from a
505 separate configuration file.
506
507 =head3 Using YAML
508
509 YAML is a method for creating flexible and readable configuration
510 files. It's a great way to keep your Catalyst application configuration
511 in one easy-to-understand location.
512
513 In your application class (e.g. C<lib/MyApp.pm>):
514
515   use YAML;
516   # application setup
517   __PACKAGE__->config( YAML::LoadFile(__PACKAGE__->config->{'home'} . '/myapp.yml') );
518   __PACKAGE__->setup;
519
520 Now create C<myapp.yml> in your application home:
521
522   --- #YAML:1.0
523   # DO NOT USE TABS FOR INDENTATION OR label/value SEPARATION!!!
524   name:     MyApp
525
526   # authentication; perldoc Catalyst::Plugin::Authentication::CDBI
527   authentication:
528     user_class:           'MyApp::M::MyDB::Customer'
529     user_field:           'username'
530     password_field:       'password'
531     password_hash:        'md5'
532     role_class:           'MyApp::M::MyDB::Role'
533     user_role_class:      'MyApp::M::MyDB::PersonRole'
534     user_role_user_field: 'person'
535
536   # session; perldoc Catalyst::Plugin::Session::FastMmap
537   session:
538     expires:        '3600'
539     rewrite:        '0'
540     storage:        '/tmp/myapp.session'
541
542   # emails; perldoc Catalyst::Plugin::Email
543   # this passes options as an array :(
544   email:
545     - SMTP
546     - localhost
547
548 This is equivalent to:
549
550   # configure base package
551   __PACKAGE__->config( name => MyApp );
552   # configure authentication
553   __PACKAGE__->config->{authentication} = {
554     user_class => 'MyApp::M::MyDB::Customer',
555     ...
556   };
557   # configure sessions
558   __PACKAGE__->config->{session} = {
559     expires => 3600,
560     ...
561   };
562   # configure email sending
563   __PACKAGE__->config->{email} = [qw/SMTP localhost/];
564
565 See also L<YAML>.
566
567 =head2 Using existing CDBI (etc.) classes with Catalyst
568
569 Many people have existing Model classes that they would like to use with
570 Catalyst (or, conversely, they want to write Catalyst models that can be
571 used outside of Catalyst, e.g.  in a cron job). It's trivial to write a
572 simple component in Catalyst that slurps in an outside Model:
573
574     package MyApp::M::Catalog;
575     use base qw/Catalyst::Base Some::Other::CDBI::Module::Catalog/;
576     1;
577
578 and that's it! Now C<Some::Other::CDBI::Module::Catalog> is part of your
579 Cat app as C<MyApp::M::Catalog>.
580
581 =head2 Delivering a Custom Error Page
582
583 By default, Catalyst will display its own error page whenever it
584 encounters an error in your application. When running under C<-Debug>
585 mode, the error page is a useful screen including the error message and
586 a full Data::Dumper output of the C<$c> context object. When not in
587 C<-Debug>, users see a simple "Please come back later" screen.
588
589 To use a custom error page, use a special C<end> method to short-circuit
590 the error processing. The following is an example; you might want to
591 adjust it further depending on the needs of your application (for
592 example, any calls to C<fillform> will probably need to go into this
593 C<end> method; see L<Catalyst::Plugin::FillInForm>).
594
595     sub end : Private {
596         my ( $self, $c ) = @_;
597
598         if ( scalar @{ $c->error } ) {
599             $c->stash->{errors}   = $c->error;
600             $c->stash->{template} = 'errors.tt';
601             $c->forward('MyApp::View::TT');
602             $c->error(0);
603         }
604
605         return 1 if $c->response->status =~ /^3\d\d$/;
606         return 1 if $c->response->body;
607
608         unless ( $c->response->content_type ) {
609             $c->response->content_type('text/html; charset=utf-8');
610         }
611
612         $c->forward('MyApp::View::TT');
613     }
614
615 You can manually set errors in your code to trigger this page by calling
616
617     $c->error( 'You broke me!' );
618
619 =head2 Require user logins
620
621 It's often useful to restrict access to your application to a set of
622 registered users, forcing everyone else to the login page until they're
623 signed in.
624
625 To implement this in your application make sure you have a customer
626 table with username and password fields and a corresponding Model class
627 in your Catalyst application, then make the following changes:
628
629 =head3 lib/MyApp.pm
630
631   use Catalyst qw/
632       Authentication
633       Authentication::Store::DBIC
634       Authentication::Credential::Password
635   /;
636
637   __PACKAGE__->config->{authentication}->{dbic} = {
638     'user_class'        => 'My::Model::DBIC::User',
639     'user_field'        => 'username',
640     'password_field'    => 'password'
641     'password_type'     => 'hashed',
642     'password_hash_type'=> 'SHA-1'
643   };
644
645   sub auto : Private {
646     my ($self, $c) = @_;
647     my $login_path = 'user/login';
648
649     # allow people to actually reach the login page!
650     if ($c->request->path eq $login_path) {
651       return 1;
652     }
653
654     # if a user doesn't exist, force login
655     if ( !$c->user_exists ) {
656       # force the login screen to be shown
657       $c->response->redirect($c->request->base . $login_path);
658     }
659
660     # otherwise, we have a user - continue with the processing chain
661     return 1;
662   }
663
664 =head3 lib/MyApp/C/User.pm
665
666   sub login : Path('/user/login') {
667     my ($self, $c) = @_;
668
669     # default template
670     $c->stash->{'template'} = "user/login.tt";
671     # default form message
672     $c->stash->{'message'} = 'Please enter your username and password';
673
674     if ( $c->request->param('username') ) {
675       # try to log the user in
676       # login() is provided by ::Authentication::Credential::Password
677       if( $c->login(
678         $c->request->param('username'),
679         $c->request->param('password'),
680         );
681
682         # if login() returns 1, user is now logged in
683         $c->response->redirect('/some/page');
684       }
685
686       # otherwise we failed to login, try again!
687       $c->stash->{'message'} = 
688          'Unable to authenticate the login details supplied';
689     }
690   }
691
692   sub logout : Path('/user/logout') {
693     my ($self, $c) = @_;
694     # log the user out
695     $c->logout;
696
697     # do the 'default' action
698     $c->response->redirect($c->request->base);
699   }
700
701
702 =head3 root/base/user/login.tt
703
704  [% INCLUDE header.tt %]
705  <form action="/user/login" method="POST" name="login_form">
706     [% message %]<br />
707     <label for="username">username:</label><br />
708     <input type="text" id="username" name="username" /><br />
709
710     <label for="password">password:</label><br />
711     <input type="password" id="password" name="password" /><br />
712
713     <input type="submit" value="log in" name="form_submit" />
714   </form>
715   [% INCLUDE footer.tt %]
716
717 =head2 Role-based Authorization
718
719 For more advanced access control, you may want to consider using role-based
720 authorization. This means you can assign different roles to each user, e.g.
721 "user", "admin", etc.
722
723 The C<login> and C<logout> methods and view template are exactly the same as
724 in the previous example.
725
726 The L<Catalyst::Plugin::Authorization::Roles> plugin is required when
727 implementing roles:
728
729  use Catalyst qw/
730     Authentication
731     Authentication::Credential::Password
732     Authentication::Store::Htpasswd
733     Authorization::Roles
734   /;
735
736 Roles are implemented automatically when using
737 L<Catalyst::Authentication::Store::Htpasswd>:
738
739   # no additional role configuration required
740   __PACKAGE__->config->{authentication}{htpasswd} = "passwdfile";
741
742 Or can be set up manually when using L<Catalyst::Authentication::Store::DBIC>:
743
744   # Authorization using a many-to-many role relationship
745   __PACKAGE__->config->{authorization}{dbic} = {
746     'role_class'           => 'My::Model::DBIC::Role',
747     'role_field'           => 'name',
748     'user_role_user_field' => 'user',
749
750     # DBIx::Class only (omit if using Class::DBI)
751     'role_rel'             => 'user_role',
752
753     # Class::DBI only, (omit if using DBIx::Class)
754     'user_role_class'      => 'My::Model::CDBI::UserRole'
755     'user_role_role_field' => 'role',
756   };
757
758 To restrict access to any action, you can use the C<check_user_roles> method:
759
760   sub restricted : Local {
761      my ( $self, $c ) = @_;
762
763      $c->detach("unauthorized")
764        unless $c->check_user_roles( "admin" );
765
766      # do something restricted here
767   }
768
769 You can also use the C<assert_user_roles> method. This just gives an error if
770 the current user does not have one of the required roles:
771
772   sub also_restricted : Global {
773     my ( $self, $c ) = @_;
774     $c->assert_user_roles( qw/ user admin / );
775   }
776
777 =head1 AUTHOR
778
779 Sebastian Riedel, C<sri@oook.de>
780 Danijel Milicevic, C<me@danijel.de>
781 Viljo Marrandi, C<vilts@yahoo.com>  
782 Marcus Ramberg, C<mramberg@cpan.org>
783 Jesse Sheidlower, C<jester@panix.com>
784 Andy Grundman, C<andy@hybridized.org> 
785 Chisel Wright, C<pause@herlpacker.co.uk>
786 Will Hawes, C<info@whawes.co.uk>
787 Gavin Henry, C<ghenry@cpan.org> (Spell checking)
788
789
790 =head1 COPYRIGHT
791
792 This program is free software, you can redistribute it and/or modify it
793 under the same terms as Perl itself.