X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Manual.git;a=blobdiff_plain;f=lib%2FCatalyst%2FManual%2FCookbook.pod;h=bf6372bc664a3dab5dc107268f4ddc68d6553699;hp=cde45f603a435edd86068158101f60145bc86020;hb=02bb2b5a140dc22d7d002fcdce868656d704676a;hpb=bbddff000787154dd9130f45634da8ef06529d86 diff --git a/lib/Catalyst/Manual/Cookbook.pod b/lib/Catalyst/Manual/Cookbook.pod index cde45f6..bf6372b 100644 --- a/lib/Catalyst/Manual/Cookbook.pod +++ b/lib/Catalyst/Manual/Cookbook.pod @@ -1,3 +1,5 @@ +=encoding utf8 + =head1 NAME Catalyst::Manual::Cookbook - Cooking with Catalyst @@ -11,7 +13,7 @@ Yummy code like your mum used to bake! =head1 Basics These recipes cover some basic stuff that is worth knowing for -catalyst developers. +Catalyst developers. =head2 Delivering a Custom Error Page @@ -33,9 +35,12 @@ to go into this C method; see L). if ( scalar @{ $c->error } ) { $c->stash->{errors} = $c->error; + for my $error ( @{ $c->error } ) { + $c->log->error($error); + } $c->stash->{template} = 'errors.tt'; $c->forward('MyApp::View::TT'); - $c->error(0); + $c->clear_errors; } return 1 if $c->response->status =~ /^3\d\d$/; @@ -65,7 +70,7 @@ Normally you enable the debugging info by adding the C<-Debug> flag to your C statement . However, you can also enable it using environment variable, so you can (for example) get debug info without modifying your application scripts. Just set C or -CMYAPPE_DEBUG> to a true value. +C<< _DEBUG >> to a true value. =head2 Sessions @@ -107,61 +112,59 @@ retrieve the user data for you. =head3 Using a session Once the session modules are loaded, the session is available as C<< -$c->session >>, and can be writen to and read from as a simple hash +$c->session >>, and can be written to and read from as a simple hash reference. =head3 EXAMPLE - package MyApp; - use Moose; - use namespace::autoclean; - - use Catalyst qw/ - Session - Session::Store::FastMmap - Session::State::Cookie - /; - extends 'Catalyst'; - __PACKAGE__->setup; - - package MyApp::Controller::Foo; - use Moose; - use namespace::autoclean; - BEGIN { extends 'Catalyst::Controller'; - ## Write data into the session - - sub add_item : Local { - my ( $self, $c ) = @_; - - my $item_id = $c->req->param("item"); - - push @{ $c->session->{items} }, $item_id; + package MyApp; + use Moose; + use namespace::autoclean; + + use Catalyst qw/ + Session + Session::Store::FastMmap + Session::State::Cookie + /; + extends 'Catalyst'; + __PACKAGE__->setup; + + package MyApp::Controller::Foo; + use Moose; + use namespace::autoclean; + BEGIN { extends 'Catalyst::Controller' }; + ## Write data into the session + + sub add_item : Local { + my ( $self, $c ) = @_; - } + my $item_id = $c->req->params->{item}; - ## A page later we retrieve the data from the session: + push @{ $c->session->{items} }, $item_id; + } - sub get_items : Local { - my ( $self, $c ) = @_; + ## A page later we retrieve the data from the session: - $c->stash->{items_to_display} = $c->session->{items}; + sub get_items : Local { + my ( $self, $c ) = @_; - } + $c->stash->{items_to_display} = $c->session->{items}; + } =head3 More information -L +L -L +L -L +L -L +L -L +L -L +L =head2 Configure your application @@ -171,11 +174,11 @@ separate configuration file. =head3 Using Config::General -L is a method for creating flexible +L is a method for creating flexible and readable configuration files. It's a great way to keep your Catalyst application configuration in one easy-to-understand location. -Now create C in your application home: +Now create F in your application home: name MyApp @@ -196,23 +199,31 @@ This is equivalent to: # configure base package __PACKAGE__->config( name => MyApp ); # configure authentication - __PACKAGE__->config->{authentication} = { - user_class => 'MyApp::Model::MyDB::Customer', - ... - }; + __PACKAGE__->config( + 'Plugin::Authentication' => { + user_class => 'MyApp::Model::MyDB::Customer', + ... + }, + _; # configure sessions - __PACKAGE__->config->{session} = { - expires => 3600, - ... - }; + __PACKAGE__->config( + session => { + expires => 3600, + ... + }, + ); # configure email sending - __PACKAGE__->config->{email} = [qw/SMTP localhost/]; + __PACKAGE__->config( email => [qw/SMTP localhost/] ); + +L explains precedence of multiple sources for configuration +values, how to access the values in your components, and many 'base' +config variables used internally. -See also L. +See also L. =head1 Skipping your VCS's directories -Catalyst uses Module::Pluggable to load Models, Views and Controllers. +Catalyst uses Module::Pluggable to load Models, Views, and Controllers. Module::Pluggable will scan through all directories and load modules it finds. Sometimes you might want to skip some of these directories, for example when your version control system makes a subdirectory with @@ -221,7 +232,7 @@ Catalyst skips subversion and CVS directories already, there are other source control systems. Here is the configuration you need to add their directories to the list to skip. -You can make catalyst skip these directories using the Catalyst config: +You can make Catalyst skip these directories using the Catalyst config: # Configure the application __PACKAGE__->config( @@ -234,7 +245,7 @@ and other options. =head1 Users and Access Control -Most multiuser, and some single user web applications require that +Most multiuser, and some single-user web applications require that users identify themselves, and the application is often required to define those roles. The recipes below describe some ways of doing this. @@ -243,7 +254,7 @@ this. This is extensively covered in other documentation; see in particular L and the Authentication chapter -of the Tutorial at L. +of the Tutorial at L. =head2 Pass-through login (and other actions) @@ -278,7 +289,7 @@ verification>. =item Authorization Making sure the user only accesses functions you want them to -access. This is done by checking the verified users data against your +access. This is done by checking the verified user's data against your internal list of groups, or allowed persons for the current page. =back @@ -297,34 +308,34 @@ C<< $c->user >> call. Examples: - Password - Simple username/password checking. - HTTPD - Checks using basic HTTP auth. - TypeKey - Check using the typekey system. + Password - Simple username/password checking. + HTTPD - Checks using basic HTTP auth. + TypeKey - Check using the typekey system. =head3 Storage backends A Storage backend contains the actual data representing the users. It is queried by the credential verifiers. Updating the store is not done -within this system, you will need to do it yourself. +within this system; you will need to do it yourself. Examples: - DBIC - Storage using a database. - Minimal - Storage using a simple hash (for testing). + DBIC - Storage using a database via DBIx::Class. + Minimal - Storage using a simple hash (for testing). =head3 User objects A User object is created by either the storage backend or the -credential verifier, and filled with the retrieved user information. +credential verifier, and is filled with the retrieved user information. Examples: - Hash - A simple hash of keys and values. + Hash - A simple hash of keys and values. =head3 ACL authorization ACL stands for Access Control List. The ACL plugin allows you to -regulate access on a path by path basis, by listing which users, or +regulate access on a path-by-path basis, by listing which users, or roles, have access to which paths. =head3 Roles authorization @@ -341,82 +352,82 @@ pass it these values. =head3 Checking roles -Role checking is done by using the C<< $c->check_user_roles >> method, -this will check using the currently logged in user (via C<< $c->user +Role checking is done by using the C<< $c->check_user_roles >> method. +This will check using the currently logged-in user (via C<< $c->user >>). You pass it the name of a role to check, and it returns true if the user is a member. =head3 EXAMPLE - package MyApp; - use Moose; - use namespace::autoclean; - extends qw/Catalyst/; - use Catalyst qw/ - Authentication - Authorization::Roles - /; + package MyApp; + use Moose; + use namespace::autoclean; + extends qw/Catalyst/; + use Catalyst qw/ + Authentication + Authorization::Roles + /; - __PACKAGE__->config( - authentication => { - default_realm => 'test', - realms => { - test => { - credential => { - class => 'Password', - password_field => 'password', - password_type => 'self_check', - }, - store => { - class => 'Htpasswd', - file => 'htpasswd', - }, - }, - }, - }, - ); + __PACKAGE__->config( + authentication => { + default_realm => 'test', + realms => { + test => { + credential => { + class => 'Password', + password_field => 'password', + password_type => 'self_check', + }, + store => { + class => 'Htpasswd', + file => 'htpasswd', + }, + }, + }, + }, + ); + + package MyApp::Controller::Root; + use Moose; + use namespace::autoclean; + + BEGIN { extends 'Catalyst::Controller' } + + __PACKAGE__->config(namespace => ''); + + sub login : Local { + my ($self, $c) = @_; + + if ( my $user = $c->req->params->{user} + and my $password = $c->req->param->{password} ) + { + if ( $c->authenticate( username => $user, password => $password ) ) { + $c->res->body( "hello " . $c->user->name ); + } else { + # login incorrect + } + } + else { + # invalid form input + } + } + + sub restricted : Local { + my ( $self, $c ) = @_; + + $c->detach("unauthorized") + unless $c->check_user_roles( "admin" ); - package MyApp::Controller::Root; - use Moose; - use namespace::autoclean; - - BEGIN { extends 'Catalyst::Controller' } - - __PACKAGE__->config(namespace => ''); - - sub login : Local { - my ($self, $c) = @_; - - if ( my $user = $c->req->param("user") - and my $password = $c->req->param("password") ) - { - if ( $c->authenticate( username => $user, password => $password ) ) { - $c->res->body( "hello " . $c->user->name ); - } else { - # login incorrect - } - } - else { - # invalid form input - } - } - - sub restricted : Local { - my ( $self, $c ) = @_; - - $c->detach("unauthorized") - unless $c->check_user_roles( "admin" ); - - # do something restricted here - } + # do something restricted here + } =head3 Using authentication in a testing environment -Ideally, to write tests for authentication/authorization code one -would first set up a test database with known data, then use +Ideally, to write tests for authentication/authorization code one would +first set up a test database with known data, then use L to simulate a user logging -in. Unfortunately the former can be rather awkward, which is why it's -a good thing that the authentication framework is so flexible. +in. Unfortunately this can be rather awkward, which is why it's a good +thing that the authentication framework is so flexible. Instead of using a test database, one can simply change the authentication store to something a bit easier to deal with in a @@ -424,9 +435,10 @@ testing environment. Additionally, this has the advantage of not modifying one's database, which can be problematic if one forgets to use the testing instead of production database. -Alternatively, if you want to authenticate real users, but not have to worry about -their passwords, you can use L -to force all users to authenticate with a global password. +Alternatively, if you want to authenticate real users, but not have to +worry about their passwords, you can use +L to force all users to +authenticate with a global password. =head3 More information @@ -437,9 +449,9 @@ L has a longer explanation. =head3 Introduction Authorization is the step that comes after -authentication. Authentication establishes that the user agent is -really representing the user we think it's representing, and then -authorization determines what this user is allowed to do. +authentication. Authentication establishes that the user agent is really +representing the user we think it's representing, and then authorization +determines what this user is allowed to do. =head3 Role Based Access Control @@ -453,14 +465,14 @@ pretty nasti!). For example: sub feed_moose : Local { my ( $self, $c ) = @_; - $c->model( "Moose" )->eat( $c->req->param("food") ); + $c->model( "Moose" )->eat( $c->req->params->{food} ); } With this action, anyone can just come into the moose cage and feed the moose, which is a very dangerous thing. We need to restrict this action, so that only a qualified moose feeder can perform that action. -The Authorization::Roles plugin let's us perform role based access +The Authorization::Roles plugin lets us perform role based access control checks. Let's load it: use parent qw/Catalyst/; @@ -475,7 +487,7 @@ And now our action should look like this: my ( $self, $c ) = @_; if ( $c->check_roles( "moose_feeder" ) ) { - $c->model( "Moose" )->eat( $c->req->param("food") ); + $c->model( "Moose" )->eat( $c->req->params->{food} ); } else { $c->stash->{error} = "unauthorized"; } @@ -508,7 +520,7 @@ administration). Checking for roles all the time can be tedious and error prone. -The Authorization::ACL plugin let's us declare where we'd like checks +The Authorization::ACL plugin lets us declare where we'd like checks to be done automatically for us. For example, we may want to completely block out anyone who isn't a @@ -559,16 +571,16 @@ If this action does not exist, an error will be thrown, which you can clean up in your C private action instead. Also, it's important to note that if you restrict access to "/" then -C, C, etc will also be restricted. +C, C, etc. will also be restricted. - MyApp->acl_allow_root_internals; + MyApp->acl_allow_root_internals; will create rules that permit access to C, C, and C in the root of your app (but not in any other controller). =head1 Models -Models are where application data belongs. Catalyst is exteremely +Models are where application data belongs. Catalyst is extremely flexible with the kind of models that it can use. The recipes here are just the start. @@ -580,11 +592,14 @@ can be used outside of Catalyst, e.g. in a cron job). It's trivial to write a simple component in Catalyst that slurps in an outside Model: package MyApp::Model::DB; + use base qw/Catalyst::Model::DBIC::Schema/; + __PACKAGE__->config( schema_class => 'Some::DBIC::Schema', - connect_info => ['dbi:SQLite:foo.db', '', '', {AutoCommit=>1}]; + connect_info => ['dbi:SQLite:foo.db', '', '', {AutoCommit=>1}], ); + 1; and that's it! Now C is part of your @@ -597,9 +612,9 @@ See L. =head2 Create accessors to preload static data once per server instance When you have data that you want to load just once from the model at -server load instead of for each request, use mk_group_accessors to +startup, instead of for each request, use mk_group_accessors to create accessors and tie them to resultsets in your package that -inherits from DBIx::Class::Schema +inherits from DBIx::Class::Schema: package My::Schema; use base qw/DBIx::Class::Schema/; @@ -623,12 +638,12 @@ inherits from DBIx::Class::Schema and now in the controller, you can now access any of these without a per-request fetch: - $c->stash->{something} = $c->model('My::Schema')->schema->ACCESSORNAMEn; + $c->stash->{something} = $c->model('My::Schema')->schema->ACCESSORNAME; =head2 XMLRPC -Unlike SOAP, XMLRPC is a very simple (and imo elegant) web-services +Unlike SOAP, XMLRPC is a very simple (and elegant) web-services protocol, exchanging small XML messages like these: Request: @@ -731,7 +746,7 @@ enforce a specific one. =head1 Views Views pertain to the display of your application. As with models, -catalyst is uncommonly flexible. The recipes below are just a start. +Catalyst is uncommonly flexible. The recipes below are just a start. =head2 Catalyst::View::TT @@ -741,8 +756,8 @@ display your data; you can choose to generate HTML, PDF files, or plain text if you wanted. Most Catalyst applications use a template system to generate their HTML, -and though there are several template systems available, Template -Toolkit is probably the most popular. +and though there are several template systems available, +L