[Catalyst-commits] r12414 - trunk/examples/CatalystAdvent/root/2009/pen

zts at dev.catalyst.perl.org zts at dev.catalyst.perl.org
Wed Dec 16 23:16:12 GMT 2009


Author: zts
Date: 2009-12-16 23:16:12 +0000 (Wed, 16 Dec 2009)
New Revision: 12414

Added:
   trunk/examples/CatalystAdvent/root/2009/pen/gitalist.pod
Modified:
   trunk/examples/CatalystAdvent/root/2009/pen/ideas.txt
Log:
First cut of gitalist advent entry.  Needs revision.


Added: trunk/examples/CatalystAdvent/root/2009/pen/gitalist.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2009/pen/gitalist.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2009/pen/gitalist.pod	2009-12-16 23:16:12 UTC (rev 12414)
@@ -0,0 +1,118 @@
+=head1 Keeping Your Model Reusable
+
+=head2 Introduction
+
+Catalyst's MVC (Model/View/Controller) structure gently guides us to
+separate the code handling the various aspects of our application, without
+insisting that we do the right thing.
+
+Good advice, and an oft-heard refrain, is to avoid creating fat controllers.
+What makes a controller fat?  The inclusion of code concerned with doing
+the work your application is built to do.  If you were building an app to
+translate text from one languate to another, the code performing the 
+translation logic should be neatly wrapped up in a model.
+
+This approach improves your life in a variety of ways.  The resulting code
+is easier to understand, easier to debug, and easier to test.  Taken one
+step further, it also makes it trivial to re-use.
+
+=head2 Gitalist
+
+Gitalist began as a project to implement gitweb (the Git web viewer) as a
+Catalyst application.  While the main aim was to produce a web application,
+we knew that we'd want to be able to write other, non-web tools that had
+access to the same functionality.
+
+With this in mind, we needed our Catalyst model (C<Gitalist::Model::GitRepos>)
+to serve as a thin layer of glue connecting our real model (C<Gitalist::Git>)
+into the application.  Fortunately, this is incredibly easy.
+
+=head2 The Real Model
+
+First, we implement the real model.  In Gitalist, we needed to represent 
+both an individual git repository (a Project), and the directory containing
+a collection of Projects (a Repo).
+
+A simplified version of the Repo class looks like this:
+
+  use MooseX::Declare;
+
+  class Gitalist::Git::Repo {
+    has path     => ( isa => Dir, required => 1 );
+    has projects => ( isa        => ArrayRef['Gitalist::Git::Project'],
+                      required   => 1,
+                      lazy_build => 1 );
+
+    method _build_projects {
+      # return an array of each git repository found.
+    }
+
+    method get_project ($project_name) {
+      my $project_path = $self->path->subdir($project_name);
+      return Gitalist::Git::Project->new( $project_path );
+    }
+  }
+
+While the actual code is more complex, there's no trace of Catalyst in
+sight - exactly as it should be!  This class can readily be used in other
+other applications, and is very easy to test.
+
+=head2 The Catalyst Model
+
+If all our actual logic is contained in the real model, it follows that our
+Catalyst model will contain none.  Sure enough, we do the minimum amount
+of work required to create an instance of C<Gitalist::Git::Repo>. 
+  
+  package Gitalist::Model::GitRepos;
+  use Moose;
+
+  extends 'Catalyst::Model';
+  with 'Catalyst::Component::InstancePerContext';
+
+  use Gitalist::Git::Repo;
+
+  has repo_dir => ( isa => Str, is => 'ro', lazy_build => 1 );
+  sub _build_repo_dir {
+    # code to determine the value of repo_dir
+  }
+
+  sub build_per_context_instance {
+     my ($self, $app) = @_;
+
+     Gitalist::Git::Repo->new(repo_dir => $self->repo_dir);
+  }
+
+We used L<Catalist::Component::InstancePerContext> which will run
+C<build_per_context_instance> for each new request.  This suits Gitalist,
+but for other uses it may be preferable for your model to persist between
+requests - in that case, L<Catalyst::Model::Adaptor> is what you'll need.
+
+=head2 The Controller
+
+With the model in place, the controller is straightforward.  If we've done
+everything right, we shouldn't even notice that our model exists outside
+Catalyst.  Sure enough, something like the below will do what you expect.
+
+  sub project : Chained('base') CaptureArgs(1) {
+    my($self, $c, $project_name) = @_;
+
+    my $project = $c->model->('GitRepos')->get_project($project_name);
+    $c->detach('/error_404') unless $project;
+
+    $c->stash( Project => $project );
+  }
+
+=head2 Further reading
+
+L<Catalyst::Component::InstancePerContext>
+L<Catalyst::Model::Factory::PerRequest>
+L<Catalyst::Model::Adapter>
+
+L<Gitalist::Model::GitRepos>
+
+=head1 AUTHOR
+
+Zac Stevens <zts at cryptocracy.com>
+
+
+

Modified: trunk/examples/CatalystAdvent/root/2009/pen/ideas.txt
===================================================================
--- trunk/examples/CatalystAdvent/root/2009/pen/ideas.txt	2009-12-16 23:06:23 UTC (rev 12413)
+++ trunk/examples/CatalystAdvent/root/2009/pen/ideas.txt	2009-12-16 23:16:12 UTC (rev 12414)
@@ -8,7 +8,7 @@
  - Plack + Catalyst - dhoss
  - DBIx::Class::InstancePerContext & DBIx::Class::Schema::RestrictWithObject   - zamolxes and dhoss? groditi?
  - DBIx::Class::Helpers - frew 
- - gitalyst - zts
+ - gitalist - zts
  - ribasushi multicreate
  - config for beginners (t0m)
 




More information about the Catalyst-commits mailing list