[Catalyst-commits] r14206 - in trunk/examples/CatalystAdvent/root/2011: . pen

caelum at dev.catalyst.perl.org caelum at dev.catalyst.perl.org
Sat Dec 10 22:52:34 GMT 2011


Author: caelum
Date: 2011-12-10 22:52:34 +0000 (Sat, 10 Dec 2011)
New Revision: 14206

Added:
   trunk/examples/CatalystAdvent/root/2011/10.pod
   trunk/examples/CatalystAdvent/root/2011/11.pod
Removed:
   trunk/examples/CatalystAdvent/root/2011/pen/autocrud.pod
   trunk/examples/CatalystAdvent/root/2011/pen/controllerrole_chainaction_massascre_1.pod
Log:
publish advent 10 and 11

Added: trunk/examples/CatalystAdvent/root/2011/10.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2011/10.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2011/10.pod	2011-12-10 22:52:34 UTC (rev 14206)
@@ -0,0 +1,689 @@
+=head1 The ControllerRole ChainAction Massacre (Part 1)
+
+=head2 Overview
+
+This article describes one way to create reusable code
+by writing L<roles|Moose::Role> for 
+L<Catalyst Controllers|Catalyst::Controller> with a massive use of 
+L<Chained Actions|Catalyst::DispatchType::Chained>.
+
+=head3 About this Article
+
+The article will start with an introduction on how to implement 
+chained actions in Moose::Roles. The second part will deal with 
+increasing reusability of your ControllerRoles by following some simple
+rules.
+
+=head3 About the title
+
+Did you see the movie "The Texas Chain Saw Massacre"? 
+I did. Did you like it? Well, I didn't. But I loved the title.
+Thats all!
+ 
+=head2 Chaining actions in Moose::Role
+
+=head3 Motivation
+
+Most Catalyst applications consist of several actions, distributed among 
+several controllers. Most of these actions 
+share some (more or less) identical code, for example if
+they work with data provided by a model. 
+Connecting to a database, fetching the required data and storing it in a local
+variable is not complicated, but it has to be done at the beginning of each
+action.
+I noticed that the first lines of code are almost identical for most of my actions:
+
+	sub foo :Local :Args(1){
+		my ($self, $c, $id) = @_;
+		my $model = $c->model('FooDB');
+		my $table = $model->resultset('footable');
+		my $item = $table->find($id);
+
+		$item->do_foo;
+
+		...
+
+Or, if I include some error handling:
+
+	sub foo :Local :Args(1){
+		my ($self, $c, $id) = @_;
+		my $model = $c->model('FooDB');
+		unless($model){ 
+			# handle fatal error
+		}
+		my $table = $model->resultset('footable');
+		unless($table){ 
+			# handle another fatal error
+		}
+
+		my $item = $table->find($id);
+		unless($item){ 
+			# not fatal -> notify user
+		}
+		
+		$item->do_foo;
+
+		...
+
+Even the error handling code is more or less identical in most actions.
+I don't want to rewrite (or copy-paste) the same code again and again. 
+Thats one reason why I love Catalyst.
+
+=head3 Chain me if you can!
+
+One possible solution to this problem is "chaining actions". This is neither new
+nor surprising, since the Catalyst Tutorial directly 
+L<points to it|https://metacpan.org/module/Catalyst::Manual::Tutorial::04_BasicCRUD#CONVERT-TO-A-CHAINED-ACTION>
+
+=head4 Converting a regular action into a chained one can be done in two steps:
+
+=over
+
+=item 1. moving the shared code to an extra action
+
+	sub get_item :Chained('/') :CaptureArgs(1){
+		my ($self, $c, @id) = @_;
+		my $model = $c->model('FooDB');
+		unless($model){ 
+			# handle fatal error
+		}
+		my $table = $model->resultset('footable');
+		unless($table){ 
+			# handle another fatal error
+		}
+
+		my $item = $table->find(@id);
+		unless($item){ 
+			# not fatal -> notify user
+		}
+
+		$c->stash(
+			model => $model,
+			table => $table,
+			item => $item,
+		);	
+
+=item 2. chaining your actions to the shared action
+
+	sub foo :Chained('get_item') :Args(0){
+		my ($self, $c) = @_;
+		$c->stash->{item}->do_foo;
+
+		...
+	}
+
+	sub bar :Chained('get_item') :Args(0){
+		my ($self, $c) = @_;
+		$c->stash->{item}->do_bar;
+
+		...
+	}
+
+=back
+
+=head4 Points of interest
+
+=over
+
+=item * The dispatcher distinguishes between "Args" and "CaptureArgs". 
+CaptureArgs are "eaten" by their method. This means they are removed from the
+argument list and are not visible to any action chained to the capturing one.
+"Args" should only be configured for a last action in a chain.
+
+=item * Since the id is now a CaptureArg for the "get_item" action, its 
+position in the path changes. Because the get_item action also has a PathPart,
+the path of "foo" changes from
+
+	"/foo/$id"
+
+to
+
+	"/get_item/$id/foo"
+
+=item * Empty PathParts are allowed. By setting an empty PathPart for
+the "get_item" action 
+
+	sub get_item :Chained('/') :PathPart("") :CaptureArgs(1){
+		...
+
+The resulting path will change from
+
+	"/get_item/$id/foo"
+
+to
+
+	"/$id/foo"
+
+which is more beautiful in my opinion. Take care that the paths stay unique!
+
+=item * L<uri_for_action|https://metacpan.org/module/Catalyst#c-uri_for_action-path-captures_and_args-args-query_values-> knows how to handle CaptureArgs. See the Catalyst documentation for details.
+
+=back
+
+=head3 Role Baby Role!
+
+At this point, we know that Catalyst makes it easy to reuse code by creating chained actions. But we still have to make our code available in our controller.
+
+Using L<roles|Moose::Role> makes reusing your code easy. Roles allow you to specify subroutines and attributes. They will be 
+present in any class which has the role applied to it. Since Catalyst Controllers are Moose objects, applying a role to it is as easy as 
+adding 
+ 
+ with "RoleName";
+
+to your class.  
+The CPAN module L<MooseX::MethodAttributes::Role> makes it possible to add method attributes to subroutines.
+This allows you to implement complete Catalyst actions, including "Path", "Args", "Chains" and whatever you need.
+
+To make the "get_item" action more reusable, move it to a role as shown in the following example:
+
+	package CatalystX::TraitFor::Controller::MyGetItem;
+
+	use MooseX::MethodAttributes::Role;
+
+	sub get_item :Chained('/') :CaptureArgs(1){
+		my ($self, $c, @id) = @_;
+		my $model = $c->model('FooDB');
+		unless($model){ 
+			# handle fatal error
+		}
+		my $table = $model->resultset('footable');
+		unless($table){ 
+			# handle another fatal error
+		}
+
+		my $item = $table->find(@id);
+		unless($item){ 
+			# not fatal -> notify user
+		}
+
+		$c->stash(
+			model => $model,
+			table => $table,
+			item => $item,
+		);	
+	use namespace::autoclean;
+	1;
+
+If you feel like using the actions "foo" and "bar" in several controllers, you can move their code to roles aswell.
+You can ensure that the actions you are chaining to are present in your controller by using Moose's L<require|Moose::Role>
+keyword. Keep in mind that this ensures that the required subroutine is present, but it does not require it to be a Catalyst action.
+The resulting roles for "foo" and "bar" will look like this:
+
+For Foo:
+
+	package CatalystX::TraitFor::Controller::MyFoo;
+
+	use MooseX::MethodAttributes::Role;
+
+	requires qw/get_item/;
+
+	sub foo :Chained('get_item') :Args(0){
+		my ($self, $c) = @_;
+		$c->stash->{item}->do_foo;
+
+		...
+	}
+
+	use namespace::autoclean;
+	1;
+
+and for Bar:
+
+	package CatalystX::TraitFor::Controller::MyBar;
+
+	use MooseX::MethodAttributes::Role;
+
+	requires qw/get_item/;
+
+	sub bar :Chained('get_item') :Args(0){
+		my ($self, $c) = @_;
+		$c->stash->{item}->do_bar;
+
+		...
+	}
+
+	use namespace::autoclean;
+	1;
+
+Now it is possible to "plug" your actions to any controller by applying the corresponding roles to them.
+You should consider changing some PathParts in your controllers, otherwise the actions will have the same
+path in all controllers:
+
+	package MyController;
+
+	use Moose;
+	extends "Catalyst::Controller";
+	with qw/
+		CatalystX::TraitFor::Controller::MyGetItem 
+		CatalystX::TraitFor::Controller::MyFoo
+	/;
+
+	__PACKAGE__->config(
+		action => {
+			get_item => {PathPart => 'mycontroller'},
+		},
+	);
+
+	__PACKAGE__->meta->make_immutable;
+	no Moose;
+	1;
+
+Or, if you want to provide your own "foo" method:
+
+	package AnotherController;
+
+	use Moose;
+	extends "Catalyst::Controller";
+	with qw/
+		CatalystX::TraitFor::Controller::MyGetItem 
+	/;
+
+	sub foo :Chained('get_item') :Args(0){
+		# your foo-code here
+	}
+
+	__PACKAGE__->config(
+		action => {
+			get_resultset => {PathPart => 'anothercontroller'},
+		},
+	);
+
+	__PACKAGE__->meta->make_immutable;
+	no Moose;
+	1;
+
+It is possible to modify the chains for each controller. If you want to do something before "get_item":
+
+	package ConfiguredController;
+
+	use Moose;
+	extends "Catalyst::Controller";
+	with qw/
+		CatalystX::TraitFor::Controller::MyGetItem 
+		CatalystX::TraitFor::Controller::MyFoo
+	/;
+	
+	sub prepare :Chained('/') :PathPart("") :CaptureArgs(0){
+		# your code here
+	}
+
+	__PACKAGE__->config(
+		action => {
+			get_item => {Chained => 'prepare', PathPart => 'something'},
+		},
+	);
+
+
+	__PACKAGE__->meta->make_immutable;
+	no Moose;
+	1;
+
+And if your table has more than one primary key:
+
+	package TwoPkController;
+
+	use Moose;
+	extends "Catalyst::Controller";
+	with qw/
+		CatalystX::TraitFor::Controller::MyGetItem 
+		CatalystX::TraitFor::Controller::MyFoo
+	/;
+	
+	__PACKAGE__->config(
+		action => {
+			get_item => {CaptureArgs => 2, PathPart => 'something'},
+		},
+	);
+
+	__PACKAGE__->meta->make_immutable;
+	no Moose;
+	1;
+
+
+By creating chained actions and putting them into ControllerRoles, it is possible to
+create some kind of "application bricks" which can easily be added to any controller.
+If you have implemented some functionality once, and you need it somewhere else,
+you can enable it by adding a single line of code to your controller. 
+
+Creating applications that way reminds me of playing with Lego. The only difference is that
+I can create my own Lego-bricks, and modify existing bricks if they do not exactly fit my needs.
+A childhood dream comes true. It's kind of cool, isn't it?
+
+=head2 Increasing reusability
+
+Code-reusability in the first chapter is very limited. This chapter will tell you why, and shows some simple tricks how to make your code more reusable:
+
+=head3 Oh my tiny little actions!
+
+One problem in the previous example is that the "get_item" action does more than getting one item. It can only be used by actions which 
+require exactly one item in the stash. By splitting "get_item" into three atomic parts, the code gets even more reusable:
+
+	package CatalystX::TraitFor::Controller::MyGetItem;
+
+	use MooseX::MethodAttributes::Role;
+
+	sub get_model :Chained('/') :CaptureArgs(0){
+		my ($self, $c) = @_;
+		my $model = $c->model('FooDB');
+		unless($model){ 
+			# handle fatal error
+		}
+
+		$c->stash(
+			model => $model,
+		);
+	}
+
+	sub get_resultset :Chained('get_model') :CaptureArgs(0){
+		my ($self, $c) = @_;
+		my $table = $c->stash->{model}->resultset('footable');
+		unless($table){ 
+			# handle another fatal error
+		}
+
+		$c->stash(
+			table => $table,
+		);	
+	}
+
+	sub get_item :Chained('get_resultset') :CaptureArgs(1){
+		my ($self, $c, $id) = @_;
+		my $item = $c->stash{table}->find($id);
+		unless($item){ 
+			# not fatal -> notify user
+		}
+
+		$c->stash(
+			item => $item,
+		);	
+	}
+
+	use namespace::autoclean;
+	1;
+
+If you realize that some controllers never need one item, but often need the model and resultset, you can distribute this actions
+among two or three roles (named "MyGetModel", "MyGetRS" and "MyGetItem"). Remember to require the actions you are chaining to!
+
+Distributing the code among several roles makes your code more flexible. If one of your controllers should get the model in
+a different way, but needs the same resultset and item code, you can consume the "MyGetRS" and "MyGetItem" roles and implement 
+the "get_model" action in your controller. If you often need all actions in the same controller, and you don't want to write 3 three
+"with"-lines, you can create a role which includes all three actions:
+
+The "reunion-role":
+
+	package CatalystX::TraitFor::Controller::ModelActions;
+
+	use Moose::Role;
+	with qw/
+		CatalystX::TraitFor::Controller::MyGetModel
+		CatalystX::TraitFor::Controller::MyGetRS
+		CatalystX::TraitFor::Controller::MyGetItem
+	/;
+
+	no Moose::Role;
+	1;
+
+A controller which uses all three action would look like this:
+
+	package MyCompleteController;
+
+	use Moose;
+	extends "Catalyst::Controller";
+	with "CatalystX::TraitFor::Controller::ModelActions";
+
+	__PACKAGE__->meta->make_immutable;
+
+	no Moose;
+	1;
+
+A controller with a custom "get_model" action would look like this:
+
+	package MyPartlyController;
+
+	use Moose;
+	extends "Catalyst::Controller";
+	with qw/
+		CatalystX::TraitFor::Controller::MyGetRS
+		CatalystX::TraitFor::Controller::MyGetItem
+	/;
+
+	sub get_model :Chained("/") :CaptureArgs(0) :PathPart(""){ ... }
+
+	__PACKAGE__->meta->make_immutable;
+
+	no Moose;
+	1;
+
+=head3 Don't force me! Don't force yourself! Don't force anybody!
+
+The second problem in my example is that several things which should be flexible 
+are hardcoded in my roles. The most important examples are the name of the model and the
+name of the table. This means that we can easily add these actions to any controller, but all
+controllers would do EXACTLY the same thing, which is not what we want. Even if you plan to 
+use your role exactly once in each application, you force your application to use the same 
+model name and table name as your role.
+
+Using attributes to store these information makes your roles configurable and much more
+reusable:
+
+	package CatalystX::TraitFor::Controller::MyGetModel;
+
+	use MooseX::MethodAttributes::Role;
+
+	has "model_name" => (
+		is => 'rw',
+		isa => 'Str',
+		default => sub{
+			"DB",
+		},
+	);
+
+	sub get_model :Chained('/') :CaptureArgs(0){
+		my ($self, $c) = @_;
+		my $model = $c->model($self->model_name);
+		unless($model){ 
+			# handle fatal error
+		}
+
+		$c->stash(
+			model => $model,
+		);	
+	}
+
+	use namespace::autoclean;
+	1;
+
+With this modification, you can configure the model name for each controller:
+
+	package MyDifferentController;
+
+	use Moose;
+	extends "Catalyst::Controller";
+	with "CatalystX::TraitFor::Controller::ModelActions";
+
+	...
+	
+	__PACKAGE__->config(
+		model_name => "AnotherDB",
+	);
+
+	__PACKAGE__->meta->make_immutable;
+
+	no Moose;
+	1;
+
+=head3 Whats your name?
+
+The next problem is related to the previous one. The stash-keys of model, resultset and item are hardcoded aswell.
+This may result in conflicting names, overwritten values in the stash and a lot of trouble. Avoid this by making the 
+stash-keys configurable aswell. The default values can even be created dynamically:
+
+	package CatalystX::TraitFor::Controller::MyGetModel;
+
+	use MooseX::MethodAttributes::Role;
+
+	has "stash_model_as" => (
+		is => 'rw',
+		isa => 'Str',
+		default => sub{
+			my @split = split "::", ref(shift);
+			my $controllername = pop @split;
+			$controllername =~ tr/[A-Z]/[a-z]/;
+			return $controllername . "_model";
+		},
+	);
+
+	has model_name => ( ... );
+
+	sub get_model :Chained('/') :CaptureArgs(0){
+		my ($self, $c) = @_;
+		my $model = $c->model($self->model_name);
+		unless($model){ 
+			# handle fatal error
+		}
+
+		my $model_as = $self->stash_model_as;
+		$c->stash(
+			$model_as => $model,
+		);	
+	}
+
+	use namespace::autoclean;
+	1;
+
+You will have to modify your "get_resultset" action aswell:
+
+	package CatalystX::TraitFor::Controller::MyGetResultSet;
+
+	use MooseX::MethodAttributes::Role;
+
+	has 'table_name' => ( ... );
+
+	has 'stash_table_as' => ( ... );
+
+	sub get_resultset :Chained('get_model') :CaptureArgs(0){
+		my ($self, $c) = @_;
+		my $table = $c->stash->{$self->stash_model_as}->resultset($self->table_name);
+
+		...
+	}
+
+	use namespace::autoclean;
+	1;
+
+In this example, the default stash keys are created dynamically from the controllers name.
+If you apply the roles to a controller named "MyApp::Controller::Foo", the model will be 
+stashed as "foo_model". If you don't like this behaviour, you can override the default in
+the __PACKAGE__->config(...);
+
+
+=head3 Sorry, babe! I don't remember you...
+
+Now we know how to write reusable, modular and configurable code with chained actions and Moose::Role.
+My last tip is not new. In fact, its kind of old-fashioned:: Choose "good" names for your roles, and PLEASE write a POD for your modules! 
+If you use ControllerRoles as intensive as I do, most of your controllers will only consist of the package name, a few
+"Moose" lines, the list of consumed roles and some configuration.
+If you choose "good" names for your roles, the controllers code will be more or less self-explanatory.
+If you don't choose your names wisely, it will be hard to understand what the consuming controller
+does.
+
+Here is an Example: Try to guess the purpose of the following controllers:
+
+=over
+
+=item 1.
+
+	package MyApp::Controller:Foo;
+
+	use Moose;
+	extends "Catalyst::Controller";
+	with "CatalystX::TraitFor::Controller::MyRole";
+
+	__PACKAGE__->meta->make_immutable;
+	no Moose;
+	1;
+
+No chance!
+
+=item 2.
+
+	package MyApp::Controller:Bar;
+
+	use Moose;
+	extends "Catalyst::Controller";
+	with "CatalystX::TraitFor::Controller::ChainedCRUD";
+
+	__PACKAGE__->meta->make_immutable;
+	no Moose;
+	1;
+
+Hard to guess. Is this a CRUD controller? It might use chained actions...
+
+=back
+
+When you write the documentation for your roles, remember to include all attributes and actions.
+You should include some extra information about your chained actions:
+
+=over
+
+=item * how many arguments does this action expect by default, and which
+
+=item * what items does this action expect to be in the stash
+
+=item * which items in the stash are modified
+
+=item * which items are added to the stash
+
+=item * which keys are used for each of the items, and where do the keys come from
+
+=back
+
+When you add all these information, everybody (including yourself) will be able to 
+understand the purpose of your roles, and how to use them. Well, not everybody will 
+be able to understand your code. Maybe not even you. But the chance that yourself and others
+understand and use your code increases.
+
+=head2 Conclusion
+
+=over
+
+=item * Chaining actions can make your code more reusable
+
+=item * Making your actions as atomic as possible increases flexibility and reusability
+
+=item * It is possible to write reuseable, chained actions in Moose::Role's
+
+=item * Making your roles as configurable as possible dramatically increases the chance that you (and others) find it usefull in other projects
+
+=item * Moose helps you making your modules configurabel in an easy and flexible way
+
+=item * Roles can easily be applied to controllers. This makes it possible to create small
+"Controller-Bricks" which can be plugged to almost every controller.
+
+=item * Clever naming and documentation is mandatory
+
+=item * The author does not like violent movies, but he sometimes likes violent titles
+
+=back
+
+=head2 Whats next?
+
+The "ControllerRole ChainAction Massacre Part 2" will deal with
+
+=over
+
+=item * how to use BUILD and BUILDARGS methods in ControllerRoles
+
+=item * some more examples
+
+=item * some restrictions related to Moose::Role
+
+=item * how to bypass some of these restrictions
+
+=item * performance issues
+
+=back
+
+=head2 Author:
+
+Lukas Thiemeier <lukast at cpan.org>

Added: trunk/examples/CatalystAdvent/root/2011/11.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2011/11.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2011/11.pod	2011-12-10 22:52:34 UTC (rev 14206)
@@ -0,0 +1,239 @@
+=head1 Easy CRUD for your Catalyst App
+
+CRUD is a rapid application development tool which produces Create, Search,
+Update and Delete methods for your back-end data. If you worked through the
+L<Catalyst Tutorial|Catalyst::Manual::Tutorial> then CRUD should be a familiar
+term, but it can be implemented in different ways.
+
+Some helper scripts which accompany L<Catalyst> modules will generate CRUD
+code, subroutine stubs, package templates, and so on. You can use these as a
+starting point from which to grow your application, and the term for this is
+scaffolding.
+
+In the Python world there's a plugin for their Django web framework which is a
+bit like scaffold helper scripts on steroids.  It generates a fully working
+web interface for manipulating back-end data.
+
+Catalyst's L<AutoCRUD|Catalyst::Plugin::AutoCRUD> plugin does something
+similar. After installing the plugin you have a new URL path at C</autocrud>
+from which you can access and edit any data accessible through the app Models.
+However unlike L<Django's plugin|https://docs.djangoproject.com/en/dev/ref/contrib/admin/>,
+this is all done on the fly, there are no scaffolding files written to disk.
+To give you an idea of what's generated, head over to the demo site at
+L<http://demo.autocrud.pl>.
+
+That doesn't mean you can't control what AutoCRUD produces. This article will
+look at a few ways to tailor the appearance of the Catalyst::Plugin::AutoCRUD
+products.
+
+=head1 The mini Synopsis
+
+Once AutoCRUD is installed, update your main application module to have:
+
+ use Catalyst qw(ConfigLoader AutoCRUD); # <-- add the plugin name here in MyApp.pm
+
+You need a database, and ideally also the L<Catalyst Models|Catalyst::Model::DBIC::Schema>
+to access it. No Models? No problem - this is AutoCRUD after all! Add the
+following to myapp.conf:
+
+ <Model::AutoCRUD::DBIC>
+     connect_info   dbi:<engine>:dbname=<db>;host=<hostname>;
+     connect_info   <username>
+     connect_info   <password>
+ </Model::AutoCRUD::DBIC>
+
+Season the above to taste, changing your database engine, database name, host,
+username and password as needed. AutoCRUD will do the rest.
+
+You'll now have the C</autocrud> path enabled. Where can you go from here...?
+
+=head1 Dedicated AutoCRUD App
+
+One common scenario is an app dedicated to AutoCRUD - a cheap way to spin up a
+web user interface for a database. Set the following in your app config file
+(something like C<myapp.conf>):
+
+ <Plugin::AutoCRUD>
+     basepath ""
+ </Plugin::AutoCRUD>
+
+Instead of running AutoCRUD at the C</autocrud> path, this makes it run from the
+root path, C</>.
+
+=head1 Read-only Interface
+
+If you want to present some data to users but not have them change any of it,
+there are three config options:
+
+ <Plugin::AutoCRUD>
+    <sites>
+        <default>
+            update_allowed no
+            create_allowed no
+            delete_allowed no
+        </default>
+    </sites>
+ </Plugin::AutoCRUD>
+
+Each of these options can be left out (it defaults to "yes"), or included, and
+you can pick any combination of the three.
+
+=head1 Without JavaScript
+
+Hopefully you've seen by now that the default user interface is a fancy
+dynamic web application. If this isn't to your taste, then an alternative
+simple HTML interface is available, but it's read-only. Add the following to
+your app's configuration:
+
+ <Plugin::AutoCRUD>
+    <sites>
+        <default>
+            frontend skinny
+        </default>
+    </sites>
+ </Plugin::AutoCRUD>
+
+Beware that this doesn't prevent users from changing data, just because the
+web front-end has no editing facility! If you really want a simple HTML,
+read-only interface, then combine this setting with the three "allowed"
+options, above.
+
+=head1 Columns and Names
+
+Perhaps your tables have a large number of columns, and the columns don't have
+very human-friendly names. AutoCRUD config can also control the displayed
+columns and their names!
+
+For this you need to know the database name and table name, which are easily
+found in the URL path when you browse to the table in the normal AutoCRUD
+interface.
+
+ <Plugin::AutoCRUD>
+    <sites>
+        <default>
+            <mydb>
+                <thetable>
+                    columns  id
+                    columns  title
+                    columns  length
+                    <headings>
+                        id      Key
+                        title   Name
+                        length  "Time Taken"
+                    </headings>
+                </thetable>
+            </mydb>
+        </default>
+    </sites>
+ </Plugin::AutoCRUD>
+
+Let's walk through this configuration. Below the C<< <default> >> key (which
+is something you don't need to worry about right now) you can see the database
+name, and table name, as C<< <mydb> >> and C<< <thetable> >>. This allows you
+to have different column configurations for different tables - simply add new
+sections to the config.
+
+Within the table's configuration the list of C<column> values are, guess
+what... the columns AutoCRUD will display in the table. And inside the C<<
+<headings> >> key we can tell AutoCRUD what to put in the user interface as
+their names.
+
+As you might guess, AutoCRUD does its best to work things like the headings
+out automatically, so these settings are all optional. If you don't include
+config for a table, you get all the columns shown and AutoCRUD's choice of
+heading.
+
+=head1 DBIx::Class Tricks
+
+This is something it can take a while for new users to grasp... AutoCRUD is
+only as smart as your L<DBIx::Class> result set classes. What does this mean?
+Well, assuming you have existing Catalyst Models, AutoCRUD never asks your
+database what its columns are. It asks the Model.
+
+So you can use the Model to "hide" columns from AutoCRUD quite easily.
+Sometimes this is easier to maintain than the app config we saw just above.
+Remember you can also have two DBIx::Class result sets for the same table,
+only with different names and column configs (perhaps a full config for your
+app, and a limited one for AutoCRUD).
+
+One other trick you can do with DBIx::Class is to put a sub into your result
+set class called C<display_name>. AutoCRUD will use this to "stringify" a row
+from the table. For example:
+
+ sub display_name {
+     my $self = shift;
+     return $self->forename .' '. $self->surname;
+ }
+
+=head1 Hiding a Database or Table
+
+Let's say you use the above trick of having two DBIx::Class result sets for
+the same table, so you want to hide one from AutoCRUD. There's a simple config
+key for this:
+
+ <Plugin::AutoCRUD>
+    <sites>
+        <default>
+            <mydb>
+                <secrettable>
+                    hidden yes
+                </secrettable>
+            </mydb>
+        </default>
+    </sites>
+ </Plugin::AutoCRUD>
+
+=head1 Overriding Templates
+
+The final thing we'll cover in this article is messing around with AutoCRUD's
+templates. The app is built using our old friend L<Template::Toolkit>, and you
+have the option to override any of the templates used.
+
+This can be handy for many reasons. Perhaps you want to add something to the
+footer, like the L<demo site|http://demo.autocrud.pl> does for its hosting
+provider logo. Or you can do something altogether more serious and embed the
+app in a page in your own site, by changing the wrapper template.
+
+First, set up a path on your filesystem from which AutoCRUD can pick up
+template overrides. This can be set multiple times if you want a set of paths
+to be searched:
+
+ <Plugin::AutoCRUD>
+    tt_path /path/to/my/local/templates
+ </Plugin::AutoCRUD>
+
+Then within that path, copy the built-in AutoCRUD template and season to
+taste. You need to keep the same path structure as the files in AutoCRUD's
+distribution package. So for the footer example mentioned above, for both
+JavaScript and HTML-only versions of the app, we have two files:
+
+ extjs2/wrapper/footer.tt
+ skinny/wrapper/footer.tt
+
+=head1 Conclusion
+
+We started with a simple idea - producing a simple user interface allowing
+Create, Search, Update and Delete on your back-end data. You can start with
+scaffolding files produced by helper scripts, or with a plugin like AutoCRUD
+very quickly have an app which does the same, all on the fly.
+
+Which way you go is your choice. There's a trade-off between effort, and
+flexibility and power. AutoCRUD requires little effort and delivers a lot, and
+is even configurable to some degree, as we saw above. But maybe there comes a
+point when your app grows and you need more.
+
+Like all of Perl, there are few rules, and the journey is always as much fun
+as the end result!
+
+=head1 Further Information
+
+The L<AutoCRUD|Catalyst::Plugin::AutoCRUD> manual page is fairly
+comprehensive, with many hints and tips for other areas such as successful
+handling of Unicode. Don't forget there's also the demo site at
+L<http://demo.autocrud.pl>.
+
+=head1 Author
+
+Oliver Gorwits <oliver at cpan.org> or C<oliver> on IRC.
+
+=cut

Deleted: trunk/examples/CatalystAdvent/root/2011/pen/autocrud.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2011/pen/autocrud.pod	2011-12-10 22:10:07 UTC (rev 14205)
+++ trunk/examples/CatalystAdvent/root/2011/pen/autocrud.pod	2011-12-10 22:52:34 UTC (rev 14206)
@@ -1,239 +0,0 @@
-=head1 Easy CRUD for your Catalyst App
-
-CRUD is a rapid application development tool which produces Create, Search,
-Update and Delete methods for your back-end data. If you worked through the
-L<Catalyst Tutorial|Catalyst::Manual::Tutorial> then CRUD should be a familiar
-term, but it can be implemented in different ways.
-
-Some helper scripts which accompany L<Catalyst> modules will generate CRUD
-code, subroutine stubs, package templates, and so on. You can use these as a
-starting point from which to grow your application, and the term for this is
-scaffolding.
-
-In the Python world there's a plugin for their Django web framework which is a
-bit like scaffold helper scripts on steroids.  It generates a fully working
-web interface for manipulating back-end data.
-
-Catalyst's L<AutoCRUD|Catalyst::Plugin::AutoCRUD> plugin does something
-similar. After installing the plugin you have a new URL path at C</autocrud>
-from which you can access and edit any data accessible through the app Models.
-However unlike L<Django's plugin|https://docs.djangoproject.com/en/dev/ref/contrib/admin/>,
-this is all done on the fly, there are no scaffolding files written to disk.
-To give you an idea of what's generated, head over to the demo site at
-L<http://demo.autocrud.pl>.
-
-That doesn't mean you can't control what AutoCRUD produces. This article will
-look at a few ways to tailor the appearance of the Catalyst::Plugin::AutoCRUD
-products.
-
-=head1 The mini Synopsis
-
-Once AutoCRUD is installed, update your main application module to have:
-
- use Catalyst qw(ConfigLoader AutoCRUD); # <-- add the plugin name here in MyApp.pm
-
-You need a database, and ideally also the L<Catalyst Models|Catalyst::Model::DBIC::Schema>
-to access it. No Models? No problem - this is AutoCRUD after all! Add the
-following to myapp.conf:
-
- <Model::AutoCRUD::DBIC>
-     connect_info   dbi:<engine>:dbname=<db>;host=<hostname>;
-     connect_info   <username>
-     connect_info   <password>
- </Model::AutoCRUD::DBIC>
-
-Season the above to taste, changing your database engine, database name, host,
-username and password as needed. AutoCRUD will do the rest.
-
-You'll now have the C</autocrud> path enabled. Where can you go from here...?
-
-=head1 Dedicated AutoCRUD App
-
-One common scenario is an app dedicated to AutoCRUD - a cheap way to spin up a
-web user interface for a database. Set the following in your app config file
-(something like C<myapp.conf>):
-
- <Plugin::AutoCRUD>
-     basepath ""
- </Plugin::AutoCRUD>
-
-Instead of running AutoCRUD at the C</autocrud> path, this makes it run from the
-root path, C</>.
-
-=head1 Read-only Interface
-
-If you want to present some data to users but not have them change any of it,
-there are three config options:
-
- <Plugin::AutoCRUD>
-    <sites>
-        <default>
-            update_allowed no
-            create_allowed no
-            delete_allowed no
-        </default>
-    </sites>
- </Plugin::AutoCRUD>
-
-Each of these options can be left out (it defaults to "yes"), or included, and
-you can pick any combination of the three.
-
-=head1 Without JavaScript
-
-Hopefully you've seen by now that the default user interface is a fancy
-dynamic web application. If this isn't to your taste, then an alternative
-simple HTML interface is available, but it's read-only. Add the following to
-your app's configuration:
-
- <Plugin::AutoCRUD>
-    <sites>
-        <default>
-            frontend skinny
-        </default>
-    </sites>
- </Plugin::AutoCRUD>
-
-Beware that this doesn't prevent users from changing data, just because the
-web front-end has no editing facility! If you really want a simple HTML,
-read-only interface, then combine this setting with the three "allowed"
-options, above.
-
-=head1 Columns and Names
-
-Perhaps your tables have a large number of columns, and the columns don't have
-very human-friendly names. AutoCRUD config can also control the displayed
-columns and their names!
-
-For this you need to know the database name and table name, which are easily
-found in the URL path when you browse to the table in the normal AutoCRUD
-interface.
-
- <Plugin::AutoCRUD>
-    <sites>
-        <default>
-            <mydb>
-                <thetable>
-                    columns  id
-                    columns  title
-                    columns  length
-                    <headings>
-                        id      Key
-                        title   Name
-                        length  "Time Taken"
-                    </headings>
-                </thetable>
-            </mydb>
-        </default>
-    </sites>
- </Plugin::AutoCRUD>
-
-Let's walk through this configuration. Below the C<< <default> >> key (which
-is something you don't need to worry about right now) you can see the database
-name, and table name, as C<< <mydb> >> and C<< <thetable> >>. This allows you
-to have different column configurations for different tables - simply add new
-sections to the config.
-
-Within the table's configuration the list of C<column> values are, guess
-what... the columns AutoCRUD will display in the table. And inside the C<<
-<headings> >> key we can tell AutoCRUD what to put in the user interface as
-their names.
-
-As you might guess, AutoCRUD does its best to work things like the headings
-out automatically, so these settings are all optional. If you don't include
-config for a table, you get all the columns shown and AutoCRUD's choice of
-heading.
-
-=head1 DBIx::Class Tricks
-
-This is something it can take a while for new users to grasp... AutoCRUD is
-only as smart as your L<DBIx::Class> result set classes. What does this mean?
-Well, assuming you have existing Catalyst Models, AutoCRUD never asks your
-database what its columns are. It asks the Model.
-
-So you can use the Model to "hide" columns from AutoCRUD quite easily.
-Sometimes this is easier to maintain than the app config we saw just above.
-Remember you can also have two DBIx::Class result sets for the same table,
-only with different names and column configs (perhaps a full config for your
-app, and a limited one for AutoCRUD).
-
-One other trick you can do with DBIx::Class is to put a sub into your result
-set class called C<display_name>. AutoCRUD will use this to "stringify" a row
-from the table. For example:
-
- sub display_name {
-     my $self = shift;
-     return $self->forename .' '. $self->surname;
- }
-
-=head1 Hiding a Database or Table
-
-Let's say you use the above trick of having two DBIx::Class result sets for
-the same table, so you want to hide one from AutoCRUD. There's a simple config
-key for this:
-
- <Plugin::AutoCRUD>
-    <sites>
-        <default>
-            <mydb>
-                <secrettable>
-                    hidden yes
-                </secrettable>
-            </mydb>
-        </default>
-    </sites>
- </Plugin::AutoCRUD>
-
-=head1 Overriding Templates
-
-The final thing we'll cover in this article is messing around with AutoCRUD's
-templates. The app is built using our old friend L<Template::Toolkit>, and you
-have the option to override any of the templates used.
-
-This can be handy for many reasons. Perhaps you want to add something to the
-footer, like the L<demo site|http://demo.autocrud.pl> does for its hosting
-provider logo. Or you can do something altogether more serious and embed the
-app in a page in your own site, by changing the wrapper template.
-
-First, set up a path on your filesystem from which AutoCRUD can pick up
-template overrides. This can be set multiple times if you want a set of paths
-to be searched:
-
- <Plugin::AutoCRUD>
-    tt_path /path/to/my/local/templates
- </Plugin::AutoCRUD>
-
-Then within that path, copy the built-in AutoCRUD template and season to
-taste. You need to keep the same path structure as the files in AutoCRUD's
-distribution package. So for the footer example mentioned above, for both
-JavaScript and HTML-only versions of the app, we have two files:
-
- extjs2/wrapper/footer.tt
- skinny/wrapper/footer.tt
-
-=head1 Conclusion
-
-We started with a simple idea - producing a simple user interface allowing
-Create, Search, Update and Delete on your back-end data. You can start with
-scaffolding files produced by helper scripts, or with a plugin like AutoCRUD
-very quickly have an app which does the same, all on the fly.
-
-Which way you go is your choice. There's a trade-off between effort, and
-flexibility and power. AutoCRUD requires little effort and delivers a lot, and
-is even configurable to some degree, as we saw above. But maybe there comes a
-point when your app grows and you need more.
-
-Like all of Perl, there are few rules, and the journey is always as much fun
-as the end result!
-
-=head1 Further Information
-
-The L<AutoCRUD|Catalyst::Plugin::AutoCRUD> manual page is fairly
-comprehensive, with many hints and tips for other areas such as successful
-handling of Unicode. Don't forget there's also the demo site at
-L<http://demo.autocrud.pl>.
-
-=head1 Author
-
-Oliver Gorwits <oliver at cpan.org> or C<oliver> on IRC.
-
-=cut

Deleted: trunk/examples/CatalystAdvent/root/2011/pen/controllerrole_chainaction_massascre_1.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2011/pen/controllerrole_chainaction_massascre_1.pod	2011-12-10 22:10:07 UTC (rev 14205)
+++ trunk/examples/CatalystAdvent/root/2011/pen/controllerrole_chainaction_massascre_1.pod	2011-12-10 22:52:34 UTC (rev 14206)
@@ -1,689 +0,0 @@
-=head1 The ControllerRole ChainAction Massacre (Part 1)
-
-=head2 Overview
-
-This article describes one way to create reusable code
-by writing L<roles|Moose::Role> for 
-L<Catalyst Controllers|Catalyst::Controller> with a massive use of 
-L<Chained Actions|Catalyst::DispatchType::Chained>.
-
-=head3 About this Article
-
-The article will start with an introduction on how to implement 
-chained actions in Moose::Roles. The second part will deal with 
-increasing reusability of your ControllerRoles by following some simple
-rules.
-
-=head3 About the title
-
-Did you see the movie "The Texas Chain Saw Massacre"? 
-I did. Did you like it? Well, I didn't. But I loved the title.
-Thats all!
- 
-=head2 Chaining actions in Moose::Role
-
-=head3 Motivation
-
-Most Catalyst applications consist of several actions, distributed among 
-several controllers. Most of these actions 
-share some (more or less) identical code, for example if
-they work with data provided by a model. 
-Connecting to a database, fetching the required data and storing it in a local
-variable is not complicated, but it has to be done at the beginning of each
-action.
-I noticed that the first lines of code are almost identical for most of my actions:
-
-	sub foo :Local :Args(1){
-		my ($self, $c, $id) = @_;
-		my $model = $c->model('FooDB');
-		my $table = $model->resultset('footable');
-		my $item = $table->find($id);
-
-		$item->do_foo;
-
-		...
-
-Or, if I include some error handling:
-
-	sub foo :Local :Args(1){
-		my ($self, $c, $id) = @_;
-		my $model = $c->model('FooDB');
-		unless($model){ 
-			# handle fatal error
-		}
-		my $table = $model->resultset('footable');
-		unless($table){ 
-			# handle another fatal error
-		}
-
-		my $item = $table->find($id);
-		unless($item){ 
-			# not fatal -> notify user
-		}
-		
-		$item->do_foo;
-
-		...
-
-Even the error handling code is more or less identical in most actions.
-I don't want to rewrite (or copy-paste) the same code again and again. 
-Thats one reason why I love Catalyst.
-
-=head3 Chain me if you can!
-
-One possible solution to this problem is "chaining actions". This is neither new
-nor surprising, since the Catalyst Tutorial directly 
-L<points to it|https://metacpan.org/module/Catalyst::Manual::Tutorial::04_BasicCRUD#CONVERT-TO-A-CHAINED-ACTION>
-
-=head4 Converting a regular action into a chained one can be done in two steps:
-
-=over
-
-=item 1. moving the shared code to an extra action
-
-	sub get_item :Chained('/') :CaptureArgs(1){
-		my ($self, $c, @id) = @_;
-		my $model = $c->model('FooDB');
-		unless($model){ 
-			# handle fatal error
-		}
-		my $table = $model->resultset('footable');
-		unless($table){ 
-			# handle another fatal error
-		}
-
-		my $item = $table->find(@id);
-		unless($item){ 
-			# not fatal -> notify user
-		}
-
-		$c->stash(
-			model => $model,
-			table => $table,
-			item => $item,
-		);	
-
-=item 2. chaining your actions to the shared action
-
-	sub foo :Chained('get_item') :Args(0){
-		my ($self, $c) = @_;
-		$c->stash->{item}->do_foo;
-
-		...
-	}
-
-	sub bar :Chained('get_item') :Args(0){
-		my ($self, $c) = @_;
-		$c->stash->{item}->do_bar;
-
-		...
-	}
-
-=back
-
-=head4 Points of interest
-
-=over
-
-=item * The dispatcher distinguishes between "Args" and "CaptureArgs". 
-CaptureArgs are "eaten" by their method. This means they are removed from the
-argument list and are not visible to any action chained to the capturing one.
-"Args" should only be configured for a last action in a chain.
-
-=item * Since the id is now a CaptureArg for the "get_item" action, its 
-position in the path changes. Because the get_item action also has a PathPart,
-the path of "foo" changes from
-
-	"/foo/$id"
-
-to
-
-	"/get_item/$id/foo"
-
-=item * Empty PathParts are allowed. By setting an empty PathPart for
-the "get_item" action 
-
-	sub get_item :Chained('/') :PathPart("") :CaptureArgs(1){
-		...
-
-The resulting path will change from
-
-	"/get_item/$id/foo"
-
-to
-
-	"/$id/foo"
-
-which is more beautiful in my opinion. Take care that the paths stay unique!
-
-=item * L<uri_for_action|https://metacpan.org/module/Catalyst#c-uri_for_action-path-captures_and_args-args-query_values-> knows how to handle CaptureArgs. See the Catalyst documentation for details.
-
-=back
-
-=head3 Role Baby Role!
-
-At this point, we know that Catalyst makes it easy to reuse code by creating chained actions. But we still have to make our code available in our controller.
-
-Using L<roles|Moose::Role> makes reusing your code easy. Roles allow you to specify subroutines and attributes. They will be 
-present in any class which has the role applied to it. Since Catalyst Controllers are Moose objects, applying a role to it is as easy as 
-adding 
- 
- with "RoleName";
-
-to your class.  
-The CPAN module L<MooseX::MethodAttributes::Role> makes it possible to add method attributes to subroutines.
-This allows you to implement complete Catalyst actions, including "Path", "Args", "Chains" and whatever you need.
-
-To make the "get_item" action more reusable, move it to a role as shown in the following example:
-
-	package CatalystX::TraitFor::Controller::MyGetItem;
-
-	use MooseX::MethodAttributes::Role;
-
-	sub get_item :Chained('/') :CaptureArgs(1){
-		my ($self, $c, @id) = @_;
-		my $model = $c->model('FooDB');
-		unless($model){ 
-			# handle fatal error
-		}
-		my $table = $model->resultset('footable');
-		unless($table){ 
-			# handle another fatal error
-		}
-
-		my $item = $table->find(@id);
-		unless($item){ 
-			# not fatal -> notify user
-		}
-
-		$c->stash(
-			model => $model,
-			table => $table,
-			item => $item,
-		);	
-	use namespace::autoclean;
-	1;
-
-If you feel like using the actions "foo" and "bar" in several controllers, you can move their code to roles aswell.
-You can ensure that the actions you are chaining to are present in your controller by using Moose's L<require|Moose::Role>
-keyword. Keep in mind that this ensures that the required subroutine is present, but it does not require it to be a Catalyst action.
-The resulting roles for "foo" and "bar" will look like this:
-
-For Foo:
-
-	package CatalystX::TraitFor::Controller::MyFoo;
-
-	use MooseX::MethodAttributes::Role;
-
-	requires qw/get_item/;
-
-	sub foo :Chained('get_item') :Args(0){
-		my ($self, $c) = @_;
-		$c->stash->{item}->do_foo;
-
-		...
-	}
-
-	use namespace::autoclean;
-	1;
-
-and for Bar:
-
-	package CatalystX::TraitFor::Controller::MyBar;
-
-	use MooseX::MethodAttributes::Role;
-
-	requires qw/get_item/;
-
-	sub bar :Chained('get_item') :Args(0){
-		my ($self, $c) = @_;
-		$c->stash->{item}->do_bar;
-
-		...
-	}
-
-	use namespace::autoclean;
-	1;
-
-Now it is possible to "plug" your actions to any controller by applying the corresponding roles to them.
-You should consider changing some PathParts in your controllers, otherwise the actions will have the same
-path in all controllers:
-
-	package MyController;
-
-	use Moose;
-	extends "Catalyst::Controller";
-	with qw/
-		CatalystX::TraitFor::Controller::MyGetItem 
-		CatalystX::TraitFor::Controller::MyFoo
-	/;
-
-	__PACKAGE__->config(
-		action => {
-			get_item => {PathPart => 'mycontroller'},
-		},
-	);
-
-	__PACKAGE__->meta->make_immutable;
-	no Moose;
-	1;
-
-Or, if you want to provide your own "foo" method:
-
-	package AnotherController;
-
-	use Moose;
-	extends "Catalyst::Controller";
-	with qw/
-		CatalystX::TraitFor::Controller::MyGetItem 
-	/;
-
-	sub foo :Chained('get_item') :Args(0){
-		# your foo-code here
-	}
-
-	__PACKAGE__->config(
-		action => {
-			get_resultset => {PathPart => 'anothercontroller'},
-		},
-	);
-
-	__PACKAGE__->meta->make_immutable;
-	no Moose;
-	1;
-
-It is possible to modify the chains for each controller. If you want to do something before "get_item":
-
-	package ConfiguredController;
-
-	use Moose;
-	extends "Catalyst::Controller";
-	with qw/
-		CatalystX::TraitFor::Controller::MyGetItem 
-		CatalystX::TraitFor::Controller::MyFoo
-	/;
-	
-	sub prepare :Chained('/') :PathPart("") :CaptureArgs(0){
-		# your code here
-	}
-
-	__PACKAGE__->config(
-		action => {
-			get_item => {Chained => 'prepare', PathPart => 'something'},
-		},
-	);
-
-
-	__PACKAGE__->meta->make_immutable;
-	no Moose;
-	1;
-
-And if your table has more than one primary key:
-
-	package TwoPkController;
-
-	use Moose;
-	extends "Catalyst::Controller";
-	with qw/
-		CatalystX::TraitFor::Controller::MyGetItem 
-		CatalystX::TraitFor::Controller::MyFoo
-	/;
-	
-	__PACKAGE__->config(
-		action => {
-			get_item => {CaptureArgs => 2, PathPart => 'something'},
-		},
-	);
-
-	__PACKAGE__->meta->make_immutable;
-	no Moose;
-	1;
-
-
-By creating chained actions and putting them into ControllerRoles, it is possible to
-create some kind of "application bricks" which can easily be added to any controller.
-If you have implemented some functionality once, and you need it somewhere else,
-you can enable it by adding a single line of code to your controller. 
-
-Creating applications that way reminds me of playing with Lego. The only difference is that
-I can create my own Lego-bricks, and modify existing bricks if they do not exactly fit my needs.
-A childhood dream comes true. It's kind of cool, isn't it?
-
-=head2 Increasing reusability
-
-Code-reusability in the first chapter is very limited. This chapter will tell you why, and shows some simple tricks how to make your code more reusable:
-
-=head3 Oh my tiny little actions!
-
-One problem in the previous example is that the "get_item" action does more than getting one item. It can only be used by actions which 
-require exactly one item in the stash. By splitting "get_item" into three atomic parts, the code gets even more reusable:
-
-	package CatalystX::TraitFor::Controller::MyGetItem;
-
-	use MooseX::MethodAttributes::Role;
-
-	sub get_model :Chained('/') :CaptureArgs(0){
-		my ($self, $c) = @_;
-		my $model = $c->model('FooDB');
-		unless($model){ 
-			# handle fatal error
-		}
-
-		$c->stash(
-			model => $model,
-		);
-	}
-
-	sub get_resultset :Chained('get_model') :CaptureArgs(0){
-		my ($self, $c) = @_;
-		my $table = $c->stash->{model}->resultset('footable');
-		unless($table){ 
-			# handle another fatal error
-		}
-
-		$c->stash(
-			table => $table,
-		);	
-	}
-
-	sub get_item :Chained('get_resultset') :CaptureArgs(1){
-		my ($self, $c, $id) = @_;
-		my $item = $c->stash{table}->find($id);
-		unless($item){ 
-			# not fatal -> notify user
-		}
-
-		$c->stash(
-			item => $item,
-		);	
-	}
-
-	use namespace::autoclean;
-	1;
-
-If you realize that some controllers never need one item, but often need the model and resultset, you can distribute this actions
-among two or three roles (named "MyGetModel", "MyGetRS" and "MyGetItem"). Remember to require the actions you are chaining to!
-
-Distributing the code among several roles makes your code more flexible. If one of your controllers should get the model in
-a different way, but needs the same resultset and item code, you can consume the "MyGetRS" and "MyGetItem" roles and implement 
-the "get_model" action in your controller. If you often need all actions in the same controller, and you don't want to write 3 three
-"with"-lines, you can create a role which includes all three actions:
-
-The "reunion-role":
-
-	package CatalystX::TraitFor::Controller::ModelActions;
-
-	use Moose::Role;
-	with qw/
-		CatalystX::TraitFor::Controller::MyGetModel
-		CatalystX::TraitFor::Controller::MyGetRS
-		CatalystX::TraitFor::Controller::MyGetItem
-	/;
-
-	no Moose::Role;
-	1;
-
-A controller which uses all three action would look like this:
-
-	package MyCompleteController;
-
-	use Moose;
-	extends "Catalyst::Controller";
-	with "CatalystX::TraitFor::Controller::ModelActions";
-
-	__PACKAGE__->meta->make_immutable;
-
-	no Moose;
-	1;
-
-A controller with a custom "get_model" action would look like this:
-
-	package MyPartlyController;
-
-	use Moose;
-	extends "Catalyst::Controller";
-	with qw/
-		CatalystX::TraitFor::Controller::MyGetRS
-		CatalystX::TraitFor::Controller::MyGetItem
-	/;
-
-	sub get_model :Chained("/") :CaptureArgs(0) :PathPart(""){ ... }
-
-	__PACKAGE__->meta->make_immutable;
-
-	no Moose;
-	1;
-
-=head3 Don't force me! Don't force yourself! Don't force anybody!
-
-The second problem in my example is that several things which should be flexible 
-are hardcoded in my roles. The most important examples are the name of the model and the
-name of the table. This means that we can easily add these actions to any controller, but all
-controllers would do EXACTLY the same thing, which is not what we want. Even if you plan to 
-use your role exactly once in each application, you force your application to use the same 
-model name and table name as your role.
-
-Using attributes to store these information makes your roles configurable and much more
-reusable:
-
-	package CatalystX::TraitFor::Controller::MyGetModel;
-
-	use MooseX::MethodAttributes::Role;
-
-	has "model_name" => (
-		is => 'rw',
-		isa => 'Str',
-		default => sub{
-			"DB",
-		},
-	);
-
-	sub get_model :Chained('/') :CaptureArgs(0){
-		my ($self, $c) = @_;
-		my $model = $c->model($self->model_name);
-		unless($model){ 
-			# handle fatal error
-		}
-
-		$c->stash(
-			model => $model,
-		);	
-	}
-
-	use namespace::autoclean;
-	1;
-
-With this modification, you can configure the model name for each controller:
-
-	package MyDifferentController;
-
-	use Moose;
-	extends "Catalyst::Controller";
-	with "CatalystX::TraitFor::Controller::ModelActions";
-
-	...
-	
-	__PACKAGE__->config(
-		model_name => "AnotherDB",
-	);
-
-	__PACKAGE__->meta->make_immutable;
-
-	no Moose;
-	1;
-
-=head3 Whats your name?
-
-The next problem is related to the previous one. The stash-keys of model, resultset and item are hardcoded aswell.
-This may result in conflicting names, overwritten values in the stash and a lot of trouble. Avoid this by making the 
-stash-keys configurable aswell. The default values can even be created dynamically:
-
-	package CatalystX::TraitFor::Controller::MyGetModel;
-
-	use MooseX::MethodAttributes::Role;
-
-	has "stash_model_as" => (
-		is => 'rw',
-		isa => 'Str',
-		default => sub{
-			my @split = split "::", ref(shift);
-			my $controllername = pop @split;
-			$controllername =~ tr/[A-Z]/[a-z]/;
-			return $controllername . "_model";
-		},
-	);
-
-	has model_name => ( ... );
-
-	sub get_model :Chained('/') :CaptureArgs(0){
-		my ($self, $c) = @_;
-		my $model = $c->model($self->model_name);
-		unless($model){ 
-			# handle fatal error
-		}
-
-		my $model_as = $self->stash_model_as;
-		$c->stash(
-			$model_as => $model,
-		);	
-	}
-
-	use namespace::autoclean;
-	1;
-
-You will have to modify your "get_resultset" action aswell:
-
-	package CatalystX::TraitFor::Controller::MyGetResultSet;
-
-	use MooseX::MethodAttributes::Role;
-
-	has 'table_name' => ( ... );
-
-	has 'stash_table_as' => ( ... );
-
-	sub get_resultset :Chained('get_model') :CaptureArgs(0){
-		my ($self, $c) = @_;
-		my $table = $c->stash->{$self->stash_model_as}->resultset($self->table_name);
-
-		...
-	}
-
-	use namespace::autoclean;
-	1;
-
-In this example, the default stash keys are created dynamically from the controllers name.
-If you apply the roles to a controller named "MyApp::Controller::Foo", the model will be 
-stashed as "foo_model". If you don't like this behaviour, you can override the default in
-the __PACKAGE__->config(...);
-
-
-=head3 Sorry, babe! I don't remember you...
-
-Now we know how to write reusable, modular and configurable code with chained actions and Moose::Role.
-My last tip is not new. In fact, its kind of old-fashioned:: Choose "good" names for your roles, and PLEASE write a POD for your modules! 
-If you use ControllerRoles as intensive as I do, most of your controllers will only consist of the package name, a few
-"Moose" lines, the list of consumed roles and some configuration.
-If you choose "good" names for your roles, the controllers code will be more or less self-explanatory.
-If you don't choose your names wisely, it will be hard to understand what the consuming controller
-does.
-
-Here is an Example: Try to guess the purpose of the following controllers:
-
-=over
-
-=item 1.
-
-	package MyApp::Controller:Foo;
-
-	use Moose;
-	extends "Catalyst::Controller";
-	with "CatalystX::TraitFor::Controller::MyRole";
-
-	__PACKAGE__->meta->make_immutable;
-	no Moose;
-	1;
-
-No chance!
-
-=item 2.
-
-	package MyApp::Controller:Bar;
-
-	use Moose;
-	extends "Catalyst::Controller";
-	with "CatalystX::TraitFor::Controller::ChainedCRUD";
-
-	__PACKAGE__->meta->make_immutable;
-	no Moose;
-	1;
-
-Hard to guess. Is this a CRUD controller? It might use chained actions...
-
-=back
-
-When you write the documentation for your roles, remember to include all attributes and actions.
-You should include some extra information about your chained actions:
-
-=over
-
-=item * how many arguments does this action expect by default, and which
-
-=item * what items does this action expect to be in the stash
-
-=item * which items in the stash are modified
-
-=item * which items are added to the stash
-
-=item * which keys are used for each of the items, and where do the keys come from
-
-=back
-
-When you add all these information, everybody (including yourself) will be able to 
-understand the purpose of your roles, and how to use them. Well, not everybody will 
-be able to understand your code. Maybe not even you. But the chance that yourself and others
-understand and use your code increases.
-
-=head2 Conclusion
-
-=over
-
-=item * Chaining actions can make your code more reusable
-
-=item * Making your actions as atomic as possible increases flexibility and reusability
-
-=item * It is possible to write reuseable, chained actions in Moose::Role's
-
-=item * Making your roles as configurable as possible dramatically increases the chance that you (and others) find it usefull in other projects
-
-=item * Moose helps you making your modules configurabel in an easy and flexible way
-
-=item * Roles can easily be applied to controllers. This makes it possible to create small
-"Controller-Bricks" which can be plugged to almost every controller.
-
-=item * Clever naming and documentation is mandatory
-
-=item * The author does not like violent movies, but he sometimes likes violent titles
-
-=back
-
-=head2 Whats next?
-
-The "ControllerRole ChainAction Massacre Part 2" will deal with
-
-=over
-
-=item * how to use BUILD and BUILDARGS methods in ControllerRoles
-
-=item * some more examples
-
-=item * some restrictions related to Moose::Role
-
-=item * how to bypass some of these restrictions
-
-=item * performance issues
-
-=back
-
-=head2 Author:
-
-Lukas Thiemeier <lukast at cpan.org>




More information about the Catalyst-commits mailing list