Updated helper
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Manual / Tutorial.pod
index 6ad32ae..6c395af 100644 (file)
@@ -14,10 +14,13 @@ is to install the Catalyst bundle from CPAN:
     $ perl -MCPAN -e 'install Task::Catalyst'
 
 This will retrieve Catalyst and a number of useful extensions and 
-install them for you.
+install them for you. This process might not be totally painless
+though, and you might want to look at CatInABox 
+L<http://use.perl.org/~jk2addict/journal/28071>, especially if you are
+on a system that lacks a compiler.
 
 
-=head2 Setting up your application
+=head2 The very basics - Setting up the skeleton application.
 
 Catalyst includes a helper script, C<catalyst.pl>, that will set up a 
 skeleton application for you:
@@ -26,46 +29,13 @@ skeleton application for you:
 
     created "MyApp"
     created "MyApp/script"
-    created "MyApp/lib"
-    created "MyApp/root"
-    created "MyApp/root/static"
-    created "MyApp/root/static/images"
-    created "MyApp/t"
-    created "MyApp/t/Model"
-    created "MyApp/t/View"
-    created "MyApp/t/Controller"
-    created "MyApp/lib/MyApp"
-    created "MyApp/lib/MyApp/Model"
-    created "MyApp/lib/MyApp/View"
-    created "MyApp/lib/MyApp/Controller"
-    created "MyApp/lib/MyApp.pm"
-    created "MyApp/Build.PL"
-    created "MyApp/Makefile.PL"
-    created "MyApp/README"
-    created "MyApp/Changes"
-    created "MyApp/t/01app.t"
-    created "MyApp/t/02pod.t"
-    created "MyApp/t/03podcoverage.t"
-    created "MyApp/root/static/images/catalyst_logo.png"
-    created "MyApp/root/static/images/btn_120x50_built.png"
-    created "MyApp/root/static/images/btn_120x50_built_shadow.png"
-    created "MyApp/root/static/images/btn_120x50_powered.png"
-    created "MyApp/root/static/images/btn_120x50_powered_shadow.png"
-    created "MyApp/root/static/images/btn_88x31_built.png"
-    created "MyApp/root/static/images/btn_88x31_built_shadow.png"
-    created "MyApp/root/static/images/btn_88x31_powered.png"
-    created "MyApp/root/static/images/btn_88x31_powered_shadow.png"
-    created "MyApp/root/favicon.ico"
-    created "MyApp/script/myapp_cgi.pl"
-    created "MyApp/script/myapp_fastcgi.pl"
-    created "MyApp/script/myapp_server.pl"
-    created "MyApp/script/myapp_test.pl"
+    ... output snipped
     created "MyApp/script/myapp_create.pl"
 
-This creates the directory structure shown, populated with skeleton 
+This creates the directory structure, populated with skeleton 
 files.
 
-=head2 Testing out the sample application
+=head2 Testing out the skeleton application
 
 You can test out your new application by running the server script that
 Catalyst provides:
@@ -151,6 +121,12 @@ L<Catalyst::Plugin::Session::State::Cookie>
 
 L<Catalyst::Plugin::Authentication>
 
+L<Catalyst::Plugin::Authentication::Store::Minimal>
+
+L<Catalyst::Plugin::Authentication::::Minimal>
+
+L<Catalyst::Plugin::Authentication::Credential::Password>
+
 L<Catalyst::Plugin::Authorization::Roles>
 
 L<DBD::SQLite>
@@ -161,6 +137,13 @@ To get started all you need to do is type:
 
 B<catalyst.pl tutorial>
 
+=for commentary
+Poor choice of application name - searching for "tutorial" in the docs
+also results in discussion of the tutorial process, which is probably
+not what the reader wants.
+
+=cut
+
 This should create a directory called F<tutorial> and fill it with the
 default (standard) Catalyst installation. Change to this directory
 because we will be running all further commands from inside the
@@ -191,9 +174,9 @@ The simplest way to debug your Catalyst application is to run it using
 the built-in mini-server as described in L<Getting started>. 
 
 If you want to output any debugging information to the console, then
-call C<< $context->log->debug() >>, passing it a string to output. For
+call C<< $c->log->debug() >>, passing it a string to output. For
 data structures, use Data::Dumper and call C<<
-$context->log->debug(Dumper($structure)) >>
+$c->log->debug(Dumper($structure)) >>
 
 =head2 Model/View/Controller
 
@@ -254,23 +237,22 @@ commented out code which we will ignore for now. To make something
 happen when our URL is visited, we will write a "greet" action which
 looks like this:
 
- sub greet : Local
- {
-    my ($self, $context) = @_;
+  sub greet : Local {
+    my ($self, $c) = @_;
 
-    my $name = $context->req->params->{name};
-    $context->log->debug("Got name: $name\n");
+    my $name = $c->req->param('name');
+    $c->log->debug("Got name: $name\n");
 
-    if(!$name)
-    {
-       $context->stash->{message} = 'Please fill in a name!';
-    }
-    else
-    {
-        $context->stash->{message} = "Hello $name!";     
+    if ($c->req->method eq 'POST') {
+       if(!$name) {
+            $c->stash->{message} = 'Please fill in a name!';
+        }
+        else {
+            $c->stash->{message} = "Hello $name!";     
+        }
     }
-    $context->stash->{template} = 'greet.tt';
- }
+    $c->stash->{template} = 'greet.tt';
+  }
 
 Whew! So, what does all this do? Lets take it one step at a time.
 The subroutine declaration gives the action a name.  To the right of the
@@ -281,34 +263,34 @@ from the namespace minus the tutorial::controller portion, that is
 common to all controllers, and the action name itself.  Because it is a
 URI, we use forward slashes instead of double colons.  So, in summary,
 when a user requests http://localhost:3000/users/greet" the "greet"
-action defined above in the users controller will be executed.
+action defined above in the Users controller will be executed.
 
 The second line retrieves the parameters Catalyst gives us when it calls
 our method. The first is the instance of our Users class, and the second
-is commonly called the context. The context is the magical object
-containing any information you need from catalyst, or want to send to
-it. You will see it used frequently in Catalyst applications, and a list
-of all its methods is available in the L<Catalyst> POD.
+is commonly called the context, and named $c.  From now on, whenever we 
+are talking about the context object, it will be represented as $c in the code.
+
+The context is the magical object containing any information you need from catalyst, 
+or want to send to it, and is passed from action to action. You will see it used 
+frequently in Catalyst applications, and a list of all its methods is available in 
+the L<Catalyst> POD.
 
-On the third line we use the ->params method of the $context request
-object to retrieve one of the query parameters, just like in L<CGI>.
+On the third line we use the ->param method of the context's request object
+to retrieve one of the query parameters, just like in L<CGI>.
 
-On the fourth, we make a debug output of this object on the server console. 
-(NB where does ->debug go if we're running under CGI or mod_perl?)
-(NB what is $self good for under CGI/mod_perl/anything?)
+On the fourth, we make a debug output of this object on the server console,
+or the error log if running under CGI or mod_perl.
 
-Next, we check if the name field contains anything (or is "true"), if it
-isnt, we assign an error message to a "message" field in the stash. The
-stash is yet another method of the context object, it allows us to pass
-data on to other methods we call later, most usefully the View modules.
+Next, if we have a post request, we check if the name field contains anything 
+(or is "true"), if it isnt, we assign an error message to a "message" field in 
+the stash. The stash is yet another method of the context object, it allows us
+to pass data on to other methods we call later, most usefully the View modules.
 
 If the username did contain a value, then we just set our message to
 greet the user by name.
 
 Finally, we set the special "template" variable in the stash to the name
 of the template we want our view to use to display this page.
-(NB doing the param name checking this way will also complain when we
-visit the form for the first time, check for presence of params at all?)
 
 =head2 Viewing
 
@@ -334,10 +316,9 @@ so on.
 Since we're writing a simple application, just add an end action like
 this to F<tutorial.pm>:
 
- sub end : Private
- {
-   my ($self, $context) = @_;
-   $context->forward('tutorial::View::TToolkit');
+ sub end : Private {
+   my ($self, $c) = @_;
+   $c->forward('tutorial::View::TToolkit') unless $c->res->body();
  }
 
 The first line declares the end sub, and marks it as a Private action.
@@ -358,12 +339,23 @@ for example, using [% c.config.name %] in our template will output
 "tutorial", our project name.
 
 All that remains is to create a simple template called "greet.tt",
-containing a form with a text field called "name", and place it in the
-F<root> directory. By default, templates are searched for here, but we
-can change that, which brings us to..
+containing a form with a text field called "name" like below.
 
-(NB Can we ask catalyst which paths exist to provide a dynamic list of
-them? (eg greet, age etc .. regexed paths??)
+    <html><head><title> [% c.name %]</head><body>
+    <p>[%message%]</p>
+    <form action="[%c.req.uri%]" method="post">
+    <input type="text" name="name"/>
+    <input type="submit" value="Submit" name="submit"/>
+    </form>
+    </body></html>
+
+In the example above, we use [%c.req.uri%], since we're posting to ourself.
+if we post to another action, we commonly use the uri_for method, like this:
+
+ [% c.uri_for('/users/greet')%]
+
+Place this file in the F<root> directory, . By default, templates are 
+searched for here, but we can change that, which brings us to...
 
 =head2 Configuring
 
@@ -434,32 +426,27 @@ export our data to disk and read it back into the config on startup)
 To allow creation of new users we'll add a create action to our Users 
 controller.
 
- sub create : Local
- {
-    my ($self, $context) = @_;
-    my ($username, $passwd1, $passwd2) = map { $context->req->param($_)} 
+ sub create : Local {
+    my ($self, $c) = @_;
+    my ($username, $passwd1, $passwd2) = map { $c->req->param($_)} 
        ('username', 'password', 'passwordverify');
 
-    if($username && $passwd1 && $passwd2)
-    {
-       if($context->config->{authentication}{users}{$username})
-       {
-          $context->stash->{message} = 'Sorry that user already exists';
-          $context->stash->{username} = $username;
+    if($username && $passwd1 && $passwd2) {
+       if($c->config->{authentication}{users}{$username}) {
+          $c->stash->{message} = 'Sorry that user already exists';
+          $c->stash->{username} = $username;
        }
-       elsif($passwd1 eq $passwd2)
-       {
-          $context->config->({%{$context->config},
-             ($username => { password => $passwd1}});
-          $context->stash->{message} = 'User created!';
+       elsif($passwd1 eq $passwd2) {
+            $c->config->{authentication}->{users}->{$username} =
+                {password => $passwd1};
+          $c->stash->{message} = 'User created!';
        }
-       else
-       {
-          $context->stash->{username} = $username;
-          $context->stash->{message} = 'Passwords don't match!';
+       else {
+          $c->stash->{username} = $username;
+          $c->stash->{message} = 'Passwords do not match!';
        }
     }
-    $context->stash->{template} = 'usercreate.tt';
+    $c->stash->{template} = 'usercreate.tt';
  }
 
 All this is doing is checking that all the appropriate fields are filled, 
@@ -467,18 +454,45 @@ the password fields contain the same data, and then adding the user to the
 config hash.  All the checks produce a message which can be displayed to 
 the user via the View.
 
-So our that users can login, we need a login page: 
-
- sub login : Local
- {
-    my ($self, $context) = @_;
-    $context->stash->{template} = 'userlogin.tt';
-    if(!$context->login())
-    {
-       $context->stash->{message} = 'Login failed.';
-    }
+The usercreate.tt template looks like this:
+
+ <html><head><title>[% c.config.name %]</title></head><body>
+ <h1>Create a new user</h1>
+ <p> [% c.stash.message %] </p>
+ <form action="/users/create" method="post">
+ <p>User Name: <input type="text" name="username"/></p>
+ <p>Password: <input type="password" name="password"/></p>
+ <p>Confirm Password: <input type="password" name="passwordverify"/></p>
+ <p><input type="submit" name="submit" value="submit"></p>
+ </form>
+ </body></html>
+
+So our that users can login, we need a login action which we put in the
+Users controller:
+
+ sub login : Local {
+     my ($self, $c) = @_;
+     $c->stash->{template} = 'userlogin.tt';
+     if(!$c->login()) {
+             $c->stash->{message} = 'Please login.';
+     }
+     else {
+         $c->stash->{message} = "Welcome " . $c->user->id;
+     }
  }
 
+
+And the userlogin.tt template:
+
+ <html><head><title>[% c.config.name %]</title></head><body>
+<p> [% c.stash.message %] </p>
+ <form name='login' action='/users/login' method='post'>
+ <p>Username: <input type='text' name='user' /></p>
+ <p>Password: <input type='password' name='password' /></p>
+ <p><input type="submit" /></form>
+ </body></html>
+
+
 Verrrry simple. Since Credential::Password's "login" call extracts the 
 username/password data from the query itself (assuming we use a standard 
 name for our form fields), we don't have to do anything but call it.
@@ -493,15 +507,28 @@ our collection, and the Auth modules will automatically use them;
 
 Magic!
 
+=head2 Exercise
+
+As an exercise for the reader, do the following:
+
+Change users/greet and greet.tt so that the welcome message greets the
+user by name.
+
+Enforce user logging in by adding an auto action in tutorial.pm (see
+the L<Catalyst> documentation to find out about the auto action).
+
 =head2 Authorising
 
-Authentication is about verifying users, Authorisation is about allowing
-them to do things. Catalyst currently has two Authorisation modules,
-Roles and ACL. The Roles module allows you to define groups which you
-can assign your users to, and then allow access to areas of your website
-to the groups. The ACL module lets you do more fine grained
-access/restriction by allowing of denying access however you like. (It
-also supports Roles as done by the roles module.)
+Authentication is about verifying users, Authorisation is about
+allowing them to do things. Catalyst currently has two Authorisation
+modules, Roles and ACL. The Roles module allows you to define groups
+which you can assign your users to, and then allow access to areas of
+your website to the groups. The ACL module lets you do more fine
+grained access/restriction by allowing of denying access however you
+like. (It also supports Roles as done by the roles module.)  This
+example uses Catalyst::Plugin::Authorization::Roles.  To use this add
+"Authorization::Roles" into the "use Catalyst" statement in
+tutorial.pm.
 
 Adding Roles via the Minimal store we are already using is quite simple,
 we just add a roles key to each user, defining the names of the roles
@@ -510,41 +537,35 @@ they belong to.
  authentication => { 'users' =>
                             { 'fred' => 
                                { 'password' => 'fred1234',
-                                 'roles'       => ['admin'] 
+                                 'roles'       => ['admin']
                                }
                             }
                          }
 
 We need an interface for our admins to administer the roles, i.e. assign
 the users to groups. To restrict access to certain actions, we just need
-to call C<< $context->check_user_roles() >> in each action. So we can
+to call C<< $c->check_user_roles() >> in each action. So we can
 make a restricted I<localhost:3000/users/groups> page like this:
 
- sub groups : Local
- {
-    my ($self, $context) = @_;
-    if($context->check_user_roles('admin'))
-    {
+ sub groups : Local {
+    my ($self, $c) = @_;
+    if($c->check_user_roles('admin')) {
        # Now we can do things only an admin will see
-       if(my $params = $context->req->params)
-       {
-          my $users = $context->config->{authentication}{users};
-          foreach my $u (keys %$params)
-          {
+       if(my $params = $c->req->params) {
+          my $users = $c->config->{authentication}{users};
+          foreach my $u (keys %$params) {
              $users->{$u}{roles} = $params->{$u} if($users->{$u});
           }
-          $context->stash->{message} = 'Updated user roles!';
+          $c->stash->{message} = 'Updated user roles!';
        }
-       else
-       {
-           $context->stash->{users} = $context->config->{authentication};
+       else {
+           $c->stash->{users} = $c->config->{authentication};
        }
-       $context->stash->{template} = 'usersgroups.tt';
+       $c->stash->{template} = 'usersgroups.tt';
     }
-    else
-    {
-        $context->stash->{message} = 'Admins Only!';
-        $context->stash->{template} = 'error.tt';
+    else {
+        $c->stash->{message} = 'Admins Only!';
+        $c->stash->{template} = 'error.tt';
     }
  }
 
@@ -553,8 +574,35 @@ default in the check_user_roles method), is a member of the admin group.
 If it is, then we display the usergroups template, and update the users
 hash as required. Otherwise, we just show the user an error page.
 
+For this simple example, the usersgroups.tt and error.tt templates could
+both look like this:
+
+ <html><head><title>[% c.config.name %]</title></head><body>
+ <p>[% c.stash.message %]</p>
+ <p>[% c.stash.users %]</p>
+ </body></html>
+
 And that's all there is to it.
 
+=for authors
+So it's not clear what the groups action is doing - and with the
+current template, nothing happens.  Running through the sample code,
+it's clear what's happening, (which is very little), but the purpose,
+and how to display data is not clear.
+
+=cut
+
+So that you can test this out properly without having to go to the
+trouble of deleting browser cookies manually, we will add a logout
+action in the Users controller:
+
+ sub logout : Local {
+    my ($self, $c) = @_;
+    $c->stash->{message} = "You have successfully logged out";
+    $c->logout;
+ }
+
+
 =head2 Data Storage (Modelling)
 
 Whether we want our users to be able to contribute to our website, or just
@@ -581,28 +629,24 @@ module in the Model::UserData namespace for each table in your database.
 Now we need a form for our users to enter/edit their personal greetings in,
 we'll make a I<localhost:3000/users/editgreeting> page: 
 
- sub editgreeting : Local
- {
-    my ($self, $context) = @_;
-    if($context->req->params->{greeting})
-    {
-       if(!$context->user_exists)
-       {
-          $context->stash->{message} = "You're not logged in!";
+ sub editgreeting : Local {
+    my ($self, $c) = @_;
+    if($c->req->params->{greeting}) {
+       if(!$c->user_exists) {
+          $c->stash->{message} = "You're not logged in!";
        }
-       else
-       {
-          my $grtable = $context->model('UserData::Greetings');
-          my $record = $grtable->find_or_create(user => $context->user->id);
-          $record->greeting($context->req->params->{greeting};
+       else {
+          my $grtable = $c->model('UserData::Greetings');
+          my $record = $grtable->find_or_create(user => $c->user->id);
+          $record->greeting($c->req->params->{greeting};
           $record->update;
-          $context->stash->{message} = 'Greeting updated';
+          $c->stash->{message} = 'Greeting updated';
        }
     }
-    $context->stash->{template} = 'usersgreeting.tt';
+    $c->stash->{template} = 'usersgreeting.tt';
  }
 
-Using C<< $context->user_exists >> from the Authentication plugin, this checks
+Using C<< $c->user_exists >> from the Authentication plugin, this checks
 whether the user is logged in already. If they are, if they are, and they have
 entered a new greeting, we use DBIx::Class' C<find_or_create> to fetch or
 create a new record in the greetings table for the user. Once we have the
@@ -625,7 +669,7 @@ or
 
 =head3 Apache
 
-Apache also needs configuring, we need to tell it to load your
+Apache also needs to be configured: we need to tell it to load your
 application. You can either use Catalyst for your entire website, or
 subsections. Use the Location directive to choose a path to run your
 application under: 
@@ -654,15 +698,25 @@ application is accessed.
 
 =head3 FastCGI
 
-These instructions apply to the use of C<mod_fastcgi> under Apache (either 1 or 2 series).
+These instructions apply to the use of C<mod_fastcgi> under Apache
+(either 1 or 2 series).
 
-There are 3 ways to attach a program to a URL with C<mod_fastcgi>; we'll examine all of them, and explain how to avoid having the C<myapp_fastcgi.pl> substring in the user-visible URLs.
+There are 3 ways to attach a program to a URL with C<mod_fastcgi>;
+we'll examine all of them, and explain how to avoid having the
+C<myapp_fastcgi.pl> substring in the user-visible URLs.
 
-In all of these examples, we assume that the C<DocumentRoot> is C</var>, that our app is called C<MyApp> and is kept in C</usr>, that you want the users to access the app either from the root of the server-uri-space, or from C</theapp>. We also assume that the general FastCGI settings (C<FastCgiIpcDir>, loading the module) are already correct (they don't depend on Catalyst or your application layout).
+In all of these examples, we assume that the C<DocumentRoot> is
+C</var>, that our app is called C<MyApp> and is kept in C</usr>, that
+you want the users to access the app either from the root of the
+server-uri-space, or from C</theapp>. We also assume that the general
+FastCGI settings (C<FastCgiIpcDir>, loading the module) are already
+correct (they don't depend on Catalyst or your application layout).
 
 =head4 static application
 
-In this setup, you tell C<mod_fastcgi> that a particular I<file> is to be run as a FastCGI handler. Put this somewhere in Apache's configuration:
+In this setup, you tell C<mod_fastcgi> that a particular I<file> is to
+be run as a FastCGI handler. Put this somewhere in Apache's
+configuration:
 
   FastCgiServer /usr/apps/MyApp/script/myapp_fastcgi.pl
   Alias / /usr/apps/MyApp/script/myapp_fastcgi.pl/
@@ -671,11 +725,16 @@ If you want your app under C</theapp>, change the C<Alias> line to:
 
   Alias /theapp /usr/apps/MyApp/script/myapp_fastcgi.pl
 
-Note the detail of the trailing C</ >: this is a general rule af the C<Alias> directive, both sides must end with C</ >, or both must not; you can't have one with C</ > and the other without, or strange things happen.
+Note the detail of the trailing C</ >: this is a general rule af the
+C<Alias> directive, both sides must end with C</ >, or both must not;
+you can't have one with C</ > and the other without, or strange things
+happen.
 
 =head4 dynamic application
 
-In this setup, you tell C<mod_fastcgi> that certain files are to be treated as FastCGI handlers, in the same way you have to tell C<mod_cgi>. Put this in the configuration:
+In this setup, you tell C<mod_fastcgi> that certain files are to be
+treated as FastCGI handlers, in the same way you have to tell
+C<mod_cgi>. Put this in the configuration:
 
   FastCgiConfig -autoUpdate
 
@@ -694,16 +753,23 @@ Again, if you want your app under C</theapp>, change the C<Alias> line to:
 
 =head4 external server
 
-In this setup, the application is started separately from Apache, and communicates via a socket with C<mod_fastcgi>. This can be useful if you need to have a particular environment for your application (maybe different between applications), or you want to run them on different machines, or under different users for security reasons.
+In this setup, the application is started separately from Apache, and
+communicates via a socket with C<mod_fastcgi>. This can be useful if
+you need to have a particular environment for your application (maybe
+different between applications), or you want to run them on different
+machines, or under different users for security reasons.
 
-If you want to use a UNIX socket (on the filesystem), put this in Apache's configuration:
+If you want to use a UNIX socket (on the filesystem), put this in
+Apache's configuration:
 
   FastCgiExternalServer /tmp/somewhere -socket /tmp/myapp-socket
   Alias / /tmp/somewhere/
 
-Note that C</tmp> should I<not> exist: it's just a name to connect the two parts.
+Note that C</tmp> should I<not> exist: it's just a name to connect the
+two parts.
 
-Again, if you want your app under C</theapp>, change the C<Alias> line to:
+Again, if you want your app under C</theapp>, change the C<Alias> line
+to:
 
   Alias /theapp /tmp/somewhere
 
@@ -712,7 +778,9 @@ Then start your Catalyst application:
   $ cd /usr/apps/MyApp
   $ ./script/myapp_fastcgi -l /tmp/myapp-socket
 
-If you want to use a TCP socket, simply change the C</tmp> to a C<host:port> pair, both in Apache's configuration and on the command line of your application.
+If you want to use a TCP socket, simply change the C</tmp> to a
+C<host:port> pair, both in Apache's configuration and on the command
+line of your application.
 
 =head2 Upgrading
 
@@ -721,32 +789,35 @@ installing the new Catalyst package, just run:
 
 B<catalyst.pl -scripts>
 
-One level above your application directory. This will update the scripts
-directory only, and leave the rest of your app alone, If you wish to make use
-of other parts of Catalyst that have been updated, leave off the B<-scripts>
-argument, this will cause .new files to appear, for each module that has
-either been updated, or is different to the original because you have changed
-it. To find out what these changes are, type: 
+One level above your application directory. This will update the
+scripts directory only, and leave the rest of your app alone, If you
+wish to make use of other parts of Catalyst that have been updated,
+leave off the B<-scripts> argument, this will cause .new files to
+appear, for each module that has either been updated, or is different
+to the original because you have changed it. To find out what these
+changes are, type:
 
 B<diff MyApp/lib/MyApp/View/TT.pm MyApp/lib/MyApp/View/TT.pm.new>
 
-for each of the changed files. (This is a Unix command, Windows users will
-need to find some equivalent). Copy any changes you need into your original
-file, then remove the .new files. (This makes life less complicated when the
-next upgrade comes around.) 
+for each of the changed files. (This is a Unix command, Windows users
+will need to find some equivalent). Copy any changes you need into
+your original file, then remove the .new files. (This makes life less
+complicated when the next upgrade comes around.)
 
-=head1 AUTHOR
+=head1 AUTHORS
 
 Jess Robinson, C<jrobinson@cpan.org>
 Andrew Ford, C<A.Ford@ford-mason.co.uk>
 Marcus Ramberg, C<mramberg@cpan.org>
+Kieren Diment, C<kd@totaldatasolution.com>
 
 Please send comments, corrections and suggestions for improvements to
 jrobinson@cpan.org
 
 =head1 TODO
 
-Add template examples.
+Finish DBIC examples with templates and tested code.  Make
+/users/groups do something "useful"
 
 Many other things..