initial import of new Tutorial stuff from hkclark
Matt S Trout [Thu, 8 Jun 2006 14:35:00 +0000 (14:35 +0000)]
r8780@cain (orig r4166):  jester | 2006-05-20 03:40:06 +0000

13 files changed:
lib/Catalyst/Manual/Installation.pod [new file with mode: 0644]
lib/Catalyst/Manual/Installation/CentOS4.pod [new file with mode: 0644]
lib/Catalyst/Manual/Tutorial-orig.pod [new file with mode: 0644]
lib/Catalyst/Manual/Tutorial.pod
lib/Catalyst/Manual/Tutorial/AdvancedCRUD.pod [new file with mode: 0644]
lib/Catalyst/Manual/Tutorial/Appendices.pod [new file with mode: 0644]
lib/Catalyst/Manual/Tutorial/Authentication.pod [new file with mode: 0644]
lib/Catalyst/Manual/Tutorial/Authorization.pod [new file with mode: 0644]
lib/Catalyst/Manual/Tutorial/BasicCRUD.pod [new file with mode: 0644]
lib/Catalyst/Manual/Tutorial/CatalystBasics.pod [new file with mode: 0644]
lib/Catalyst/Manual/Tutorial/Debugging.pod [new file with mode: 0644]
lib/Catalyst/Manual/Tutorial/Intro.pod [new file with mode: 0644]
lib/Catalyst/Manual/Tutorial/Testing.pod [new file with mode: 0644]

diff --git a/lib/Catalyst/Manual/Installation.pod b/lib/Catalyst/Manual/Installation.pod
new file mode 100644 (file)
index 0000000..f21dd5b
--- /dev/null
@@ -0,0 +1,131 @@
+=head1 NAME
+
+Catalyst::Manual::Installation – Catalyst Installation
+
+
+
+=head1 DESCRIPTION
+
+This section of the Catalyst tutorial looks at a number of items required to first get going with Catalyst development.
+
+
+
+=head1 INSTALLATION
+
+On one hand, Catalyst gains immediate power and flexibility through is use of CPAN (the Comprehensive Perl Archive Network, an enormous global repository containing over 10,000 free modules).  On the other hand, Catalyst's reliance on CPAN can complicate initial installations.  Fortunately, there are a growing number of methods that can dramatically ease this undertaking:
+
+=over 4
+
+=item * 
+
+Matt Trout’s C<cat-install>
+
+Available at L<http://www.shadowcatsystems.co.uk/static/cat-install>, C<cat-install> can be a quick and painless way to get Catalyst up and running.  Just download the script from the link above and type C<perl cat-install>.
+
+=item * 
+
+Chris Laco's CatInABox
+
+Download the tarball from L<http://handelframework.com/downloads/CatInABox.tar.gz> and unpack it on your machine.  Depending on your OS platform, either run C<start.bat> or C<start.sh>.
+
+=item * 
+
+Pre-Built VMWare Images
+
+Under the VMWare community program, work is ongoing to develop a number of VMWare images where an entire Catalyst development environment has already been installed, complete with database engines and a full complement of Catalyst plugins.
+
+=back
+
+
+=head2 OTHER METHODS
+
+In addition to the "all-in-one" approaches mentioned above, there are a variety of other installation techniques:
+
+=over 4
+
+=item * 
+
+CPAN
+
+The traditional way to install Catalyst is directly from CPAN using C<Task::Catalyst> bundle:
+
+       $ perl –MCPAN –e 'install Task::Catalyst'
+
+Unless you have a particularly complete set of Perl modules already installed, be prepared for a large number of nested dependencies.
+
+=item * 
+
+Gentoo Linux
+
+For users of Gentoo, see C<http://gentoo-wiki.com/HOWTO_Catalyst_Framework> for automated installations.  In short, simply mount the portage overlay and type C<emerge catalystframework>.
+
+=items * 
+
+FreeBSD
+
+FreeBSD users can get up and running quickly by typing C<cd /usr/ports/www/p5-Catalyst && make install>.
+
+=item * 
+
+Windows ActivePerl
+
+Windows users can take advantage of the PPM tool that comes with ActivePerl to jumpstart their Catalyst environment.  Directions are available at L<http://catalyst.infogami.com/katalytes/cat_on_windows>.
+
+=back
+
+B<NOTE:> Although all of the above methods can be used to install a base Catalyst system, only the VMWare image is likely to have all of the plugins and modules you need to complete this tutorial.  When you start the C<script/myapp_server.pl> development server later to run your application, it will tell you about any modules that are missing.  To add them, type something along the lines of the following (C<Catalyst::Model::DBIC::Schema> is used here as a representative example):
+
+    # perl -MCPAN -e shell
+    
+    cpan shell -- CPAN exploration and modules installation (v1.87)
+    ReadLine support enabled
+    
+    cpan> install Catalyst::Model::DBIC::Schema
+    ...
+
+
+=head2 OTHER ITEMS
+
+Although Catalyst is a very flexible platform that can be used for wide variety of applications, the bulk of Catalyst developers use it to build web applications that set on top of a relational database.  Consequently, you will very likely need:
+
+=over 4
+
+=item * 
+
+A Web Server
+
+Although Apache is obviously an extremely popular choice, lighttpd has a growing following.  Catalyst applications can also be run under IIS. 
+
+=item * 
+
+A Database
+
+Although other databases with support for Perl are certainly possible, most Catalyst applications tend to use one of the following:
+
+=item * 
+
+SQLite
+
+SQLite is a popular choice for development and testing because it does note require that a database daemon be running or configured.  It operates on C<.db> files that are generally located in the directory of your Catalyst application project.  More information is available at L<www.sqlite.org>.
+
+=item * 
+
+MySQL
+
+MySQL is an extremely popular database available at L<www.mysql.com>.
+
+=item * 
+
+PostgreSQL
+
+PostgreSQL is a well-respected database available at L<http://www.postgresql.org/>.
+
+=back
+
+This tutorial will primarily focus on SQLite because of its simplicity; however, modifications in the script required to support MySQL and PostgreSQL will be presented in Appendix X.
+
+B<Note:> One of the advantages of the MVC design patterns is that applications become much more database independent.  As such, you will notice that only the C<.sql> files used to initialize the database change between database systems... the Catalyst code all remains the same.
+
+=back
+
+
diff --git a/lib/Catalyst/Manual/Installation/CentOS4.pod b/lib/Catalyst/Manual/Installation/CentOS4.pod
new file mode 100644 (file)
index 0000000..1b12864
--- /dev/null
@@ -0,0 +1,203 @@
+=head1 NAME
+
+Catalyst::Manual::Installation::CentOS4 – Catalyst Installation on CentOS 4
+
+
+
+=head1 DESCRIPTION
+
+This document provides directions on how to install CentOS 4 (a rebuild of RedHat Enterprise 4) and then install Catalyst.
+
+If you already have a functioning install of CentOS, RHEL, or a comparable Linux OS, you should be able to skip this first section and go straight to the C<INSTALL CATALYST> section.
+
+
+=head1 INSTALL CENTOS
+
+These directions are written for CentOS 4.3 on an i386 machine; however, you can substitute other versions as they become available.
+
+
+=over 4
+
+=item * 
+
+Go to L<http://isoredirect.centos.org/centos/4/isos/i386/> and click the nearest mirror.
+
+=item * 
+
+Download C<CentOS-4.3-i386-bin1of4.iso> (you only need the first disk).
+
+=item * 
+
+Burn the .iso to CD.
+
+=item * 
+
+Insert the CD into your machine and power it up.
+
+=item * 
+
+Hit C<Enter> at the C<boot:> prompt.
+
+=item * 
+
+CD media test: you can either select C<OK> or C<Skip> depending on whether or not you trust your burn.
+
+=item * 
+
+The installation GUI should start.  Click next at the "Welcome to CentOS-4" screen.
+
+=item * 
+
+Select a language and click C<Next>.
+
+=item * 
+
+Select a keyboard configuration and click C<Next>.
+
+=item * 
+
+Select C<Custom> for the installation type and click C<Next>.
+
+=item * 
+
+Leave C<Automatically partition> selected on the C<Disk Partitioning Setup> and click C<Next>.
+
+=item * 
+
+Uncheck C<Review (and modify if needed) the partitions created>, but leave the rest of the default settings on the C<Automatic Partitioning> screen.  Then click C<Next>.
+
+=item * 
+
+Click C<Yes> at the C<Are you sure you want to do this?> warning.
+
+=item * 
+
+Click C<Next> on the C<Boot Loader Configuration> screen.
+
+=item * 
+
+Update the C<Network Configuration> screen as necessary and click C<Next>.
+
+=item * 
+
+Check C<Remote Login (SSH)> and click C<Next> on the C<Firewall Configuration> screen.
+
+=item * 
+
+Select additional languages as necessary.  Click C<Next>.
+
+=item * 
+
+Select the appropriate time zone and click C<Next>.
+
+=item * 
+
+Enter a root password and click C<Next>.
+
+=item * 
+
+Scroll to the bottom of the C<Package Group Selection> screen and check C<Minimal> (the last option).  Click C<Next>.
+
+=item * 
+
+Click C<Next> at the C<About to Install> screen.
+
+=item * 
+
+The installation will prepare the hard drive and then install the required rpm packages.
+
+=item * 
+
+Once the installation completes, remove the CD and click C<Reboot>.
+
+=item * 
+
+Type C<vi /etc/sysconfig/iptables> and add the following line as the third to last line of the file (I<above> the C<-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited> line):
+
+    -A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 3000 -j ACCEPT
+
+This will allow Catalyst to make use of port 3000 (the default for the development server).
+
+Type C<service iptables restart> to restart the iptables firewall using the updated configuration.
+
+=item * 
+
+Type C<yum –y update> to retrieve the latest patches.
+
+=back
+
+
+=head1 INSTALL CATALYST
+
+=over 4
+
+=item * 
+
+Type <yum -y install gcc expat-devel sqlite3> to install several packages used by Catalyst.
+
+=item * 
+
+Type the following:
+
+    perl -MCPAN -e shell
+    
+    ...
+    
+    Are you ready for manual configuration? [yes] n
+    
+    ...
+    
+    cpan shell -- CPAN exploration and modules installation (v1.7601)
+    ReadLine support available (try 'install Bundle::CPAN')
+    
+    cpan> force install Module::Build
+    
+    ...
+    
+    cpan> quit
+
+=item * 
+
+Type C<wget http://www.shadowcatsystems.co.uk/static/cat-install> to retrieve a copy of the C<cat-install> script.
+
+=item * 
+
+Type C<vi cat-install> to open the installer script, then insert the following lines at the bottom of the file (after the C<install('Catalyst');> line):
+
+    install('Class::DBI');
+    install('DBIx::Class');
+    install('DBIx::Class::HTMLWidget');
+    install('Catalyst::Plugin::Session');
+    install('Catalyst::Plugin::Session::State::Cookie');
+    install('Catalyst::Plugin::Session::Store::FastMmap');
+    install('Catalyst::Plugin::Authorization::ACL');
+    install('Catalyst::Plugin::Authentication');
+    install('Catalyst::Plugin::Authorization::Roles');
+    install('Catalyst::Plugin::Authentication::Store::DBIC');
+    install('Catalyst::Plugin::DefaultEnd');
+    install('Catalyst::Plugin::StackTrace');
+    install('Catalyst::Plugin::Dumper');
+    install('Catalyst::Model::DBIC::Schema');
+    install('Catalyst::View::TT');
+    install('Test::WWW::Mechanize::Catalyst');
+
+=item * 
+
+Type C<perl cat-install>.  It will take a while to complete.
+
+=back
+
+You should now have a functioning Catalyst installation with the modules and plugins required to run the Catalyst tutorial.
+
+
+
+=head1 AUTHOR
+
+Kennedy Clark, C<hkclark@gmail.com>
+
+Please report any errors, issues or suggestions to the author.
+
+Copyright 2006, Kennedy Clark, under Creative Commons License (L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
+
+Version: .01
+
diff --git a/lib/Catalyst/Manual/Tutorial-orig.pod b/lib/Catalyst/Manual/Tutorial-orig.pod
new file mode 100644 (file)
index 0000000..9df4d8a
--- /dev/null
@@ -0,0 +1,854 @@
+=head1 NAME
+
+Catalyst::Manual::Tutorial - Getting started with Catalyst
+
+=head1 DESCRIPTION
+
+THIS IS THE PREVIOUS VERSION OF THE TUTORIAL, NOW SUPPLANTED
+BY A MULTI-PART ONE FROM HKCLARK. I'M KEEPING IT HERE UNDER
+THIS NAME SO WE CAN MOVE RELEVANT PARTS OF IT INTO OTHER
+C::M::MANUAL SUBSECTIONS, AND THEN DELETE IT.
+
+This document aims to get you up and running with Catalyst.
+
+=head2 Installation
+
+The first step is to install Catalyst, and the simplest way to do this 
+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. This process might not be totally painless
+though, and you might want to look at CatInABox at
+L<http://use.perl.org/~jk2addict/journal/28071>, especially if you are
+on a system that lacks a compiler.
+
+
+=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:
+
+    $ catalyst.pl tutorial
+
+    created "tutorial"
+    created "tutorial/script"
+    ... output snipped
+    created "tutorial/script/tutorial_create.pl"
+
+This creates the directory structure, populated with skeleton 
+files.
+
+=head2 Testing out the skeleton application
+
+You can test out your new application by running the server script that
+Catalyst provides:
+
+    $ cd tutorial
+    $ script/tutorial_server.pl 
+
+    [...] [catalyst] [debug] Debug messages enabled
+    [...] [catalyst] [debug] Loaded plugins:
+    .------------------------------------------------------------------------------.
+    | Catalyst::Plugin::Static::Simple                                             |
+    '------------------------------------------------------------------------------'
+    [...] [catalyst] [debug] Loaded dispatcher "Catalyst::Dispatcher"
+    [...] [catalyst] [debug] Loaded engine "Catalyst::Engine::HTTP"
+    [...] [catalyst] [debug] Found home "/home/users/me/tutorial"
+    [...] [catalyst] [debug] Loaded Private actions:
+    .--------------------------------------+---------------------------------------.
+    | Private                              | Class                                 |
+    +--------------------------------------+---------------------------------------+
+    | /default                             | tutorial                              |
+    '--------------------------------------+---------------------------------------'
+
+    [...] [catalyst] [info] tutorial powered by Catalyst 5.67
+    You can connect to your server at http://localhost:3000
+
+(Note that each line logged by Catalyst begins with a timestamp, which has
+been replaced here with "C<...>" so that the text fits onto the lines.)
+
+The server is now waiting for you to make requests of it.  Try using 
+telnet to manually make a simple GET request of the server (when 
+telnet responds with "Escape character is '^]'.", type "GET / HTTP/1.0"
+and hit return twice):
+
+    $ telnet localhost 3000
+    Trying 127.0.0.1...
+    Connected to localhost.
+    Escape character is '^]'.
+    GET / HTTP/1.0
+
+    HTTP/1.0 200 OK
+    Date: Mon, 07 Nov 2005 14:57:39 GMT
+    Content-Length: 5525
+    Content-Type: text/html; charset=utf-8
+    Status: 200
+    X-Catalyst: 5.67
+
+    [...]
+    Connection closed by foreign host.
+    $
+
+You can see the full welcome message by visiting
+http://localhost:3000/ with your browser.
+
+More trace messages will appear in the original terminal window:
+
+    [...] [catalyst] [debug] **********************************
+    [...] [catalyst] [debug] * Request 1 (0.063/s) [2148]
+    [...] [catalyst] [debug] **********************************
+    [...] [catalyst] [debug] Arguments are ""
+    [...] [catalyst] [debug] "GET" request for "" from localhost
+    [...] [catalyst] [info] Request took 0.046883s (21.330/s)
+    .------------------------------------------------------------------+-----------.
+    | Action                                                           | Time      |
+    +------------------------------------------------------------------+-----------+
+    | /default                                                         | 0.000000s |
+    '------------------------------------------------------------------+-----------'
+
+The server will continue running until you interrupt it.
+
+The application can also be tested from the command line using the generated
+helper script, C<script/tutorial_test.pl>.
+
+=head2 Getting started
+
+So you picked Catalyst. Good choice. We assume you have installed it as
+well. For this tutorial you will also need the following modules:
+
+L<Catalyst::Plugin::Session>
+
+L<Catalyst::Plugin::Session::Store::File>
+
+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>
+
+...
+
+If you have not already done this following the example above, then to get 
+started all you need to do is type:
+
+    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
+F<tutorial> directory.
+
+If you now run the built-in mini-server with:
+    
+    script/tutorial_server.pl
+
+it will show some standard debug messages in the console screen 
+(more about those in a minute), and then inform you that you can now 
+connect to the test server on port 3000. Point your browser at 
+http://localhost:3000/ to see the built-in Catalyst welcome screen.
+
+The other important thing B<catalyst.pl> did was create your root
+controller. This file is a standard Perl module like all the other
+controllers that you might add to your application. It lives in the
+F<lib/> directory, and will have the same name as you supplied to the
+command above. In our case it is F<tutorial.pm>. Alongside this file is
+a directory of the same name, which is the top level namespace for the
+entire application. Thus every other module we create will be
+"tutorial::something".
+
+The root controller is used to load plugins, to configure the
+application and its plugins, and for generic private actions.  We will
+explain more about those later.
+
+=head2 Debugging
+
+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<< $c->log->debug() >>, passing it a string to output. For data
+structures, C<use> L<Data::Dumper> and call C<<
+$c->log->debug(Dumper($structure)) >>
+
+=head2 Model/View/Controller
+
+The recommended method for code organization in a Catalyst application
+is known as the "Model View Controller" design pattern (also referred to
+"MVC". See L<http://en.wikipedia.org/wiki/Model-view-controller>). 
+
+The point of the MVC pattern is to separate the dependencies of parts of
+the application from each other, and give them standard
+interfaces. Following this theory of organization should give your code
+all the benefits of modularity. The main benefits are interchangeability
+of parts and reusability of code.
+
+Thus you could replace your flat-file data storage with a relational
+database, or your MySQL database with an Oracle database, and not have
+to change any of your Controller or View logic. Or you could later
+decide to output information from your application as RSS instead of
+HTML just by adding a new View module.
+
+=head3 Model
+
+Models deal with the storage of data. For a complex website, you may
+need multiple data sources, each having its own model class that
+provides an abstracted interface to it. In this tutorial we are going to
+be using a simple database.
+
+=head3 View
+
+Views are used to display information to the user. In a web framework,
+the View is generally used to output HTML to the browser. As mentioned
+previously, Views can also be used to output RSS or any other kind of
+data. One easy way to do this with Catalyst is to use a templating
+system such as L<Template Toolkit|Template>. If outputting HTML is all
+you are going to do, then you will probably only need one View.
+
+=head3 Controller
+
+A controller is responsible for responding to user choices, and thus
+controls what the application does. Since this is a web framework,
+Catalyst controllers are frequently used to react directly to URLs
+requested by the user. This tutorial will describe the simplest way of
+using controllers, where each path or part of a path is assigned its own
+action (or subroutine). More complex controlling mechanisms will be
+mentioned briefly, and can be read about in detail elsewhere in the
+manual.
+
+
+=head2 Controlling
+
+Now let's write our first bit of application code. First, we would like
+our application to greet our users. We'll assume for now that our users
+will be sent to the I<users/greet> URL. To create a controller that
+serves the I<users> namespace, we run the following command in our
+F<tutorial> directory:
+
+    script/tutorial_create.pl controller Users
+
+This will create a Users.pm in F<lib/tutorial/Controller>. Open this
+file in an editor and take a look. You will notice there is some
+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, $c) = @_;
+
+    my $name = $c->req->param('name');
+    $c->log->debug("Got name: $name\n");
+
+    if ($c->req->method eq 'POST') {
+       if(!$name) {
+            $c->stash->{message} = 'Please fill in a name!';
+        }
+        else {
+            $c->stash->{message} = "Hello $name!";     
+        }
+    }
+    $c->stash->{template} = 'greet.tt';
+  }
+
+Whew! So, what does all this do? Let's take it one step at a time. The
+subroutine declaration gives the action a name. To the right of the name
+there is an attribute type that looks like this:
+
+    : Local 
+    
+That defines which URIs will translate to this action. "Local" matches
+exactly one URI: 
+
+    /users/greet
+    
+The URI matched by "Local" is composed 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.
+
+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", held in C<$context>, or abbreviated to
+C<$c>.  From now on, whenever we are talking about the context object,
+it will be represented as C<$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 ->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,
+or the error log if running under CGI or mod_perl.
+
+Next, if we have a post request, we check if the name field contains
+anything (or is "true"). If it isn't, we assign an error message to a
+"message" field in the stash. The stash is a special hash of the context
+object, which allows us to pass data on to other methods we call later,
+typically in the View.
+
+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.
+
+=head2 Viewing
+
+OK, so reacting and checking the users data is all fine, but how do we
+actually display the page/form in the first place, and our results? In
+this tutorial, we'll use Template Toolkit for our viewing. To create our
+TT-based view, just run the following command:
+
+    script/tutorial_create.pl view TToolkit TT
+
+Notice that this time we not only gave it the type of module we wanted
+to create (a view), and a name, but also a third argument, "TT". This is
+a Catalyst helper module, which will make a standard Template Toolkit
+module for you. And that's all you need to do there.
+
+To use the view, the easiest way is to set up a standard "end" action.
+This is a special private action which will not be matched to a path
+like our "greet" action, but instead will be called after all other
+processing is done. Only one end action will be called in any request
+cycle. If there is one in a controller, it will be preferred over one in
+the application module, and 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, $c) = @_;
+        $c->forward('tutorial::View::TToolkit') unless $c->res->body();
+    }
+
+The first line declares the end sub, and marks it as a Private action.
+(This is the second and last attribute type we'll be using in this
+tutorial.) The second line collects our standard parameters as shown in
+the controller's greet action.
+
+The third line directs Catalyst to pass processing on to our TToolkit
+view. The forward method, when just passed a class name, calls
+C<process> on that class. The standard TT view's C<process> method
+renders the template named in the template variable in the stash, using
+all the other variables in the stash as values to fill it in.
+
+NB: This is such a common way to end your processing that there is a
+plugin which does it for you: L<Catalyst::Plugin::DefaultEnd>.
+
+Template Toolkit also has access to the entire context object via the
+special variable "c". For example, using C<[% 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" like below.
+
+    <html><head><title> [% c.config.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<[% 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
+
+As previously mentioned, the configuration of modules, plugins, and so
+on is done in the main application file. This is especially true for
+bits which need to be done before an instance of them is created, for
+example Template Toolkit.
+
+The TT View looks for its templates in the F<root> directory by default.
+Since this is also the directory that static files go in, we'd rather
+have a separate F<templates> directory. To do this, change the config
+call in F<tutorial.pm> like this:
+
+ __PACKAGE__->config( name => 'tutorial',
+                      'View::TToolkit' => {
+                          'INCLUDE_PATH' => __PACKAGE__->path_to('templates')
+     }
+  );
+
+And move the F<greet.tt> file from F<root> to the F<templates> directory
+(after creating it).
+
+Now we can run our application again by killing (B<ctrl-c>) and restarting
+B<script/tutorial_server.pl>. Try connecting to
+I<localhost:3000/users/greet> with a browser and see what happens. What
+happens if you try to visit I<localhost:3000/users> ? 
+
+=head2 Users and Authenticating
+
+One of the many reasons to write dynamic websites instead of just using
+static HTML is to allow us to produce different content for different
+users, as well as restricting access to pages (which we could do with
+just Apache's htpasswd system).
+
+In this tutorial, we will just be using basic authentication. When
+writing a real application, you'll want to use a database or other
+secure store to contain your user data.
+
+To add authentication, all we need to do is add the
+L<Catalyst::Plugin::Authentication> module to our main application
+file. Then we need to pick a storage method (one of the
+L<Catalyst::Plugin::Authentication::Store> modules), and a method of
+verifying the user's credentials (one of the
+L<Catalyst::Plugin::Authentication::Credential> modules). Edit
+F<tutorial.pm> to look like this:
+
+ use Catalyst qw/-Debug Static::Simple Authentication 
+                Authentication::Store::Minimal 
+                Authentication::Credential::Password/;
+
+To configure, add some users to the config call. For example:
+
+ authentication => { 'users' =>
+                       { 'fred' =>
+                           { 'password' => 'fred1234',
+                           }
+                       }
+                   }
+
+Generally, setting up configuration data for plugins is done based on
+the type of plugin. Check the documentation of the plugin for exact
+details; in this example we should look in
+L<Catalyst::Plugin::Authentication::Store::Minimal>.
+
+Since our user data is in the config, we can update it at runtime, and
+thus add users dynamically. (Of course, to keep them permanently we'd
+need to save our data to disk and read it back into the config on
+startup.)
+
+To allow creation of new users we'll add a C<create> action to our Users
+controller.
+
+ sub create : Local {
+    my ($self, $c) = @_;
+    my ($username, $passwd1, $passwd2) = map { $c->req->param($_)} 
+       ('username', 'password', 'passwordverify');
+
+    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) {
+            $c->config->{authentication}->{users}->{$username} =
+                {password => $passwd1};
+          $c->stash->{message} = 'User created!';
+       }
+       else {
+          $c->stash->{username} = $username;
+          $c->stash->{message} = 'Passwords do not match!';
+       }
+    }
+    $c->stash->{template} = 'usercreate.tt';
+ }
+
+All this is doing is checking that all the appropriate fields are
+filled, and that 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.
+
+The usercreate.tt template looks like this:
+
+ <html><head><title>[% c.config.name %]</title></head><body>
+ <h1>Create a new user</h1>
+ <h2>Current users are:</h2>
+ <p>
+    [% FOREACH key = c.config.authentication.users.keys %]
+        [% key %]<br/>
+    [% END %]
+ </p>
+ <p> [% 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>
+
+In order for our users to be able to login, we need a C<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> [% 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>
+
+
+Very 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.
+
+To keep the user logged in, all we need to do is add the Session modules
+to our collection, and the Auth modules will automatically use them:
+
+ use Catalyst qw/-Debug Static::Simple Authentication 
+                 Authentication::Store::Minimal 
+                 Authentication::Credential::Password
+                 Session Session::Store::File Session::State::Cookie/;
+
+Magic!
+
+=head2 Exercise
+
+As an exercise for the reader, do the following:
+
+Change C<users/greet> and C<greet.tt> so that the welcome message greets
+the user by name.
+
+Enforce user logging in by adding an C<auto> action in tutorial.pm (see
+the L<Catalyst> documentation to find out about the C<auto> action).
+
+=head2 Authorizing
+
+Authentication is about verifying users, and authorization is about
+allowing them to do things. Catalyst currently has two Authorization
+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
+L<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
+they belong to.
+
+ authentication => { 'users' =>
+                            { 'fred' => 
+                               { 'password' => 'fred1234',
+                                 '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<< $c->check_user_roles() >> in each action. So we can
+make a restricted I<http://localhost:3000/users/groups> page like this:
+
+ 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 = $c->req->params) {
+          my $users = $c->config->{authentication}{users};
+          foreach my $u (keys %$params) {
+             $users->{$u}{roles} = $params->{$u} if($users->{$u});
+          }
+          $c->stash->{message} = 'Updated user roles!';
+       }
+       else {
+           $c->stash->{users} = $c->config->{authentication};
+       }
+       $c->stash->{template} = 'usersgroups.tt';
+    }
+    else {
+        $c->stash->{message} = 'Admins Only!';
+        $c->stash->{template} = 'error.tt';
+    }
+ }
+
+What we are doing here is checking whether the logged-in user (used by
+default in the C<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>[% 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 create it from changeable data, we need to store the data
+somewhere. Generally this is done using a database, but models can also
+use other data sources, for example another website, or an RSS feed.
+
+If you have or want a database, there are still choices to be
+made. There are several modules for accessing databases using an
+object-oriented wrapper. The best known are probably L<DBIx::Class> and
+L<Class::DBI>. Catalyst supports making models using either of these.
+
+For a simple example, we will allow our users to store their favorite
+greeting in our database. Create a table called "greetings" in a
+database, that contains a "user" field and a "greeting" field. The
+simplest way to create a model of your database is to use these helper
+modules, for example with L<DBIx::Class>:
+
+    script/tutorial_create.pl model UserData DBIC dbi:SQLite:/path/to/mydb.db
+
+This will cause the DBIx::Class Loader to inspect your database, and create a
+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, so we'll make a I<http://localhost:3000/users/editgreeting> page:
+
+ 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 = $c->model('UserData::Greetings');
+          my $record = $grtable->find_or_create(user => $c->user->id);
+          $record->greeting($c->req->params->{greeting});
+          $record->update;
+          $c->stash->{message} = 'Greeting updated';
+       }
+    }
+    $c->stash->{template} = 'usersgreeting.tt';
+ }
+
+Using C<< $c->user_exists >> from the Authentication plugin, this checks
+whether the user is logged in already. If they are, and they have
+entered a new greeting, we use DBIx::Class' C<find_or_create> method to
+fetch or create a new record in the greetings table for the user. Once
+we have the record, we change the value of the greeting field, and call
+C<update> to store the new value in the database.
+
+=head2 Engines (Apache and FastCGI)
+
+Now that we have the basics together, we can try running our application on a
+"real" server instead of just using the test server that Catalyst comes
+with. L<Catalyst::Engine> is the module used to implement various types of
+servers to run it on. The current popular ones are Apache and FastCGI.
+
+=head3 Apache
+
+Apache 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: 
+
+ <Location />
+   SetHandler           perl-script
+   PerlResponseHandler  tutorial
+ </Location>
+
+You will need to install the perl modules of your application into one
+of perl's library directories, as listed by B<perl -V>, so that Apache
+can find them. Alternatively you can use the C<PerlSwitches> directive
+to tell Apache where to look:
+
+ PerlSwitches -I/path/to/tutorial/
+
+These instructions are for using Apache2 and mod_perl 2.0. If you are
+using mod_perl 1.3 or 1.99, please refer to either
+L<Catalyst::Engine::Apache::MP13> or L<Catalyst::Engine::Apache2::MP19>
+for details of the slightly different ways to do it.
+
+If you wish to ensure that Apache pre-loads your application, use the
+PerlModule directive. This means that there will be less of a delay when
+your application is accessed.
+
+ PerlModule tutorial
+
+=head3 FastCGI
+
+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<tutorial_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<tutorial> 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:
+
+  FastCgiServer /usr/apps/tutorial/script/tutorial_fastcgi.pl
+  Alias / /usr/apps/tutorial/script/tutorial_fastcgi.pl/
+
+If you want your app under C</theapp>, change the C<Alias> line to:
+
+  Alias /theapp /usr/apps/tutorial/script/tutorial_fastcgi.pl
+
+Note the detail of the trailing C</ >: this is a general rule of 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:
+
+  FastCgiConfig -autoUpdate
+
+  <Directory /usr/apps/tutorial/script>
+   Options +ExecCGI
+   <Files *_fastcgi.pl>
+    SetHandler fastcgi-script
+   </Files>
+  </Directory>
+
+  Alias / /usr/apps/tutorial/script/tutorial_fastcgi.pl/
+
+Again, if you want your app under C</theapp>, change the C<Alias> line to:
+
+  Alias /theapp /usr/apps/tutorial/script/tutorial_fastcgi.pl
+
+=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.
+
+If you want to use a UNIX socket (on the filesystem), put this in
+Apache's configuration:
+
+  FastCgiExternalServer /tmp/somewhere -socket /tmp/tutorial-socket
+  Alias / /tmp/somewhere/
+
+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:
+
+  Alias /theapp /tmp/somewhere
+
+Then start your Catalyst application:
+
+  $ cd /usr/apps/tutorial
+  $ ./script/tutorial_fastcgi -l /tmp/tutorial-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.
+
+=head2 Upgrading
+
+Upgrading your application to newer Catalyst versions is quite
+simple. After installing the new Catalyst package, just run:
+
+    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:
+
+    diff tutorial/lib/tutorial/View/TT.pm tutorial/lib/tutorial/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.)
+
+=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>
+Gavin Henry, C<ghenry@cpan.org>
+
+Please send comments, corrections and suggestions for improvements to
+jrobinson@cpan.org, ghenry@cpan.org
+
+=head1 TODO
+
+Finish DBIC examples with templates and tested code. Make /users/groups
+do something "useful".
+
+Many other things....
+
+=head1 COPYRIGHT
+
+This program is free software, you can redistribute it and/or modify 
+it under the same terms as Perl itself.
index bb974f6..d69d300 100644 (file)
 =head1 NAME
 
-Catalyst::Manual::Tutorial - Getting started with Catalyst
+Catalyst::Manual::Tutorial - Catalyst Tutorial: Overview
+
+
 
 =head1 DESCRIPTION
 
-This document aims to get you up and running with Catalyst.
+The Catalyst framework is a flexible and comprehensive environment for quickly building high-functionality web applications.  This tutorial is design to provide a rapid introduction to its basics and most commonly used features while focusing on real-world best practices.
+
+The tutorial is broken down into the following sections:
+
+=over 4
+
+=item *
+
+L<Introduction|Catalyst::Manual::Tutorial01_Intro>
+
+=item * 
+
+L<Catalyst Basics|Catalyst::Manual::Tutorial02_CatalystBasics>
+
+=item * 
+
+L<CRUD|Catalyst::Manual::Tutorial03_BasicCRUD>
+
+=item * 
+
+L<Authentication|Catalyst::Manual::Tutorial04_Authentication>
+
+=item * 
+
+L<Authorization|Catalyst::Manual::Tutorial05_Authorization>
+
+=item * 
+
+L<Debugging|Catalyst::Manual::Tutorial06_Debugging>
+
+=item * 
+
+L<Testing|Catalyst::Manual::Tutorial07_Testing>
+
+=item * 
+
+L<CRUD|Catalyst::Manual::Tutorial08_AdvancedCRUD>
+
+=item * 
+
+L<Appendicies|Catalyst::Manual::Tutorial09_Appendicies>
+
+=back
+
+
+Full source code is available for each section via the main Catalyst Subversion repository at L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/>.  See the end of each section for the specifics on retrieving code for that section.
+
+A tarball of the final application is available at C<to_be_compled_in_final_version>.
+
+
+
+=head1 Detailed Table Of Contents
+
+=head2 Part 1: Introduction
+
+=over 4
+
+=item *
+
+VERSIONS AND CONVENTIONS USED IN THIS TUTORIAL
+
+=item *
+
+CATALYST INSTALLATION
+
+=item *
+
+DATABASES
+
+=item *
+
+WHERE TO GET WORKING CODE
+
+=back
+
+
+
+=head2 Part 2: Catalyst Application Development Basics
+
+=over 4
+
+=item *
+
+CREATE A CATALYST PROJECT
+
+=item *
+
+CREATE A SQLITE DATABASE
+
+=item *
+
+EDIT THE LIST OF CATALYST PLUGINS
+
+=item *
+
+DATABASE ACCESS WITH DBIx::Class
+
+=over 4
+
+=item *
+
+Create a DBIC Schema File
+
+=item *
+
+Create the DBIC ``Result Source'' Files
+
+=item *
+
+Use Catalyst::Model::DBIC::Schema To Load The Model Class
+
+=back
+
+=item *
+
+CREATE A CATALYST CONTROLLER
+
+=item *
+
+CATALYST VIEWS
+
+=over 4
+
+=item *
+
+Create a Catalyst View Using TTSITE
+
+=item *
+
+Globally Customize Every View
+
+=item *
+
+Create a TT Template Page
+
+=back
+
+=item *
+
+RUN THE APPLICATION
+
+=back
+
+
+
+=head2 Part 3: Basic CRUD
+
+=over 4
+
+=item *
+
+FORMLESS SUBMISSION
+
+=over 4
+
+=item *
+
+Include a Create Action in the Books Controller
+
+=item *
+
+Include a Template for the url_create Action:
+
+=item *
+
+Try the url_create Feature
+
+=back
+
+=item *
+
+MANUALLY BUILDING A CREATE FORM
+
+=over 4
+
+=item *
+
+Add Method to Display The Form
+
+=item *
+
+Add a Template for the Form
+
+=item *
+
+Add Method to Process Form Values and Update Database
+
+=item *
+
+Test Out The Form
+
+=back
+
+=item *
+
+A SIMPLE DELETE FEATURE
+
+=over 4
+
+=item *
+
+Include a Delete Link in the List
+
+=item *
+
+Add a Delete Action to the Controller
+
+=item *
+
+Try the Delete Feature
+
+=back
+
+=back
+
+
+
+=head2 Part 4: Authentication
+
+=over 4
+
+=item *
+
+BASIC AUTHENTICATION
+
+=over 4
+
+=item *
+
+Add Users and Roles to the Database
+
+=item *
+
+Add User and Role Information to Dbic Schema
+
+=item *
+
+Create New ``Result Source Objects''
+
+=item *
+
+Sanity-Check Reload of Development Server
+
+=item *
+
+Include Authentication and Session Plugins
+
+=item *
+
+Configure Authentication
+
+=item *
+
+Add Login and Logout Controllers
+
+=item *
+
+Add a Login Form TT Template Page
+
+=item *
+
+Add Valid User Check
+
+=item *
+
+Displaying Content Only to Authenticated Users
+
+=item *
+
+Try Out Authentication
+
+=back
+
+=item *
+
+USING PASSWORD HASHES
+
+=over 4
+
+=item *
+
+Get a SHA-1 Hash for the Password
+
+=item *
+
+Switch to SHA-1 Password Hashes in the Database
+
+=item *
+
+Enable SHA-1 Hash Passwords in Catalyst::Plugin::Authentication::Store::DBIC
+
+=item *
+
+Try Out the Hashed Passwords
+
+=back
+
+=back
+
+
+
+=head2 Part 5: Authorization
+
+=over 4
+
+=item *
 
-=head2 Installation
+BASIC AUTHORIZATION
 
-The first step is to install Catalyst, and the simplest way to do this 
-is to install the Catalyst bundle from CPAN:
+=over 4
 
-    $ perl -MCPAN -e 'install Task::Catalyst'
+=item *
 
-This will retrieve Catalyst and a number of useful extensions and 
-install them for you. This process might not be totally painless
-though, and you might want to look at CatInABox at
-L<http://use.perl.org/~jk2addict/journal/28071>, especially if you are
-on a system that lacks a compiler.
+Update Plugins to Include Support Authorization
 
+=item *
 
-=head2 The very basics - Setting up the skeleton application.
+Add Config Information for Authorization
 
-Catalyst includes a helper script, C<catalyst.pl>, that will set up a 
-skeleton application for you:
+=item *
 
-    $ catalyst.pl tutorial
+Add Role-Specific Logic to the ``Book List'' Template
 
-    created "tutorial"
-    created "tutorial/script"
-    ... output snipped
-    created "tutorial/script/tutorial_create.pl"
+=item *
 
-This creates the directory structure, populated with skeleton 
-files.
+Limit Books::add to admin Users
 
-=head2 Testing out the skeleton application
+=item *
 
-You can test out your new application by running the server script that
-Catalyst provides:
+Try Out Authentication And Authorization
 
-    $ cd tutorial
-    $ script/tutorial_server.pl 
+=back
 
-    [debug] Debug messages enabled
-    [debug] Loaded plugins:
-    .------------------------------------------------------------------------------.
-    | Catalyst::Plugin::Static::Simple                                             |
-    '------------------------------------------------------------------------------'
-    [debug] Loaded dispatcher "Catalyst::Dispatcher"
-    [debug] Loaded engine "Catalyst::Engine::HTTP"
-    [debug] Found home "/home/users/me/tutorial"
-    [debug] Loaded Private actions:
-    .--------------------------------------+---------------------------------------.
-    | Private                              | Class                                 |
-    +--------------------------------------+---------------------------------------+
-    | /default                             | tutorial                              |
-    '--------------------------------------+---------------------------------------'
+=item *
 
-    [...] [catalyst] [info] tutorial powered by Catalyst 5.67
-    You can connect to your server at http://localhost:3000
+ENABLE ACL-BASED AUTHORIZATION
 
-(Note that each line logged by Catalyst begins with a timestamp, which has
-been replaced here with "C<...>" so that the text fits onto the lines.)
+=over 4
 
-The server is now waiting for you to make requests of it.  Try using 
-telnet to manually make a simple GET request of the server (when 
-telnet responds with "Escape character is '^]'.", type "GET / HTTP/1.0"
-and hit return twice):
+=item *
 
-    $ telnet localhost 3000
-    Trying 127.0.0.1...
-    Connected to localhost.
-    Escape character is '^]'.
-    GET / HTTP/1.0
+Add the Catalyst::Plugin::Authorization::ACL Plugin
 
-    HTTP/1.0 200 OK
-    Date: Mon, 07 Nov 2005 14:57:39 GMT
-    Content-Length: 5525
-    Content-Type: text/html; charset=utf-8
-    Status: 200
-    X-Catalyst: 5.67
+=item *
 
-    [...]
-    Connection closed by foreign host.
-    $
+Add ACL Rules to the Application Class
 
-You can see the full welcome message by visiting
-http://localhost:3000/ with your browser.
+=item *
 
-More trace messages will appear in the original terminal window:
+Add a Method to Handle Access Violations
 
-    [debug] *** Request 1 (0.063/s) [2148]
-    [debug] Arguments are ""
-    [debug] "GET" request for "/" from localhost
-    [info] Request took 0.046883s (21.330/s)
-    .------------------------------------------------------------------+-----------.
-    | Action                                                           | Time      |
-    +------------------------------------------------------------------+-----------+
-    | /default                                                         | 0.000000s |
-    '------------------------------------------------------------------+-----------'
+=back
 
-The server will continue running until you interrupt it.
+=back
 
-The application can also be tested from the command line using the generated
-helper script, C<script/tutorial_test.pl>.
 
-=head2 Getting started
+=head2 Part 6: Debugging
 
-So you picked Catalyst. Good choice. We assume you have installed it as
-well. For this tutorial you will also need the following modules:
+=over 4
 
-L<Catalyst::Plugin::Session>
+=item *
 
-L<Catalyst::Plugin::Session::Store::File>
+LOG STATEMENTS
 
-L<Catalyst::Plugin::Session::State::Cookie>
+=item *
 
-L<Catalyst::Plugin::Authentication>
+RUNNING CATALYST UNDER THE PERL DEBUGGER
 
-L<Catalyst::Plugin::Authentication::Store::Minimal>
+=back
 
-L<Catalyst::Plugin::Authentication::::Minimal>
 
-L<Catalyst::Plugin::Authentication::Credential::Password>
 
-L<Catalyst::Plugin::Authorization::Roles>
+=head2 Part 7: Testing
 
-L<DBD::SQLite>
+=over 4
 
-...
+=item *
 
-If you have not already done this following the example above, then to get 
-started all you need to do is type:
+RUNNING THE "CANNED" CATALYST TESTS
 
-    catalyst.pl tutorial
+=item *
 
-=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.
+RUNNING A SINGLE TEST
 
-=cut
+=item *
 
-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
-F<tutorial> directory.
+ADDING YOUR OWN TEST SCRIPT
 
-If you now run the built-in mini-server with:
-    
-    script/tutorial_server.pl
+=item *
 
-it will show some standard debug messages in the console screen 
-(more about those in a minute), and then inform you that you can now 
-connect to the test server on port 3000. Point your browser at 
-http://localhost:3000/ to see the built-in Catalyst welcome screen.
+SUPPORTING BOTH PRODUCTION AND TEST DATABASES
 
-The other important thing B<catalyst.pl> did was create your root
-controller. This file is a standard Perl module like all the other
-controllers that you might add to your application. It lives in the
-F<lib/> directory, and will have the same name as you supplied to the
-command above. In our case it is F<tutorial.pm>. Alongside this file is
-a directory of the same name, which is the top level namespace for the
-entire application. Thus every other module we create will be
-"tutorial::something".
+=back
 
-The root controller is used to load plugins, to configure the
-application and its plugins, and for generic private actions.  We will
-explain more about those later.
 
-=head2 Debugging
 
-The simplest way to debug your Catalyst application is to run it using
-the built-in mini-server as described in L<Getting started>. 
+=head2 Part 8: Advanced CRUD
 
-If you want to output any debugging information to the console, then
-call C<< $c->log->debug() >>, passing it a string to output. For data
-structures, C<use> L<Data::Dump> qw/dump/; and call 
-C<< $c->log->debug(dump($structure)) >>
+=over 4
 
-=head2 Model/View/Controller
+=item *
 
-The recommended method for code organization in a Catalyst application
-is known as the "Model View Controller" design pattern (also referred to
-"MVC". See L<http://en.wikipedia.org/wiki/Model-view-controller>). 
+HTML::WIDGET FORM CREATION
 
-The point of the MVC pattern is to separate the dependencies of parts of
-the application from each other, and give them standard
-interfaces. Following this theory of organization should give your code
-all the benefits of modularity. The main benefits are interchangeability
-of parts and reusability of code.
+=over 4
 
-Thus you could replace your flat-file data storage with a relational
-database, or your MySQL database with an Oracle database, and not have
-to change any of your Controller or View logic. Or you could later
-decide to output information from your application as RSS instead of
-HTML just by adding a new View module.
+=item *
 
-=head3 Model
+Add the HTML::Widget Plugin
 
-Models deal with the storage of data. For a complex website, you may
-need multiple data sources, each having its own model class that
-provides an abstracted interface to it. In this tutorial we are going to
-be using a simple database.
+=item *
 
-=head3 View
+Add a Form Creation Helper Method
 
-Views are used to display information to the user. In a web framework,
-the View is generally used to output HTML to the browser. As mentioned
-previously, Views can also be used to output RSS or any other kind of
-data. One easy way to do this with Catalyst is to use a templating
-system such as L<Template Toolkit|Template>. If outputting HTML is all
-you are going to do, then you will probably only need one View.
+=item *
 
-=head3 Controller
+Add Actions to Display and Save the Form
 
-A controller is responsible for responding to user choices, and thus
-controls what the application does. Since this is a web framework,
-Catalyst controllers are frequently used to react directly to URLs
-requested by the user. This tutorial will describe the simplest way of
-using controllers, where each path or part of a path is assigned its own
-action (or subroutine). More complex controlling mechanisms will be
-mentioned briefly, and can be read about in detail elsewhere in the
-manual.
-
-
-=head2 Controlling
-
-Now let's write our first bit of application code. First, we would like
-our application to greet our users. We'll assume for now that our users
-will be sent to the I<users/greet> URL. To create a controller that
-serves the I<users> namespace, we run the following command in our
-F<tutorial> directory:
-
-    script/tutorial_create.pl controller Users
-
-This will create a Users.pm in F<lib/tutorial/Controller>. Open this
-file in an editor and take a look. You will notice there is some
-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, $c) = @_;
-
-    my $name = $c->req->param('name');
-    $c->log->debug("Got name: $name\n");
-
-    if ($c->req->method eq 'POST') {
-       if(!$name) {
-            $c->stash->{message} = 'Please fill in a name!';
-        }
-        else {
-            $c->stash->{message} = "Hello $name!";     
-        }
-    }
-    $c->stash->{template} = 'greet.tt';
-  }
-
-Whew! So, what does all this do? Let's take it one step at a time. The
-subroutine declaration gives the action a name. To the right of the name
-there is an attribute type that looks like this:
-
-    : Local 
-    
-That defines which URIs will translate to this action. "Local" matches
-exactly one URI: 
-
-    /users/greet
-    
-The URI matched by "Local" is composed 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.
-
-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", held in C<$context>, or abbreviated to
-C<$c>.  From now on, whenever we are talking about the context object,
-it will be represented as C<$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 ->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,
-or the error log if running under CGI or mod_perl.
-
-Next, if we have a post request, we check if the name field contains
-anything (or is "true"). If it isn't, we assign an error message to a
-"message" field in the stash. The stash is a special hash of the context
-object, which allows us to pass data on to other methods we call later,
-typically in the View.
-
-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.
-
-=head2 Viewing
-
-OK, so reacting and checking the users data is all fine, but how do we
-actually display the page/form in the first place, and our results? In
-this tutorial, we'll use Template Toolkit for our viewing. To create our
-TT-based view, just run the following command:
-
-    script/tutorial_create.pl view TToolkit TT
-
-Notice that this time we not only gave it the type of module we wanted
-to create (a view), and a name, but also a third argument, "TT". This is
-a Catalyst helper module, which will make a standard Template Toolkit
-module for you. And that's all you need to do there.
-
-To use the view, the easiest way is to set up a standard "end" action.
-This is a special private action which will not be matched to a path
-like our "greet" action, but instead will be called after all other
-processing is done. Only one end action will be called in any request
-cycle. If there is one in a controller, it will be preferred over one in
-the application module, and 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, $c) = @_;
-        $c->forward('tutorial::View::TToolkit') unless $c->res->body();
-    }
-
-The first line declares the end sub, and marks it as a Private action.
-(This is the second and last attribute type we'll be using in this
-tutorial.) The second line collects our standard parameters as shown in
-the controller's greet action.
-
-The third line directs Catalyst to pass processing on to our TToolkit
-view. The forward method, when just passed a class name, calls
-C<process> on that class. The standard TT view's C<process> method
-renders the template named in the template variable in the stash, using
-all the other variables in the stash as values to fill it in.
-
-NB: This is such a common way to end your processing that there is a
-plugin which does it for you: L<Catalyst::Plugin::DefaultEnd>.
-
-Template Toolkit also has access to the entire context object via the
-special variable "c". For example, using C<[% 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" like below.
-
-    <html><head><title> [% c.config.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<[% 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
-
-As previously mentioned, the configuration of modules, plugins, and so
-on is done in the main application file. This is especially true for
-bits which need to be done before an instance of them is created, for
-example Template Toolkit.
-
-The TT View looks for its templates in the F<root> directory by default.
-Since this is also the directory that static files go in, we'd rather
-have a separate F<templates> directory. To do this, change the config
-call in F<tutorial.pm> like this:
-
- __PACKAGE__->config( name => 'tutorial',
-                      'View::TToolkit' => {
-                          'INCLUDE_PATH' => __PACKAGE__->path_to('templates')
-     }
-  );
-
-And move the F<greet.tt> file from F<root> to the F<templates> directory
-(after creating it).
-
-Now we can run our application again by killing (B<ctrl-c>) and restarting
-B<script/tutorial_server.pl>. Try connecting to
-I<localhost:3000/users/greet> with a browser and see what happens. What
-happens if you try to visit I<localhost:3000/users> ? 
-
-=head2 Users and Authenticating
-
-One of the many reasons to write dynamic websites instead of just using
-static HTML is to allow us to produce different content for different
-users, as well as restricting access to pages (which we could do with
-just Apache's htpasswd system).
-
-In this tutorial, we will just be using basic authentication. When
-writing a real application, you'll want to use a database or other
-secure store to contain your user data.
-
-To add authentication, all we need to do is add the
-L<Catalyst::Plugin::Authentication> module to our main application
-file. Then we need to pick a storage method (one of the
-L<Catalyst::Plugin::Authentication::Store> modules), and a method of
-verifying the user's credentials (one of the
-L<Catalyst::Plugin::Authentication::Credential> modules). Edit
-F<tutorial.pm> to look like this:
-
- use Catalyst qw/-Debug Static::Simple Authentication 
-                Authentication::Store::Minimal 
-                Authentication::Credential::Password/;
-
-To configure, add some users to the config call. For example:
-
- authentication => { 'users' =>
-                       { 'fred' =>
-                           { 'password' => 'fred1234',
-                           }
-                       }
-                   }
-
-Generally, setting up configuration data for plugins is done based on
-the type of plugin. Check the documentation of the plugin for exact
-details; in this example we should look in
-L<Catalyst::Plugin::Authentication::Store::Minimal>.
-
-Since our user data is in the config, we can update it at runtime, and
-thus add users dynamically. (Of course, to keep them permanently we'd
-need to save our data to disk and read it back into the config on
-startup.)
-
-To allow creation of new users we'll add a C<create> action to our Users
-controller.
-
- sub create : Local {
-    my ($self, $c) = @_;
-    my ($username, $passwd1, $passwd2) = map { $c->req->param($_)} 
-       ('username', 'password', 'passwordverify');
-
-    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) {
-            $c->config->{authentication}->{users}->{$username} =
-                {password => $passwd1};
-          $c->stash->{message} = 'User created!';
-       }
-       else {
-          $c->stash->{username} = $username;
-          $c->stash->{message} = 'Passwords do not match!';
-       }
-    }
-    $c->stash->{template} = 'usercreate.tt';
- }
-
-All this is doing is checking that all the appropriate fields are
-filled, and that 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.
-
-The usercreate.tt template looks like this:
-
- <html><head><title>[% c.config.name %]</title></head><body>
- <h1>Create a new user</h1>
- <h2>Current users are:</h2>
- <p>
-    [% FOREACH key = c.config.authentication.users.keys %]
-        [% key %]<br/>
-    [% END %]
- </p>
- <p> [% 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>
-
-In order for our users to be able to login, we need a C<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> [% 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>
-
-
-Very 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.
-
-To keep the user logged in, all we need to do is add the Session modules
-to our collection, and the Auth modules will automatically use them:
-
- use Catalyst qw/-Debug Static::Simple Authentication 
-                 Authentication::Store::Minimal 
-                 Authentication::Credential::Password
-                 Session Session::Store::File Session::State::Cookie/;
-
-Magic!
-
-=head2 Exercise
-
-As an exercise for the reader, do the following:
-
-Change C<users/greet> and C<greet.tt> so that the welcome message greets
-the user by name.
-
-Enforce user logging in by adding an C<auto> action in tutorial.pm (see
-the L<Catalyst> documentation to find out about the C<auto> action).
-
-=head2 Authorizing
-
-Authentication is about verifying users, and authorization is about
-allowing them to do things. Catalyst currently has two Authorization
-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
-L<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
-they belong to.
-
- authentication => { 'users' =>
-                            { 'fred' => 
-                               { 'password' => 'fred1234',
-                                 '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<< $c->check_user_roles() >> in each action. So we can
-make a restricted I<http://localhost:3000/users/groups> page like this:
-
- 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 = $c->req->params) {
-          my $users = $c->config->{authentication}{users};
-          foreach my $u (keys %$params) {
-             $users->{$u}{roles} = $params->{$u} if($users->{$u});
-          }
-          $c->stash->{message} = 'Updated user roles!';
-       }
-       else {
-           $c->stash->{users} = $c->config->{authentication};
-       }
-       $c->stash->{template} = 'usersgroups.tt';
-    }
-    else {
-        $c->stash->{message} = 'Admins Only!';
-        $c->stash->{template} = 'error.tt';
-    }
- }
-
-What we are doing here is checking whether the logged-in user (used by
-default in the C<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>[% 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 create it from changeable data, we need to store the data
-somewhere. Generally this is done using a database, but models can also
-use other data sources, for example another website, or an RSS feed.
-
-If you have or want a database, there are still choices to be
-made. There are several modules for accessing databases using an
-object-oriented wrapper. The best known are probably L<DBIx::Class> and
-L<Class::DBI>. Catalyst supports making models using either of these.
-
-For a simple example, we will allow our users to store their favorite
-greeting in our database. Create a table called "greetings" in a
-database, that contains a "user" field and a "greeting" field. The
-simplest way to create a model of your database is to use these helper
-modules, for example with L<DBIx::Class>:
-
-    script/tutorial_create.pl model UserData DBIC dbi:SQLite:/path/to/mydb.db
-
-This will cause the DBIx::Class Loader to inspect your database, and create a
-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, so we'll make a I<http://localhost:3000/users/editgreeting> page:
-
- 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 = $c->model('UserData::Greetings');
-          my $record = $grtable->find_or_create(user => $c->user->id);
-          $record->greeting($c->req->params->{greeting});
-          $record->update;
-          $c->stash->{message} = 'Greeting updated';
-       }
-    }
-    $c->stash->{template} = 'usersgreeting.tt';
- }
-
-Using C<< $c->user_exists >> from the Authentication plugin, this checks
-whether the user is logged in already. If they are, and they have
-entered a new greeting, we use DBIx::Class' C<find_or_create> method to
-fetch or create a new record in the greetings table for the user. Once
-we have the record, we change the value of the greeting field, and call
-C<update> to store the new value in the database.
-
-=head2 Engines (Apache and FastCGI)
-
-Now that we have the basics together, we can try running our application on a
-"real" server instead of just using the test server that Catalyst comes
-with. L<Catalyst::Engine> is the module used to implement various types of
-servers to run it on. The current popular ones are Apache and FastCGI.
+=item *
 
-=head3 Apache
+Update the CSS
 
-Apache 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: 
+=item *
 
- <Location />
-   SetHandler           perl-script
-   PerlResponseHandler  tutorial
- </Location>
+Create a Template Page To Display The Form
 
-You will need to install the perl modules of your application into one
-of perl's library directories, as listed by B<perl -V>, so that Apache
-can find them. Alternatively you can use the C<PerlSwitches> directive
-to tell Apache where to look:
+=item *
 
- PerlSwitches -I/path/to/tutorial/
+Add Links for Create and Update via HTML::Widget
 
-These instructions are for using Apache2 and mod_perl 2.0. If you are
-using mod_perl 1.3 or 1.99, please refer to either
-L<Catalyst::Engine::Apache::MP13> or L<Catalyst::Engine::Apache2::MP19>
-for details of the slightly different ways to do it.
+=item *
 
-If you wish to ensure that Apache pre-loads your application, use the
-PerlModule directive. This means that there will be less of a delay when
-your application is accessed.
+Test The <HTML::Widget> Create Form
 
- PerlModule tutorial
+=back
 
-=head3 FastCGI
+=item *
 
-These instructions apply to the use of C<mod_fastcgi> under Apache
-(either 1 or 2 series).
+HTML::WIDGET VALIDATION AND FILTERING
 
-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<tutorial_fastcgi.pl> substring in the user-visible URLs.
+=over 4
 
-In all of these examples, we assume that the C<DocumentRoot> is
-C</var>, that our app is called C<tutorial> 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).
+=item *
 
-=head4 static application
+Add Constraints and Filters to the Widget Creation Method
 
-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:
+=item *
 
-  FastCgiServer /usr/apps/tutorial/script/tutorial_fastcgi.pl
-  Alias / /usr/apps/tutorial/script/tutorial_fastcgi.pl/
+Rebuild the Form Submission Method to Include Validation
 
-If you want your app under C</theapp>, change the C<Alias> line to:
+=item *
 
-  Alias /theapp /usr/apps/tutorial/script/tutorial_fastcgi.pl
+Try Out the Form
 
-Note the detail of the trailing C</ >: this is a general rule of 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.
+=back
 
-=head4 dynamic application
+=item *
 
-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:
+Enable DBIx::Class::HTMLWidget Support
 
-  FastCgiConfig -autoUpdate
+=over 4
 
-  <Directory /usr/apps/tutorial/script>
-   Options +ExecCGI
-   <Files *_fastcgi.pl>
-    SetHandler fastcgi-script
-   </Files>
-  </Directory>
+=item *
 
-  Alias / /usr/apps/tutorial/script/tutorial_fastcgi.pl/
+Add DBIx::Class::HTMLWidget to DBIC Model
 
-Again, if you want your app under C</theapp>, change the C<Alias> line to:
+=item *
 
-  Alias /theapp /usr/apps/tutorial/script/tutorial_fastcgi.pl
+Use populate_from_widget in hw_create_do
 
-=head4 external server
+=back
 
-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.
+=back
 
-If you want to use a UNIX socket (on the filesystem), put this in
-Apache's configuration:
 
-  FastCgiExternalServer /tmp/somewhere -socket /tmp/tutorial-socket
-  Alias / /tmp/somewhere/
 
-Note that C</tmp> should I<not> exist: it's just a name to connect the
-two parts.
+=head2 Part 9: Appendices
 
-Again, if you want your app under C</theapp>, change the C<Alias> line
-to:
+=over 4
 
-  Alias /theapp /tmp/somewhere
+=item *
 
-Then start your Catalyst application:
+APPENDIX 1: CUT AND PASTE FOR POD-BASED EXAMPLES
 
-  $ cd /usr/apps/tutorial
-  $ ./script/tutorial_fastcgi -l /tmp/tutorial-socket
+=over 4
 
-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.
+=item *
 
-=head2 Upgrading
+"Un-indenting" with Vi/Vim
 
-Upgrading your application to newer Catalyst versions is quite
-simple. After installing the new Catalyst package, just run:
+=item *
 
-    catalyst.pl -scripts
+"Un-indenting" with Emacs
 
-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:
+=back
 
-    diff tutorial/lib/tutorial/View/TT.pm tutorial/lib/tutorial/View/TT.pm.new
+=item *
 
-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.)
+APPENDIX 2: USING MYSQL AND POSTGRESQL
 
-=head1 AUTHORS
+=over 4
 
-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>
-Gavin Henry, C<ghenry@cpan.org>
+=item *
 
-Please send comments, corrections and suggestions for improvements to
-jrobinson@cpan.org, ghenry@cpan.org
+MySQL
 
-=head1 TODO
+=item *
 
-Finish DBIC examples with templates and tested code. Make /users/groups
-do something "useful".
+PostgreSQL
 
-Many other things....
+=back
 
-=head1 COPYRIGHT
+=back
 
-This program is free software, you can redistribute it and/or modify 
-it under the same terms as Perl itself.
diff --git a/lib/Catalyst/Manual/Tutorial/AdvancedCRUD.pod b/lib/Catalyst/Manual/Tutorial/AdvancedCRUD.pod
new file mode 100644 (file)
index 0000000..ba003ae
--- /dev/null
@@ -0,0 +1,486 @@
+=head1 NAME
+
+Catalyst::Manual::Tutorial::AdvancedCRUD - Catalyst Tutorial – Part 8: Advanced CRUD
+
+
+
+=head1 OVERVIEW
+
+This is B<Part 8 of 9> for the Catalyst tutorial.
+
+L<Totorial Overview|Catalyst::Manual::Tutorial>
+
+=over 4
+
+=item 1
+
+L<Introduction|Catalyst::Manual::Tutorial::Intro>
+
+=item 2
+
+L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
+
+=item 3
+
+L<Basic CRUD|Catalyst::Manual::Tutorial03_BasicCRUD>
+
+=item 4
+
+L<Authentication|Catalyst::Manual::Tutorial::Authentication>
+
+=item 5
+
+L<Authorization|Catalyst::Manual::Tutorial::Authorization>
+
+=item 6
+
+L<Debugging|Catalyst::Manual::Tutorial::Debugging>
+
+=item 7
+
+L<Testing|Catalyst::Manual::Tutorial::Testing>
+
+=item 8
+
+B<AdvancedCRUD>
+
+=item 9
+
+L<Appendicies|Catalyst::Manual::Tutorial::Appendicies>
+
+=back
+
+
+
+=head1 DESCRIPTION
+
+This part of the tutorial explores more advanced functionality for Create, Read, Update, and Delete (CRUD) than we saw in Part 3.  In particular, it looks at a number of techniques that can be useful for the Update portion of CRUD, such as automated form generation, validation of user-entered data, and automated transfer of data between forms and model objects.
+
+In keeping with the Catalyst (and Perl) spirit of flexibility, there are many different ways approach advanced CRUD operations in a Catalyst environment.  One alternative is to use L<Catalyst::Helper::Controller::Scaffold|Catalyst::Helper::Controller::Scaffold> to instantly construct a set of Controller methods and templates for basic CRUD operations.  Although a popular subject in Quicktime movies that serve as promotional material for various frameworks, more real-world applications require more control.  Other options include L<Data::FormValidator|Data::FormValidator> and L<HTML::FillInForm|HTML::FillInForm>.
+
+Here, we will make use of the L<HTML::Widget|HTML::Widget> to not only ease form creation, but to also provide validation of the submitted data.  The approached used by the part of the tutorial is to slowly incorporate additional L<HTML::Widget|HTML::Widget> functionality in a step-wise fashion (we start with fairly simple form creation and then move on to more complex and "magical" features such as validation and auto-population/auto-saving).
+
+B<Note:> Part 8 of the tutorial is optional.  Users who do not which to use L<HTML::Widget|HTML::Widget> may skip this section.
+
+B<TIP>: Note that all of the code for this part of the tutorial can be pulled from the Catalyst Subversion repository in one step with the following command:
+
+    svn checkout http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/Tutorial@###
+    IMPORTANT: Does not work yet.  Will be completed for final version.
+
+
+
+=head1 C<HTML::WIDGET> FORM CREATION
+
+This section looks at how L<HTML::Widget|HTML::Widget> can be used to add additional functionality to the manually created form from Part 3.
+
+
+=head2 Add the C<HTML::Widget> Plugin
+
+Open C<lib/MyApp.pm> in your editor and add the following to the list of plugins (be sure to leave the existing plugins enabled:
+
+    HTML::Widget
+
+
+=head2 Add a Form Creation Helper Method
+
+Open C<lib/MyApp/Controller/Books.pm> in your editor and add the following method:
+
+    =head2 make_book_widget
+    
+    Build an HTML::Widget form for book creation and updates
+    
+    =cut
+    
+    sub make_book_widget {
+        my ($self, $c) = @_;
+
+        # Create an HTML::Widget to build the form
+        my $w = $c->widget('book_form')->method('post');
+    
+        # Get authors
+        my @authorObjs = $c->model("MyAppDB::Author")->all();
+        my @authors = map {$_->id => $_->last_name }
+                           sort {$a->last_name cmp $b->last_name} @authorObjs;
+    
+        # Create the form feilds
+        $w->element('Textfield', 'title'  )->label('Title')->size(60);
+        $w->element('Textfield', 'rating' )->label('Rating')->size(1);
+        $w->element('Select',    'authors')->label('Authors')
+            ->options(@authors);
+        $w->element('Submit',    'submit' )->value('submit');
+
+        # Return the widget    
+        return $w;
+    }
+
+This method provides a central location (so it can be called by multiple actions, such as create and edit) that builds an HTML::Wiget-based form with the appropriate fields.  The "Get Authors" code uses DBIC to retrieve a list of model objects and then uses C<map> to quickly create a hash where the hash keys are the database primary keys from the authors table and the associated values are the last names of the authors.
+
+
+=head2 Add Actions to Display and Save the Form
+
+Open C<lib/MyApp/Controller/Books.pm> in your editor and add the following methods:
+
+    =head2 hw_create
+    
+    Build an HTML::Widget form for book creation and updates
+    
+    =cut
+    
+    sub hw_create : Local {
+        my ($self, $c) = @_;
+    
+        # Create the widget and set the action for the form
+        my $w = $self->make_book_widget($c);
+        $w->action($c->uri_for('hw_create_do'));
+    
+        # Write form to stash variable for use in template
+        $c->stash->{widget_result} = $w->result;
+    
+        # Set the template
+        $c->stash->{template} = 'books/hw_form.tt2';
+    }
+    
+    
+    =head2 hw_create_do
+    
+    Build an HTML::Widget form for book creation and updates
+    
+    =cut
+    
+    sub hw_create_do : Local {
+        my ($self, $c) = @_;
+    
+        # Retrieve the data from the form
+        my $title   = $c->request->params->{title};
+        my $rating  = $c->request->params->{rating};
+        my $authors = $c->request->params->{authors};
+    
+        # Call create() on the book model object. Pass the table 
+        # columns/field values we want to set as hash values
+        my $book = $c->model('MyAppDB::Book')->create({
+                title   => $title,
+                rating  => $rating
+            });
+        
+        # Add a record to the join table for this book, mapping to 
+        # appropriate author
+        $book->add_to_book_authors({author_id => $authors});
+        
+        # Set a status message for the user
+        $c->stash->{status_msg} = 'Book created';
+    
+        # Use 'hw_create' to redisplay the form
+        $c->detach('hw_create');
+    
+    }
+
+Note how we use C<make_book_widget> to build the core parts of the form in one location, but we set the action (the URL the form is sent to when the user clicks the 'Submit' button) separately in C<hw_create>.  Doing so allows us to have the same form submit the data to different actions (e.g., C<hw_create_do> for a create operation but C<hw_update_do> to update an existing book object).
+
+
+=head2 Update the CSS
+
+Edit C<root/src/ttsite.css> and add the following lines to the bottom of the file:
+
+    label {
+        display: block;
+        width: 10em;
+        position: relative;
+        margin: .5em 0em;
+    }
+    label input {
+        position: absolute;
+        left: 100%;
+    }
+    label select {
+        position: absolute;
+        left: 100%;
+    }
+    .submit {
+        margin-top: 2em;;
+    }
+    .error_messages {
+        color: [% site.col.error %];
+    }
+
+These changes will display form elements vertically and also show error messages in red.  Note that we are pulling the color scheme settings from the C<root/lib/config/col> file that was created by the TTSite helper.  This allows us to change the color used by various error styles in the CCS from a single location.
+
+
+=head2 Create a Template Page To Display The Form
+
+C<root/src/books/hw_form.tt2>
+    [% META title = 'Create/Update Book' %]
+    
+    [% widget_result.as_xml %]
+    
+    <p><a href="[% Catalyst.uri_for('list') %]">Return to book list</a></p>
+
+
+=head2 Add Links for Create and Update via C<HTML::Widget>
+
+Open C<root/src/books/list.tt2> in your editor and add the following to the bottom of the existing file:
+
+    <p>
+      HTML::Widget:
+      <a href="[% Catalyst.uri_for('hw_create') %]">Create</a>
+      <a href="[% Catalyst.uri_for('hw_update') %]">Update</a>
+    </p>
+
+
+=head2 Test The <HTML::Widget> Create Form
+
+Press C<Ctrl-C> to kill the previous server instance (if it's still running) and restart it:
+
+    $ script/myapp_server.pl
+
+Login as C<test01>.  Once at the Book List page, click the HTML::Widget "Create" link to display for form produced by C<make_book_widget>.  Fill out the form with the following values: Title = "Internetworking with TCP/IP Vol. II", Rating = "4", and Author = "Comer".  Click Submit, and you will be returned to the Create/Update Book page with a "Book created" status message displayed.  Click "Return to book list" to view the newly created book on the main list.
+
+Also note that this implementation allows you to can create books with bogus information.  Although we have constrained the authors with the drop-down list, there are no restrictions on items such as the length of the title (for example, you can create a one-letter title) and value for the rating (you can use any number you want, and even non-numeric values with SQLite).  The next section seeks to address this concern.
+
+B<Note:> Depending on the database you are using and how you established the columns in your tables, the database could obviously provide various levels of "type enforcement" on your data.  The key point being made in the previous paragraph is that the I<web application> itself is not performing any validation.
+
+
+=head1 C<HTML::WIDGET> VALIDATION AND FILTERING
+
+Although the use of L<HTML::Widget|HTML::Widget> in the previous section did provide an automated mechanism to build the form, the real power of this module stems from functionality that can automatically validate and filter the user input.  Validation uses constraints to be sure that users input appropriate data (for example, that the email field of a form contains a valid email address).  Filtering can be used to remove extraneous whitespace from fields or to escape meta-characters in user input.
+
+
+=head2 Add Constraints and Filters to the Widget Creation Method
+
+Open C<lib/MyApp/Controller/Books.pm> in your editor and update the C<make_book_widget> method to match the following (new sections have been marked with a C<*** NEW:> comment):
+
+    sub make_book_widget {
+        my ($self, $c) = @_;
+
+        # Create an HTML::Widget to build the form
+        my $w = $c->widget('book_form')->method('post');
+            
+        # Get authors
+        my @authorObjs = $c->model("MyAppDB::Author")->all();
+        my @authors = map {$_->id => $_->last_name }
+                           sort {$a->last_name cmp $b->last_name} @authorObjs;
+   
+        # Create the form feilds
+        $w->element('Textfield', 'title'  )->label('Title')->size(60);
+        $w->element('Textfield', 'rating' )->label('Rating')->size(1);
+        # ***NEW: Convert to multi-select list
+        $w->element('Select',    'authors')->label('Authors')
+            ->options(@authors)->multiple(1)->size(3);
+        $w->element('Submit',    'submit' )->value('submit');
+    
+        # ***NEW: Set constraints
+        $w->constraint(All     => qw/title rating authors/)
+            ->message('Required. ');
+        $w->constraint(Integer => qw/rating/)
+            ->message('Must be an integer. ');
+        $w->constraint(Range   => qw/rating/)->min(1)->max(5)
+            ->message('Must be a number between 1 and 5. ');
+        $w->constraint(Length  => qw/title/)->min(5)->max(50)
+            ->message('Must be between 5 and 50 characters. ');
+    
+        # ***NEW: Set filters
+        for my $column (qw/title rating authors/) {
+            $w->filter( HTMLEscape => $column );
+            $w->filter( TrimEdges  => $column );
+        }
+    
+        # Return the widget    
+        return $w;
+    }
+
+The main changes are:
+
+=over 4
+
+=item *
+
+The C<Select> element for C<authors> is changed from a single-select drop-down to a multi-select list by adding calls to C<multiple> (set to C<true>) and C<size> (set to the number of rows to display).
+
+=item *
+
+Four sets of constraints are added to provide validation of the user input.
+
+=item *
+
+Two filters are run on every field to remove and escape unwanted input.
+
+=back
+
+
+=head2 Rebuild the Form Submission Method to Include Validation
+
+Edit C<lib/MyApp/Controller/Books.pm> and change C<hw_create_do> to match the following code (enough of the code is different that you probably want to cut and paste this over code the existing method):
+
+    sub hw_create_do : Local {
+        my ($self, $c) = @_;
+    
+        # Retrieve the data from the form
+        my $title   = $c->request->params->{title};
+        my $rating  = $c->request->params->{rating};
+        my $authors = $c->request->params->{authors};
+        
+        # Create the widget and set the action for the form
+        my $w = $self->make_book_widget($c);
+        $w->action($c->uri_for('hw_create_do'));
+    
+        # Validate the form parameters
+        my $result = $w->process($c->req);
+    
+        # Write form (including validation error messages) to
+        # stash variable for use in template
+        $c->stash->{widget_result} = $result;
+    
+        # Were their validation errors?
+        if ($result->has_errors) {
+            # Warn the user at the top of the form that there were errors.
+            # Note that there will also be per-field feedback on
+            # validation errors because of '$w->process($c->req)' above.
+            $c->stash->{error_msg} = 'Validation errors!';
+        } else {
+            # Everything validated OK, so do the create
+            # Call create() on the book model object. Pass the table
+            # columns/field values we want to set as hash values
+            my $book = $c->model('MyAppDB::Book')->create({
+                    title   => $title,
+                    rating  => $rating
+                });
+    
+            # Add a record to the join table for this book, mapping to
+            # appropriate author.  Note that $authors will be 1 author as
+            # a scalar or ref to list of authors depending on how many the
+            # user selected; the 'ref $authors ?...' handles both cases
+            foreach my $author (ref $authors ? @$authors : $authors) {
+                $book->add_to_book_authors({author_id => $author});
+            }    
+            # Set a status message for the user
+            $c->stash->{status_msg} = 'Book created';
+        }
+    
+        # Set the template
+        $c->stash->{template} = 'books/hw_form.tt2';
+    }
+
+The key changes to C<hw_create_do> are:
+
+=over 4
+
+=item *
+
+C<hw_create_do> no longer does a C<detach> to C<hw_create> to redisplay the form.  Now that C<hw_create_do> has to process the form in order to perform the validation, we go ahead and build a complete set of form presentation logic into C<hw_create_do> (for example, C<hw_create_do> now has a C<$c-E<gt>stash-E<gt>{template}> line).  Note that if we process the form in C<hw_create_do> I<and> forward/detach back to <hw_create>, we would end up with C<make_book_widget> being called twice, resulting in a duplicate set of elements being added to the form.
+
+=item *
+
+C<$w-E<gt>process($c-E<gt>req)> is called to run the validation logic.  Not only does this set the C<has_errors> flag if validation errors are encountered, it returns a string containing any field-specific warning messages.
+
+=item *
+
+An C<if> statement checks if any validation errors were encountered.  If so, C<$c-E<gt>stash-E<gt>{error_msg}> is set and the input form is redisplayed.  If no errors were found, the object is created in a manner similar to the prior version of the C<hw_create_do> method.
+
+=back
+
+
+=head2 Try Out the Form
+
+Press C<Ctrl-C> to kill the previous server instance (if it's still running) and restart it:
+
+    $ script/myapp_server.pl
+
+Now try adding a book with various errors: title less than 5 characters, non-numeric rating, a rating of 0 or 6, etc.  Also try selecting one, two, and zero authors.  When you click Submit, the HTML::Widget C<constraint> items will validate the logic and insert feedback as appropriate.
+
+
+=head1 Enable C<DBIx::Class::HTMLWidget> Support
+
+In this section we will take advantage of some of the "auto-population" features of C<DBIx::Class::HTMLWidget>.  Enabling C<DBIx::Class::HTMLWidget> provides two additional methods to your DBIC model classes: 
+
+=over 4
+
+=item *
+
+fill_wiget()
+
+Takes data from the database and transfers it to your form widget.
+
+=item *
+
+populate_from_widget()
+
+Takes data from a form widget and uses it to update the corresponding records in the database.
+
+=back
+
+In other words, the two methods are a mirror image of each other: one reads from the database while the other writes to the database.
+
+
+=head2 Add C<DBIx::Class::HTMLWidget> to DBIC Model
+
+In order to use L<DBIx::Class::HTMLWidget|DBIx::Class::HTMLWidget>, we need to add C<HTMLWidget> to the C<load_components> line of DBIC result source files that need to use the C<fill_widget> and C<populate_from_widget> methods.  In this case, open C<lib/MyAppDB/Book.pm> and update the C<load_components> line to match:
+
+       __PACKAGE__->load_components(qw/PK::Auto Core HTMLWidget/);
+
+
+=head2 Use C<populate_from_widget> in C<hw_create_do>
+
+Edit C<lib/MyApp/Controller/Books.pm> and update C<hw_create_do> to match the following code:
+
+    =head2 hw_create_do
+    
+    Build an HTML::Widget form for book creation and updates
+    
+    =cut
+    
+    sub hw_create_do : Local {
+        my ($self, $c) = @_;
+    
+        # Create the widget and set the action for the form
+        my $w = $self->make_book_widget($c);
+        $w->action($c->uri_for('hw_create_do'));
+    
+        # Validate the form parameters
+        my $result = $w->process($c->req);
+    
+        # Write form (including validation error messages) to
+        # stash variable for use in template
+        $c->stash->{widget_result} = $result;
+    
+        # Were their validation errors?
+        if ($result->has_errors) {
+            # Warn the user at the top of the form that there were errors.
+            # Note that there will also be per-field feedback on
+            # validation errors because of '$w->process($c->req)' above.
+            $c->stash->{error_msg} = 'Validation errors!';
+        } else {
+            my $book = $c->model('MyAppDB::Book')->new({});
+            $book->populate_from_widget($result);
+    
+            # Add a record to the join table for this book, mapping to
+            # appropriate author.  Note that $authors will be 1 author as
+            # a scalar or ref to list of authors depending on how many the
+            # user selected; the 'ref $authors ?...' handles both cases
+            my $authors = $c->request->params->{authors};
+            foreach my $author (ref $authors ? @$authors : $authors) {
+                $book->add_to_book_authors({author_id => $author});
+            }
+    
+            # Set a status message for the user
+            $c->stash->{status_msg} = 'Book created';
+        }
+    
+        # Set the template
+        $c->stash->{template} = 'books/hw_form.tt2';
+    }
+
+In this version of C<hw_create_do> we removed the logic that manually pulled the form variables and used them to call C<$c-E<gt>model('MyAppDB::Book')-E<gt>create> and replaced it with a single call to C<$book-E<gt>populate_from_widget>.  Note that we still have to call C<$book-E<gt>add_to_book_authors> once per author because C<populate_from_widget> does not currently handle the relationships between tables.
+
+
+
+=head1 AUTHOR
+
+Kennedy Clark, C<hkclark@gmail.com>
+
+Please report any errors, issues or suggestions to the author.
+
+Copyright 2006, Kennedy Clark. All rights reserved.
+
+This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+Version: .94
+
diff --git a/lib/Catalyst/Manual/Tutorial/Appendices.pod b/lib/Catalyst/Manual/Tutorial/Appendices.pod
new file mode 100644 (file)
index 0000000..7bd0f4f
--- /dev/null
@@ -0,0 +1,113 @@
+=head1 NAME
+
+Catalyst::Manual::Tutorial::Appendices - Catalyst Tutorial – Part 9: Appendices
+
+
+
+=head1 OVERVIEW
+
+This is B<Part 9 of 9> for the Catalyst tutorial.
+
+L<Totorial Overview|Catalyst::Manual::Tutorial>
+
+=over 4
+
+=item 1
+
+L<Introduction|Catalyst::Manual::Tutorial::Intro>
+
+=item 2
+
+L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
+
+=item 3
+
+L<Basic CRUD|Catalyst::Manual::Tutorial03_BasicCRUD>
+
+=item 4
+
+L<Authentication|Catalyst::Manual::Tutorial::Authentication>
+
+=item 5
+
+L<Authorization|Catalyst::Manual::Tutorial::Authorization>
+
+=item 6
+
+L<Debugging|Catalyst::Manual::Tutorial::Debugging>
+
+=item 7
+
+L<Testing|Catalyst::Manual::Tutorial::Testing>
+
+=item 8
+
+L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
+
+=item 9
+
+B<Appendicies>
+
+=back
+
+
+
+=head1 DESCRIPTION
+
+This part of the tutorial provides supporting information relevant to the Catalyst tutorial.
+
+
+
+=head1 APPENDIX 1: CUT AND PASTE FOR POD-BASED EXAMPLES
+
+You may notice that Pod indents example code with four spaces.  This section provides some quick advice to "un-indent" this text in common editors.
+
+
+=head2 "Un-indenting" with Vi/Vim
+
+When cutting and pasting multi-line text from Pod-based documents, the following vi/vim regexs can be helpful to "un-indent" the inserted text (do NOT type the quotes, they are only included to show spaces in the regex patterns).  I<Note that all 3 of the regexs end in 4 spaces>:
+
+=over 4
+
+=item * 
+
+":0,$s/^    "
+
+Removes four leading spaces from the entire file (from the first line, C<0>, to the last line, C<$>).
+
+=item * 
+
+":.,$s/^    "
+
+Removes the first four spaces from the line the cursor is on at the time the regex command is executed (".") to the last line of the file.
+
+=item * 
+
+":.,44s/^    "
+
+Removes four leading space from the current line through line 44 (obviously adjust the C<44> to the appropriate value in your example).
+
+=back
+
+
+=head2 "Un-indenting" with Emacs
+
+B<todo>
+
+
+
+=head1 APPENDIX 2: USING MYSQL AND POSTGRESQL
+
+The main database used in this tutorial is the very simple yet deceptively powerful SQLite.  This section provides information that can be used to "convert" the tutorial to use MySQL and PostgreSQL.  However, note that part of the beauty of the MVC architecture is that very little database-specific code is spread throughout the system (at least when MVC is "done right").  Consequently, converting from one database to another is relatively painless with most Catalyst applications.  In general, you just need to adapt the schema definition C<.sql> file you use to initialize your database and adjust a few configuration parameters.
+
+Also note that the purpose of the data definition statements for this section are not designed to take maximum advantage of the various features in each database for issues such as referential integrity and field types/constraints.
+
+
+=head2 MySQL
+
+B<todo>
+
+=head2 PostgreSQL
+
+B<todo>
+
diff --git a/lib/Catalyst/Manual/Tutorial/Authentication.pod b/lib/Catalyst/Manual/Tutorial/Authentication.pod
new file mode 100644 (file)
index 0000000..e1ac250
--- /dev/null
@@ -0,0 +1,658 @@
+=head1 NAME
+
+Catalyst::Manual::Tutorial::Authentication - Catalyst Tutorial – Part 4: Authentication
+
+
+
+=head1 OVERVIEW
+
+This is B<Part 4 of 9> for the Catalyst tutorial.
+
+L<Totorial Overview|Catalyst::Manual::Tutorial>
+
+=over 4
+
+=item 1
+
+L<Introduction|Catalyst::Manual::Tutorial::Intro>
+
+=item 2
+
+L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
+
+=item 3
+
+L<Basic CRUD|Catalyst::Manual::Tutorial03_BasicCRUD>
+
+=item 4
+
+B<Authentication>
+
+=item 5
+
+L<Authorization|Catalyst::Manual::Tutorial::Authorization>
+
+=item 6
+
+L<Debugging|Catalyst::Manual::Tutorial::Debugging>
+
+=item 7
+
+L<Testing|Catalyst::Manual::Tutorial::Testing>
+
+=item 8
+
+L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
+
+=item 9
+
+L<Appendicies|Catalyst::Manual::Tutorial::Appendicies>
+
+=back
+
+
+
+=head1 DESCRIPTION
+
+Now that we finally have a simple yet functional application, we can focus on providing authentication (with authorization coming in Part 5).
+
+This part of the tutorial is divided into two main sections: 1) basic, cleartext authentication and 2) hash-based authentication.
+
+B<TIP>: Note that all of the code for this part of the tutorial can be pulled from the Catalyst Subversion repository in one step with the following command:
+
+    svn checkout http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/Tutorial@###
+    IMPORTANT: Does not work yet.  Will be completed for final version.
+
+
+
+=head1 BASIC AUTHENTICATION
+
+This section explores how add authentication logic to a Catalyst application.
+
+
+=head2 Add Users and Roles to the Database
+
+First, we add both user and role information to the database (we add the role information here although it will not be used until the authorization section, Part 5).  Create a new SQL script file by opening C<myapp02.sql> in your editor and insert:
+
+    --
+    -- Add users and roles tables, along with a many-to-many join table
+    --
+    CREATE TABLE users (
+            id            INTEGER PRIMARY KEY,
+            username      TEXT,
+            password      TEXT,
+            email_address TEXT,
+            first_name    TEXT,
+            last_name     TEXT,
+            active        INTEGER
+    );
+    CREATE TABLE roles (
+            id   INTEGER PRIMARY KEY,
+            role TEXT
+    );
+    CREATE TABLE user_roles (
+            user_id INTEGER,
+            role_id INTEGER,
+            PRIMARY KEY (user_id, role_id)
+    );
+    --
+    -- Load up some initial test data
+    --
+    INSERT INTO users VALUES (1, 'test01', 'mypass', 't01@na.com', 'Joe',  'Blow', 1);
+    INSERT INTO users VALUES (2, 'test02', 'mypass', 't02@na.com', 'Jane', 'Doe',  1);
+    INSERT INTO users VALUES (3, 'test03', 'mypass', 't03@na.com', 'No',   'Go',   0);
+    INSERT INTO roles VALUES (1, 'user');
+    INSERT INTO roles VALUES (2, 'admin');
+    INSERT INTO user_roles VALUES (1, 1);
+    INSERT INTO user_roles VALUES (1, 2);
+    INSERT INTO user_roles VALUES (2, 1);
+    INSERT INTO user_roles VALUES (3, 1);
+
+Then load this into the C<myapp.db> database with the following command:
+
+    $ sqlite3 myapp.db < myapp02.sql
+
+
+=head2 Add User and Role Information to Dbic Schema
+
+This step adds DBIC-based classes for the user-related database tables (the role information will not be used until the Part 5):
+
+Edit C<lib/MyAppDB.pm> and update the contents to match (only the C<MyAppDB =E<gt> [qw/Book BookAuthor Author User UserRole Role/]> line has changed):
+
+    package MyAppDB;
+    
+    =head1 NAME 
+    
+    MyAppDB -- DBIC Schema Class
+    
+    =cut
+    
+    # Our schema needs to inherit from 'DBIx::Class::Schema'
+    use base qw/DBIx::Class::Schema/;
+    
+    # Need to load the DB Model classes here.
+    # You can use this syntax if you want:
+    #    __PACKAGE__->load_classes(qw/Book BookAuthor Author User UserRole Role/);
+    # Also, if you simply want to load all of the classes in a directory
+    # of the same name as your schema class (as we do here) you can use:
+    #    __PACKAGE__->load_classes(qw//);
+    # But the variation below is more flexible in that it can be used to 
+    # load from multiple namespaces.
+    __PACKAGE__->load_classes({
+        MyAppDB => [qw/Book BookAuthor Author User UserRole Role/]
+    });
+    
+    1;
+
+
+=head2 Create New "Result Source Objects"
+
+Create the following three files with the content shown below.
+
+C<lib/MyAppDB/User.pm>:
+
+    package MyAppDB::User;
+    
+    use base qw/DBIx::Class/;
+    
+    # Load required DBIC stuff
+    __PACKAGE__->load_components(qw/PK::Auto Core/);
+    # Set the table name
+    __PACKAGE__->table('users');
+    # Set columns in table
+    __PACKAGE__->add_columns(qw/id username password email_address first_name last_name/);
+    # Set the primary key for the table
+    __PACKAGE__->set_primary_key('id');
+    
+    #
+    # Set relationships:
+    #
+    
+    # has_many():
+    #   args:
+    #     1) Name of relationship, DBIC will create accessor with this name
+    #     2) Name of the model class referenced by this relationship
+    #     3) Column name in *foreign* table
+    __PACKAGE__->has_many(map_user_role => 'MyAppDB::UserRole', 'user_id');
+    
+    
+    =head1 NAME
+    
+    MyAppDB::User - A model object representing a person with access to the system.
+    
+    =head1 DESCRIPTION
+    
+    This is an object that represents a row in the 'users' table of your application
+    database.  It uses DBIx::Class (aka, DBIC) to do ORM.
+    
+    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
+    Offline utilities may wish to use this class directly.
+    
+    =cut
+    
+    1;
+
+
+C<lib/MyAppDB/Role.pm>:
+
+    package MyAppDB::Role;
+    
+    use base qw/DBIx::Class/;
+    
+    # Load required DBIC stuff
+    __PACKAGE__->load_components(qw/PK::Auto Core/);
+    # Set the table name
+    __PACKAGE__->table('roles');
+    # Set columns in table
+    __PACKAGE__->add_columns(qw/id role/);
+    # Set the primary key for the table
+    __PACKAGE__->set_primary_key('id');
+    
+    #
+    # Set relationships:
+    #
+    
+    # has_many():
+    #   args:
+    #     1) Name of relationship, DBIC will create accessor with this name
+    #     2) Name of the model class referenced by this relationship
+    #     3) Column name in *foreign* table
+    __PACKAGE__->has_many(map_user_role => 'MyAppDB::UserRole', 'role_id');
+    
+    
+    =head1 NAME
+    
+    MyAppDB::Role - A model object representing a class of access permissions to 
+    the system.
+    
+    =head1 DESCRIPTION
+    
+    This is an object that represents a row in the 'roles' table of your 
+    application database.  It uses DBIx::Class (aka, DBIC) to do ORM.
+    
+    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
+    "Offline" utilities may wish to use this class directly.
+    
+    =cut
+    
+    1;
+
+
+C<lib/MyAppDB/UserRole.pm>:
+
+    package MyAppDB::UserRole;
+    
+    use base qw/DBIx::Class/;
+    
+    # Load required DBIC stuff
+    __PACKAGE__->load_components(qw/PK::Auto Core/);
+    # Set the table name
+    __PACKAGE__->table('user_roles');
+    # Set columns in table
+    __PACKAGE__->add_columns(qw/user_id role_id/);
+    # Set the primary key for the table
+    __PACKAGE__->set_primary_key(qw/user_id role_id/);
+    
+    #
+    # Set relationships:
+    #
+    
+    # belongs_to():
+    #   args:
+    #     1) Name of relationship, DBIC will create accessor with this name
+    #     2) Name of the model class referenced by this relationship
+    #     3) Column name in *this* table
+    __PACKAGE__->belongs_to(user => 'MyAppDB::User', 'user_id');
+    
+    # belongs_to():
+    #   args:
+    #     1) Name of relationship, DBIC will create accessor with this name
+    #     2) Name of the model class referenced by this relationship
+    #     3) Column name in *this* table
+    __PACKAGE__->belongs_to(role => 'MyAppDB::Role', 'role_id');
+    
+    
+    =head1 NAME
+    
+    MyAppDB::UserRole - A model object representing the JOIN between Users and Roles.
+    
+    =head1 DESCRIPTION
+    
+    This is an object that represents a row in the 'user_roles' table of your application
+    database.  It uses DBIx::Class (aka, DBIC) to do ORM.
+    
+    You probably won't need to use this class directly -- it will be automatically
+    used by DBIC where joins are needed.
+    
+    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
+    Offline utilities may wish to use this class directly.
+    
+    =cut
+    
+    1;
+
+The code for these three result source classes is obviously very familiar to the C<Book>, C<Author>, and C<BookAuthor> classes created in Part 2.
+
+
+=head2 Sanity-Check Reload of Development Server
+
+We aren't ready to try out the authentication just yet; we only want to do a quick check to be sure our model loads correctly.  Press C<Ctrl-C> to kill the previous server instance (if it's still running) and restart it:
+
+    $ script/myapp_server.pl
+
+Look for the three new model objects in the startup debug output:
+
+    ...
+     .-------------------------------------------------------------------+----------.
+    | Class                                                             | Type     |
+    +-------------------------------------------------------------------+----------+
+    | MyApp::Controller::Books                                          | instance |
+    | MyApp::Controller::Root                                           | instance |
+    | MyApp::Model::MyAppDB                                             | instance |
+    | MyApp::Model::MyAppDB::Author                                     | class    |
+    | MyApp::Model::MyAppDB::Book                                       | class    |
+    | MyApp::Model::MyAppDB::BookAuthor                                 | class    |
+    | MyApp::Model::MyAppDB::Role                                       | class    |
+    | MyApp::Model::MyAppDB::User                                       | class    |
+    | MyApp::Model::MyAppDB::UserRole                                   | class    |
+    | MyApp::View::TT                                                   | instance |
+    '-------------------------------------------------------------------+----------'
+    ...
+
+Again, notice that your "result source" classes have been "re-loaded" by Catalyst under C<MyApp::Model>.
+
+
+=head2 Include Authentication and Session Plugins
+
+Edit C<lib/MyApp.pm> and update it as follows (everything below C<DefaultEnd> is new):
+
+    use Catalyst qw/
+            -Debug
+            ConfigLoader
+            Static::Simple
+            
+            Dumper
+            StackTrace
+            DefaultEnd
+            
+            Authentication
+            Authentication::Store::DBIC
+            Authentication::Credential::Password
+            
+            Session
+            Session::Store::FastMmap
+            Session::State::Cookie
+            /;
+
+The three C<Authentication> plugins work together to support Authentication while the C<Session> plugins are required to maintain state across multiple HTTP requests.  Note that there are several options for L<Session::Store|Catalyst::Plugin::Session::Store> (although L<Session::Store::FastMmap|Catalyst::Plugin::Session::Store::FastMmap> is generally a good choice if you are on Unix; try L<Cache::FileCache|Catalyst::Plugin::Cache::FileCache> if you are on Win32) -- consult L<Session::Store|Catalyst::Plugin::Session::Store> and its subclasses for additional information.
+
+
+=head2 Configure Authentication
+
+Although C<__PACKAGE__-E<gt>config(name =E<gt> 'value');> is still supported, newer Catalyst applications tend to place all configuration information in C<myapp.yml> and automatically load this information into C<MyApp-E<gt>config> using the L<ConfigLoader|Catalyst::Plugin::ConfigLoader> plugin.
+
+Edit the C<myapp.yml> YAML and update it to match:
+
+    ---
+    name: MyApp
+    authentication:
+        dbic:
+            # Note this first definition would be the same as setting
+            # __PACKAGE__->config->{authentication}->{dbic}->{user_class} = 'MyAppDB::User'
+            # in lib/MyApp.pm (IOW, each hash key becomes a "name:" in the YAML file).
+            #
+            # This is the model object created by Catalyst::Model::DBIC from your
+            # schema (you created 'MyAppDB::User' but as the Catalyst startup
+            # debug messages show, it was loaded as 'MyApp::Model::MyAppDB::User').
+            # NOTE: Omit 'MyAppDB::Model' to avoid a component lookup issue in Catalyst 5.66
+            user_class: MyAppDB::User
+            # This is the name of the field in your 'users' table that contains the user's name
+            user_field: username
+            # This is the name of the field in your 'users' table that contains the password
+            password_field: password
+            # Other options can go here for hashed passwords
+
+Inline comments in the code above explain how each field is being used.
+
+B<TIP>: Although YAML uses a very simple and easy-to-ready format, it does require the use of a consistent level of indenting.  Be sure you line up everything on a given 'level' with the same number of indents.  Also, be sure not to use C<tab> characters (YAML does not support them because they are handled inconsistently across editors). 
+
+
+=head2 Add Login and Logout Controllers
+
+Use the Catalyst create script to create two stub controller files:
+
+    $ script/myapp_create.pl controller Login
+    $ script/myapp_create.pl controller Logout
+
+B<NOTE>: You could easily use a single controller here.  For example, you could have a C<User> controller with both C<login> and C<logout> actions.  Remember, Catalyst is designed to be very flexible, and leaves such matters up to you, the designer and programmer.
+
+Then open C<lib/MyApp/Controller/Login.pm> and add:
+
+    =head2 default
+    
+    Login logic
+    
+    =cut
+    
+    sub default : Private {
+        my ($self, $c) = @_;
+    
+        # Get the username and password from form
+        my $username = $c->request->params->{username} || "";
+        my $password = $c->request->params->{password} || "";
+    
+        # If the username and password values were found in form
+        if ($username && $password) {
+            # Attempt to log the user in
+            if ($c->login($username, $password)) {
+                # If successful, then let them use the application
+                $c->response->redirect($c->uri_for('/books/list'));
+                return;
+            } else {
+                # Set an error message
+                $c->stash->{error_msg} = "Bad username or password.";
+            }
+        }
+    
+        # If either of above don't work out, send to the login page
+        $c->stash->{template} = 'login.tt2';
+    }
+
+This controller fetches the C<username> and C<password> values from the login form and attempts to perform a login.  If successful, it redirects the user to the book list page.  If the login fails, the user will stay at the login page but receive an error message.  If the C<username> and C<password> values are not present in the form, the user will be taken to the empty login form.
+
+Next, create a corresponding method in C<lib/MyApp/Controller/Logout.pm>:
+
+    =head2 default
+    
+    Logout logic
+    
+    =cut
+    
+    sub default : Private {
+        my ($self, $c) = @_;
+    
+        # Clear the user's state
+        $c->logout;
+    
+        # Send the user to the starting
+        $c->response->redirect($c->uri_for('/'));
+    }
+
+
+=head2 Add a Login Form TT Template Page
+
+Create a login form by opening C<root/src/login.tt2> and inserting:
+
+    [% META title = 'Login' %]
+    
+    <!-- Login form -->
+    <form method="post" action=" [% Catalyst.uri_for('/login') %] ">
+      <table>
+        <tr>
+          <td>Username:</td>
+          <td><input type="text" name="username" size="40" /></td>
+        </tr>
+        <tr>
+          <td>Password:</td>
+          <td><input type="password" name="password" size="40" /></td>
+        </tr>
+        <tr>
+          <td colspan="2"><input type="submit" name="submit" value="Submit" /></td>
+        </tr>
+      </table>
+    </form>
+
+
+=head2 Add Valid User Check
+
+We need something that provides enforcement for the authentication mechanism -- a I<global> mechanism that prevents users who have not passed authentication from reaching any pages except the login page.  This is generally done via an C<auto> action/method (prior to Catalyst v5.66, this sort of thing would go in C<MyApp.pm>, but starting in v5.66, the preferred location is C<lib/MyApp/Controller/Root.pm>).
+
+Edit the existing C<lib/MyApp/Controller/Root.pm> class file and insert the following method:
+
+    =head2 auto
+    
+    Check if there is a user and, if not, forward to login page
+    
+    =cut
+    
+    # Note that 'auto' runs after 'begin' but before your actions and that
+    # 'auto' "chain" (all from application path to most specific class are run)
+    sub auto : Private {
+        my ($self, $c) = @_;
+    
+        # Allow unauthenticated users to reach the login page
+        if ($c->request->path =~ /login/) {
+            return 1;
+        }
+    
+        # If a user doesn't exist, force login
+        if (!$c->user_exists) {
+            # Dump a log message to the development server debug output
+            $c->log->debug('***Root::auto User not found, forwarding to /login');
+            # Redirect the user to the login page
+            $c->response->redirect($c->uri_for('/login'));
+            # Return 0 to cancel 'post-auto' processing and prevent use of application
+            return 0;
+        }
+    
+        # User found, so return 1 to continue with processing after this 'auto'
+        return 1;
+    }
+
+B<Note:> Catalyst provides a number of different types of actions, such as C<Local>, C<Regex>, and C<Private>.  You should refer to L<Catalyst::Manual::Intro|Catalyst::Manual::Intro> for a more detailed explanation, but the following bullet points provide a quick introduction:
+
+=over 4
+
+=item *
+
+The majority of application use C<Local> actions for items that respond to user requests and C<Private> actions for those that do not directly respond to user input.
+
+=item *
+
+There are five types of C<Private> actions: C<begin>, C<end>, C<default>, C<index>, and C<auto>.
+
+=item *
+
+Unlike the other private C<Private> actions where only a single method is called for each request, I<every> auto action along the chain of namespaces will be called.
+
+=back
+
+By placing the authentication enforcement code inside the C<auto> method of C<lib/MyApp/Controller/Root.pm> (or C<lib/MyApp.pm>), it will be called for I<every> request that is received by the entire application.
+
+
+=head2 Displaying Content Only to Authenticated Users
+
+Let's say you want to provide some information on the login page that changes depending on whether the user has authenticated yet.  To do this, open C<root/src/login.tt2> in your editor and add the following lines to the bottom of the file:
+
+    <p>
+    [%
+       # This code illustrates how certain parts of the TT 
+       # template will only be shown to users who have logged in
+    %]
+    [% IF Catalyst.user %]
+        Please Note: You are already logged in as '[% Catalyst.user.username %]'.
+        You can <a href="[% Catalyst.uri_for('/logout') %]">logout</a> here.
+    [% ELSE %]
+        You need to log in to use this application.
+    [% END %]
+    [%#
+       Note that this whole block is a comment because the "#" appears
+       immediate after the "[%" (with no spaces in between).  Although it 
+       can be a handy way to temporarily "comment out" a whole block of 
+       TT code, it's probably a little too subtle for use in "normal" 
+       comments.
+    %]
+
+Although most of the code is comments, the middle few lines provide a "you are already logged in" reminder if the user returns to the login page after they have already authenticated.  For users who have not yet authenticated, a "You need to log in..." message is displayed (note the use of an IF-THEN-ELSE construct in TT).
+
+
+=head2 Try Out Authentication
+
+Press C<Ctrl-C> to kill the previous server instance (if it's still running) and 
+restart it:
+
+    $ script/myapp_server.pl
+
+B<IMPORTANT NOTE>: If you happen to be using Internet Explorer, you may need to use the command C<script/myapp_server.pl -k> to enable the keepalive feature in the development server.  Otherwise, the HTTP redirect on successful login may not work correctly with IE (it seems to work without –k if you are running the web browser and development server on the same machine).  If you are using browser a browser other than IE, it should work either way.  If you want to make keepalive the default, you can edit C<script/myapp_server.pl> and change the initialization value for C<$keepalive> to C<1>.  (You will need to do this every time you create a new Catalyst application or rebuild the C<myapp_server.pl> script.)
+
+Now trying going to L<http://localhost:3000/books/list> and you should be redirected to the login page, hitting Shift+Reload if necessary (the "You are already logged in" message should I<not> appear -- if it does, click the C<logout> button and try again).  Make note of the C<***Root::auto User not found...> debug message in the development server output.  Enter username C<test01> and password C<mypass>, and you should be taken to the Book List page.  
+
+Open C< root/src/books/list.tt2> and add the following lines to the bottom:
+
+    <p>
+      <a href="[% Catalyst.uri_for('/login') %]">Login</a>
+      <a href="[% Catalyst.uri_for('form_create') %]">Create</a>
+    </p>
+
+Reload your browser and you should now see a "Login" link at the bottom of the page (as mentioned earlier, you can update template files without reloading the development server).  Click this link to return to the login page.  This time you I<should> see the "You are already logged in" message.
+
+Finally, click the C<You can logout here> link on the C</login> page.  You should stay at the login page, but the message should change to "You need to log in to use this application."
+
+
+
+=head1 USING PASSWORD HASHES
+
+In this section we increase the security of our system by converting from cleartext passwords to SHA-1 password hashes.
+
+B<Note:> This section is optional.  You can skip it and the rest of the tutorial will function normally.  
+
+Note that even with the techniques shown in this section, the browser still transmits the passwords in cleartext to your application.  We are just avoiding the I<storage> of cleartext passwords in the database by using a SHA-1 hash.  If you are concerned about cleartext passwords between the browser and your application, consider using SSL/TLS.
+
+
+=head2 Get a SHA-1 Hash for the Password
+
+Catalyst uses the C<Digest > module to support a variety of hashing algorithms.  Here we will use SHA-1 (SHA = Secure Hash Algorithm).  First, we should compute the SHA-1 hash for the "mypass" password we are using.  The following command-line Perl script provides a "quick and dirty" way to do this:
+
+    $ perl -MDigest::SHA -e 'print Digest::SHA::sha1_hex("mypass"), "\n"'
+    e727d1464ae12436e899a726da5b2f11d8381b26
+    $
+
+
+=head2 Switch to SHA-1 Password Hashes in the Database
+
+Next, we need to change the C<password> column of our C<users> table to store this hash value vs. the existing cleartext password.  Open C<myapp03.sql> in your editor and enter:
+
+    --
+    -- Convert passwords to SHA-1 hashes
+    --
+    UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 1;
+    UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 2;
+    UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 3;
+
+Then use the following command to update the SQLite database:
+
+    $ sqlite3 myapp.db < myapp03.sql
+
+B<Note:> We are using SHA-1 hashes here, but many other hashing algorithms are supported.  See C<Digest > for more information.
+
+
+=head2 Enable SHA-1 Hash Passwords in C<Catalyst::Plugin::Authentication::Store::DBIC>
+
+Edit C<myapp.yml> and update it to match (the C<password_type> and C<password_hash_type> are new, everything else is the same):
+
+    ---
+    name: MyApp
+    authentication:
+        dbic:
+            # Note this first definition would be the same as setting
+            # __PACKAGE__->config->{authentication}->{dbic}->{user_class} = 'MyAppDB::User'
+            # in lib/MyApp.pm (IOW, each hash key becomes a "name:" in the YAML file).
+            #
+            # This is the model object created by Catalyst::Model::DBIC from your
+            # schema (you created 'MyAppDB::User' but as the Catalyst startup
+            # debug messages show, it was loaded as 'MyApp::Model::MyAppDB::User').
+            # NOTE: Omit 'MyAppDB::Model' to avoid a component lookup issue in Catalyst 5.66
+            user_class: MyAppDB::User
+            # This is the name of the field in your 'users' table that contains the user's name
+            user_field: username
+            # This is the name of the field in your 'users' table that contains the password
+            password_field: password
+            # Other options can go here for hashed passwords
+            # Enabled hashed passwords
+            password_type: hashed
+            # Use the SHA-1 hashing algorithm
+            password_hash_type: SHA-1
+
+
+=head2 Try Out the Hashed Passwords
+
+Press C<Ctrl-C> to kill the previous server instance (if it's still running) and restart it:
+
+    $ script/myapp_server.pl
+
+You should now be able to go to L<http://localhost:3000/books/list> and login as before.  When done, click the "Logout" link on the login page (or point your browser at L<http://localhost:3000/logout>).
+
+
+
+=head1 AUTHOR
+
+Kennedy Clark, C<hkclark@gmail.com>
+
+Please report any errors, issues or suggestions to the author.
+
+Copyright 2006, Kennedy Clark. All rights reserved.
+
+This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+Version: .94
+
diff --git a/lib/Catalyst/Manual/Tutorial/Authorization.pod b/lib/Catalyst/Manual/Tutorial/Authorization.pod
new file mode 100644 (file)
index 0000000..d83f5e3
--- /dev/null
@@ -0,0 +1,350 @@
+=head1 NAME
+
+Catalyst::Manual::Tutorial::Authorization - Catalyst Tutorial – Part 5: Authorization
+
+
+
+=head1 OVERVIEW
+
+This is B<Part 5 of 9> for the Catalyst tutorial.
+
+L<Totorial Overview|Catalyst::Manual::Tutorial>
+
+=over 4
+
+=item 1
+
+L<Introduction|Catalyst::Manual::Tutorial::Intro>
+
+=item 2
+
+L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
+
+=item 3
+
+L<Basic CRUD|Catalyst::Manual::Tutorial03_BasicCRUD>
+
+=item 4
+
+L<Authentication|Catalyst::Manual::Tutorial::Authentication>
+
+=item 5
+
+B<Authorization>
+
+=item 6
+
+L<Debugging|Catalyst::Manual::Tutorial::Debugging>
+
+=item 7
+
+L<Testing|Catalyst::Manual::Tutorial::Testing>
+
+=item 8
+
+L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
+
+=item 9
+
+L<Appendicies|Catalyst::Manual::Tutorial::Appendicies>
+
+=back
+
+
+
+=head1 DESCRIPTION
+
+This part of the tutorial adds role-based authorization to the existing authentication implemented in Part 4.  It provides simple examples of how to use roles in both TT templates and controller actions.  The first half looks at manually configured authorization.  The second half looks at how the ACL authorization plugin can simplify your code.
+
+B<TIP>: Note that all of the code for this part of the tutorial can be pulled from the Catalyst Subversion repository in one step with the following command:
+
+    svn checkout http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/Tutorial@###
+    IMPORTANT: Does not work yet.  Will be completed for final version.
+
+
+
+=head1 BASIC AUTHORIZATION
+
+In this section you learn how to manually configure authorization.
+
+
+=head2 Update Plugins to Include Support Authorization
+
+Edit C<lib/MyApp.pm> and add C<Authorization::Roles> to the list:
+
+    use Catalyst qw/
+            -Debug
+            ConfigLoader
+            Static::Simple
+            
+            Dumper
+            StackTrace
+            DefaultEnd
+            
+            Authentication
+            Authentication::Store::DBIC
+            Authentication::Credential::Password
+            Authorization::Roles
+            
+            Session
+            Session::Store::FastMmap
+            Session::State::Cookie
+            /;
+
+
+=head2 Add Config Information for Authorization
+
+Edit C<myapp.yml> and update it to match (everything from the "authorization:" line down is new):
+
+    ---
+    name: MyApp
+    authentication:
+        dbic:
+            # Note this first definition would be the same as setting
+            # __PACKAGE__->config->{authentication}->{dbic}->{user_class} = 'MyAppDB::User'
+            # in lib/MyApp.pm (IOW, each hash key becomes a "name:" in the YAML file).
+            #
+            # This is the model object created by Catalyst::Model::DBIC from your
+            # schema (you created 'MyAppDB::User' but as the Catalyst startup
+            # debug messages show, it was loaded as 'MyApp::Model::MyAppDB::User').
+            # NOTE: Omit 'MyAppDB::Model' to avoid a component lookup issue in Catalyst 5.66
+            user_class: MyAppDB::User
+            # This is the name of the field in your 'users' table that contains the user's name
+            user_field: username
+            # This is the name of the field in your 'users' table that contains the password
+            password_field: password
+            # Other options can go here for hashed passwords
+            # Enabled hashed passwords
+            password_type: hashed
+            # Use the SHA-1 hashing algorithm
+            password_hash_type: SHA-1
+    authorization:
+        dbic:
+            # This is the model object created by Catalyst::Model::DBIC from your
+            # schema (you created 'MyAppDB::Role' but as the Catalyst startup
+            # debug messages show, it was loaded as 'MyApp::Model::MyAppDB::Role').
+            # NOTE: Omit 'MyAppDB::Model' to avoid a component lookup issue in Catalyst 5.66
+            role_class: MyAppDB::Role
+            # The name of the field in the 'roles' table that contains the role name
+            role_field: role
+            # The name of the accessor used to map a user to a role
+            # See the has_many() in MyAppDB/User.pm
+            role_rel: map_user_role
+            # The name of the field in the user_role table that references the user
+            user_role_user_field: user_id
+
+
+=head2 Add Role-Specific Logic to the "Book List" Template
+
+Open C<root/src/books/list.tt2> in your editor and add the following lines to the bottom of the file:
+
+    <p>Hello [% Catalyst.user.username %], you have the following roles:</p>
+    
+    <ul>
+      [% # Dump list of roles -%]
+      [% FOR role = Catalyst.user.roles %]<li>[% role %]</li>[% END %]
+    </ul>
+    
+    <p>
+    [% # Add some simple role-specific logic to template %]
+    [% # Use $c->check_user_roles() to check authz -%]
+    [% IF Catalyst.check_user_roles('user') %]
+      [% # Give normal users a link for 'logout' %]
+      <a href="[% Catalyst.uri_for('/logout') %]">Logout</a>
+    [% END %]
+    
+    [% # Can also use $c->user->check_roles() to check authz -%]
+    [% IF Catalyst.check_user_roles('admin') %]
+      [% # Give admin users a link for 'create' %]
+      <a href="[% Catalyst.uri_for('form_create') %]">Create</a>
+    [% END %]
+    </p>
+
+This code displays a different combination of links depending on the roles assigned to the user..
+
+
+=head2 Limit C<Books::add> to C<admin> Users
+
+C<IF> statements in TT templates simply control the output that is sent to the user's browser; it provides no real enforcement (if users know or guess the appropriate URLs, they are still perfectly free to hit any action within your application).  We need to enhance the controller logic to wrap restricted actions with role validation logic.  
+
+For example, we might want to restrict the "formless create" action to admin-level users by editing C<lib/MyApp/Controller/Books.pm> and updating C<url_create> to match the following code:
+
+    =head2 url_create
+
+    Create a book with the supplied title, and rating 
+    with manual authorization
+    
+    =cut
+    
+    sub url_create : Local {
+        # In addition to self & context, get the title, rating & author_id args
+        # from the URL.  Note that Catalyst automatically puts extra information
+        # after the "/<controller_name>/<action_name/" into @_
+        my ($self, $c, $title, $rating, $author_id) = @_;
+    
+        # Check the user's roles
+        if ($c->check_user_roles('admin')) {
+            # Call create() on the book model object. Pass the table 
+            # columns/field values we want to set as hash values
+            my $book = $c->model('MyAppDB::Book')->create({
+                    title   => $title,
+                    rating  => $rating
+                });
+            
+            # Add a record to the join table for this book, mapping to 
+            # appropriate author
+            $book->add_to_book_authors({author_id => $author_id});
+            # Note: Above is a shortcut for this:
+            # $book->create_related('book_authors', {author_id => $author_id});
+            
+            # Assign the Book object to the stash for display in the view
+            $c->stash->{book} = $book;
+        
+            # This is a hack to disable XSUB processing in Data::Dumper
+            # (it's used in the view).  This is a work-around for a bug in
+            # the interaction of some versions or Perl, Data::Dumper & DBIC.
+            # You won't need this if you aren't using Data::Dumper (or if
+            # you are running DBIC 0.06001 or greater), but adding it doesn't 
+            # hurt anything either.
+            $Data::Dumper::Useperl = 1;
+        
+            # Set the TT template to use
+            $c->stash->{template} = 'books/create_done.tt2';
+        } else {
+            # Provide very simple feedback to the user
+            $c->response->body('Unauthorized!');
+        }
+    }
+
+
+To add authorization, we simply write the main code of this method in an C<if> statement that calls C<check_user_roles>.  If the user does not have the appropriate permissions, they receive an "Unauthorized!" message.  Note that we intentionally chose to display the message this way to demonstrate that TT templates will not be used if the response body has already been set.  In reality you would probably want to use a technique that maintains the visual continuity of your template layout (for example, using the "status" or "error" message feature added in Part 2).
+
+B<TIP>: If you want to keep your existing C<url_create> method, you can create a new copy and comment out the original by making it look like a Pod comment.  For example, put something like C<=begin> before C<sub add : Local {> and C<=end> after the closing C<}>.
+
+
+=head2 Try Out Authentication And Authorization
+
+Press C<Ctrl-C> to kill the previous server instance (if it's still running) and restart it:
+
+    $ script/myapp_server.pl
+
+Now trying going to L<http://localhost:3000/books/list> and you should be taken to the login page (you might have to C<Shift+Reload> your browser).  Try logging in with both C<test01> and C<test02> (both use a password of C<mypass>) and notice how the roles information updates at the bottom of the "Book List" page. Also try the C<Logout> link on the book list page.
+
+Now the "url_create" URL will work if you are already logged in as user C<test01>, but receive an authorization failure if you are logged in as C<test02>.  Try:
+
+    http://localhost:3000/books/url_create/test/1/6
+
+while logged in as each user.  Use one of the 'Logout' links (or go to L<http://localhost:3000/logout> in you browser directly) when you are done.
+
+
+
+=head1 ENABLE ACL-BASED AUTHORIZATION
+
+This section takes a brief look at how the L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL> can automate much of the work required to perform role-based authorization in a Catalyst application.
+
+
+=head2 Add the C<Catalyst::Plugin::Authorization::ACL> Plugin
+
+Open C<lib/MyApp.pm> in your editor and add the following plugin to the C<use Catalyst> statement:
+
+    Authorization::ACL
+
+Note that the remaining C<use Catalyst> plugins from earlier sections are not shown here, but they should still be included.
+
+
+=head2 Add ACL Rules to the Application Class
+
+Open C<lib/MyApp.pm> in your editor and add the following B<BELOW> the C<__PACKAGE__-E<gt>setup;> statement:
+
+    # Authorization::ACL Rules
+    __PACKAGE__->deny_access_unless(
+            "/books/form_create",
+            [qw/admin/],
+        );
+    __PACKAGE__->deny_access_unless(
+            "/books/form_create_do",
+            [qw/admin/],
+        );
+    __PACKAGE__->deny_access_unless(
+            "/books/delete",
+            [qw/user admin/],
+        );
+
+Each of the three statements above comprises an ACL plugin "rule".  The first two rules only allow admin-level users to create new books using the form (both the form itself and the data submission logic are protected).  The third statement allows both users and admin to delete books.  The C</books/url_create> action will continue to be protected by the "manually configured" authorization created earlier in this part of the tutorial.
+
+The ACL plugin permits you to apply allow/deny logic in a variety of ways.  The following provides a basic overview of the capabilities:
+
+=over 4
+
+=item * 
+
+The ACL plugin only operates on the Catalyst "private namespace".  You are using the private namespace when you use C<Local> actions.  C<Path>, C<Regex>, and C<Global> allow you to specify actions where the path and the namespace differ -- the ACL plugin will not work in these cases.
+
+=item * 
+
+Each rule is expressed in a separate C<__PACKAGE__-E<gt>deny_access_unless()> or C<__PACKAGE__-E<gt>allow_access_if()> line (there are several other methods that can be used for more complex policies, see the C<METHODS> portion of the L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL> documentation for more details).
+
+=item * 
+
+Each rule can contain multiple roles but only a single path.
+
+=item * 
+
+The rules are tried in order (with the "most specific" rules tested first), and processing stops at the first "match" where an allow or deny is specified.  Rules "fall through" if there is not a "match" (where a "match" means the user has the specified role).  If a "match" is found, then processing stops there and the appropriate allow/deny action is taken.
+
+=item * 
+
+If none of the rules match, then access is allowed.
+
+=item * 
+
+The rules currently need to be specific in the application class C<lib\MyApp.pm> B<after> the C<__PACKAGE__-E<gt>setup;> line.
+
+=back
+
+
+=head2 Add a Method to Handle Access Violations
+
+By default, L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL> throws an exception when authorization fails.  This will take the user to the Catalyst debug screen, or a "Please come back later" message if you are not using the C<-Debug> flag. This step uses the C<access_denied> method in order to provide more appropriate feedback to the user.
+
+Open C<lib/MyApp/Controller/Books.pm> in your editor and add the following method:
+
+    =head2 access_denied
+    
+    Handle Catalyst::Plugin::Authorization::ACL access denied exceptions
+    
+    =cut
+    
+    sub access_denied : Private {
+        my ($self, $c) = @_;
+    
+        # Set the error message
+        $c->stash->{error_msg} = 'Unauthorized!';
+
+        # Display the list
+        $c->forward('list');
+    }
+
+
+Then run the Catalyst development server script:    
+
+    $ script/myapp_server.pl
+
+Log in as C<test02>.  Once at the book list, click the "Create" link to try the C<form_create> action.  You should receive a red "Unauthorized!" error message at the top of the list.  (Note that in reality you would probably want to place the "Create" link code in C<root/src/books/list.tt2> inside an C<IF> statement that only displays the list to admin-level users.)  If you log in as C<test01> you should be able to view the C<form_create> form and add a new book.
+
+When you are done, use one of the 'Logout' links (or go to the L<http://localhost:3000/logout> URL directly) when you are done.
+
+
+
+=head1 AUTHOR
+
+Kennedy Clark, C<hkclark@gmail.com>
+
+Please report any errors, issues or suggestions to the author.
+
+Copyright 2006, Kennedy Clark. All rights reserved.
+
+This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+Version: .94
+
diff --git a/lib/Catalyst/Manual/Tutorial/BasicCRUD.pod b/lib/Catalyst/Manual/Tutorial/BasicCRUD.pod
new file mode 100644 (file)
index 0000000..a6034c0
--- /dev/null
@@ -0,0 +1,367 @@
+=head1 NAME
+
+Catalyst::Manual::Tutorial::BasicCRUD - Catalyst Tutorial – Part 3: Basic CRUD
+
+
+
+=head1 OVERVIEW
+
+This is B<Part 3 of 9> for the Catalyst tutorial.
+
+L<Totorial Overview|Catalyst::Manual::Tutorial>
+
+=over 4
+
+=item 1
+
+L<Introduction|Catalyst::Manual::Tutorial::Intro>
+
+=item 2
+
+L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
+
+=item 3
+
+B<Basic CRUD>
+
+=item 4
+
+L<Authentication|Catalyst::Manual::Tutorial::Authentication>
+
+=item 5
+
+L<Authorization|Catalyst::Manual::Tutorial::Authorization>
+
+=item 6
+
+L<Debugging|Catalyst::Manual::Tutorial::Debugging>
+
+=item 7
+
+L<Testing|Catalyst::Manual::Tutorial::Testing>
+
+=item 8
+
+L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
+
+=item 9
+
+L<Appendicies|Catalyst::Manual::Tutorial::Appendicies>
+
+=back
+
+
+
+=head1 DESCRIPTION
+
+This part of the tutorial builds on the fairly primitive application created in Part 2 to add basic support for Create, Read, Update, and Delete (CRUD) of C<Book> objects.  Note that the 'list' function in Part 2 already implements the Read portion of Crud (although Read normally refers to reading a single object; you could implement full read functionality using the techniques introduced below).  This section will focus on the Create and Delete aspects of CRUD.  More advanced capabilities, including full Update functionality, will be addressed in Part 8.
+
+B<TIP>: Note that all of the code for this part of the tutorial can be pulled from the Catalyst Subversion repository in one step with the following command:
+
+    svn checkout http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/Tutorial@###
+    IMPORTANT: Does not work yet.  Will be completed for final version.
+
+
+
+=head1 FORMLESS SUBMISSION
+
+Our initial attempt at object creation will utilize the "URL arguments" feature of Catalyst (we will employ the more common form-based submission in the sections that follow).
+
+
+=head2 Include a Create Action in the Books Controller
+
+Edit C<lib/MyApp/Controller/Books.pm> and enter the following method:
+
+    =head2 url_create
+    
+    Create a book with the supplied title, rating and author
+    
+    =cut
+    
+    sub url_create : Local {
+        # In addition to self & context, get the title, rating & author_id args
+        # from the URL.  Note that Catalyst automatically puts extra information
+        # after the "/<controller_name>/<action_name/" into @_
+        my ($self, $c, $title, $rating, $author_id) = @_;
+    
+        # Call create() on the book model object. Pass the table 
+        # columns/field values we want to set as hash values
+        my $book = $c->model('MyAppDB::Book')->create({
+                title   => $title,
+                rating  => $rating
+            });
+        
+        # Add a record to the join table for this book, mapping to 
+        # appropriate author
+        $book->add_to_book_authors({author_id => $author_id});
+        # Note: Above is a shortcut for this:
+        # $book->create_related('book_authors', {author_id => $author_id});
+        
+        # Assign the Book object to the stash for display in the view
+        $c->stash->{book} = $book;
+    
+        # This is a hack to disable XSUB processing in Data::Dumper
+        # (it's used in the view).  This is a work-around for a bug in
+        # the interaction of some versions or Perl, Data::Dumper & DBIC.
+        # You won't need this if you aren't using Data::Dumper (or if
+        # you are running DBIC 0.06001 or greater), but adding it doesn't 
+        # hurt anything either.
+        $Data::Dumper::Useperl = 1;
+    
+        # Set the TT template to use
+        $c->stash->{template} = 'books/create_done.tt2';
+    }
+
+Notice that Catalyst takes "extra slash-separated information" from the URL and passes it as arguments in C<@_>.  The C<url_create> action then uses a simple call to the DBIC C<create> method to add the requested information to the database (with a separate call to C<add_to_book_authors> to update the join table).  As do virtually all controller methods (at least the ones that directly handle user input), it then sets the template that should handle this request.
+
+
+=head2 Include a Template for the C<url_create> Action:
+
+Edit C<root/src/books/create_done.tt2> and then enter:
+
+    [% # Use the TT Dumper plugin to Data::Dumper variables to the browser   -%]
+    [% # Not a good idea for production use, though. :-)  'Indent=1' is      -%]
+    [% # optional, but prevents "massive indenting" of deeply nested objects -%]
+    [% USE Dumper(Indent=1) -%]
+    
+    [% # Set the page title -%]
+    [% META title = 'Book Created' %]
+    
+    [% # Output information about the record that was added.  Note use  -%]
+    [% # of 'first' to only list the first author (if > 1 author).      -%] 
+    <p>Added book '[% book.title %]' by '[% book.authors.first.last_name %]'
+    with a rating of [% book.rating %].</p>
+    
+    [% # Provide a link back to the list page                                    -%]
+    [% # 'uri_for()' builds a full URI; e.g., 'http://localhost:3000/books/list' -%]
+    <p><a href="[% Catalyst.uri_for('/books/list') %]">Return to list</a></p>
+    
+    [% # Try out the TT Dumper -%]
+    <pre>
+    Dump of the 'book' variable:
+    [% Dumper.dump(book) %]
+    </pre>
+
+The TT C<USE> directive allows access to a variety of plugin modules (we are talking TT plugins here, not Catalyst plugins) to add extra functionality to the base TT capabilities.  Here, the plugin allows L<Data::Dumper|Data::Dumper> "pretty printing" of objects and variables.  Other than that, the rest of the code should be familiar from the examples in Part 2.
+
+B<IMPORTANT NOTE> As mentioned earlier, the C<MyApp::View::TT.pm> view class created by TTSite redefines the name used to access the Catalyst context object in TT templates from the usual C<c> to C<Catalyst>.
+
+
+=head2 Try the C<url_create> Feature
+
+If the application is still running from before, use C<Ctrl-C> to kill it.  Then restart the server:
+
+    $ script/myapp_server.pl
+
+Note that new path for C</books/url_create> appears in the startup debug output.
+
+B<TIP>: You can use C<script/myapp_server.pl -r> to have the development server auto-detect changed files and reload itself (if your browser acts odd, you should also try throwing in a C<-k>).  If you make changes to just the TT templates, you do not need to reload the development server (only changes to "compiled code" such as Controller and Model C<.pm> files require a reload).
+
+Next, use your browser to enter the following URL:
+
+    http://localhost:3000/books/url_create/TCPIP_Illustrated_Vol-2/5/4
+
+Your browser should display " Added book 'TCPIP_Illustrated_Vol-2' by 'Stevens' with a rating of 5." along with a dump of the new book model object.  You should also see the following DBIC debug messages displayed in the development server log messages:
+
+    INSERT INTO books (rating, title) VALUES (?, ?): `5', `TCPIP_Illustrated_Vol-2'
+    INSERT INTO book_authors (author_id, book_id) VALUES (?, ?): `4', `6'
+
+If you then click the "Return to list" link, you should find that there are now six books shown (if necessary, Shift-Reload your browser at the C</books/list> page).
+
+
+
+=head1 MANUALLY BUILDING A CREATE FORM
+
+Although the C<url_create> action in the previous step does begin to reveal the power and flexibility of both Catalyst and DBIC, it's obviously not a very realistic example of how users should be expected to enter data.  This section begins to address that concern.
+
+
+=head2 Add Method to Display The Form
+
+Edit C<lib/MyApp/Controller/Books.pm> and add the following method:
+
+    =head2 form_create
+    
+    Display form to collect information for book to create
+    
+    =cut
+    
+    sub form_create : Local {
+        my ($self, $c) = @_;
+    
+        # Set the TT template to use
+        $c->stash->{template} = 'books/form_create.tt2';
+    }
+
+This action merely invokes a view containing a book creation form.
+
+
+=head2 Add a Template for the Form
+
+Open C<root/src/books/form_create.tt2> in your editor and enter:
+
+    [% META title = 'Book Create' -%]
+    
+    <form method="post" action="[% Catalyst.uri_for('form_create_do') %]">
+    <table>
+      <tr><td>Title:</td><td><input type="text" name="title"></td></tr>
+      <tr><td>Rating:</td><td><input type="text" name="rating"></td></tr>
+      <tr><td>Author ID:</td><td><input type="text" name="author_id"></td></tr>
+    </table>
+    <input type="submit" name="Submit" value="Submit">
+    </form>
+
+Note that we have specified the target of the form data as C<form_create_do>, the method created in the section that follows.
+
+
+=head2 Add Method to Process Form Values and Update Database
+
+Edit C<lib/MyApp/Controller/Books.pm> and add the following method to save the form information to the databse:
+
+    =head2 form_create_do
+    
+    Take information from form and add to database
+    
+    =cut
+    
+    sub form_create_do : Local {
+        my ($self, $c) = @_;
+    
+        # Retrieve the values from the form
+        my $title     = $c->request->params->{title}     || 'N/A';
+        my $rating    = $c->request->params->{rating}    || 'N/A';
+        my $author_id = $c->request->params->{author_id} || '1';
+    
+        # Create the book
+        my $book = $c->model('MyAppDB::Book')->create({
+                title   => $title,
+                rating  => $rating,
+            });
+        # Handle relationship with author
+        $book->add_to_book_authors({author_id => $author_id});
+    
+        # Store new model object in stash
+        $c->stash->{book} = $book;
+    
+        # Avoid Data::Dumper issue mention earlier
+        # You can probably omit this    
+        $Data::Dumper::Useperl = 1;
+    
+        # Set the TT template to use
+        $c->stash->{template} = 'books/create_done.tt2';
+    }
+
+
+=head2 Test Out The Form
+
+If the application is still running from before, use C<Ctrl-C> to kill it.  Then restart the server:
+
+    $ script/myapp_server.pl
+
+Point your browser to L<http://localhost:3000/books/form_create> and enter "TCP/IP Illustrated, Vol 3" for the title, a rating of 5, and an author ID of 4.  You should then be forwarded to the same C<create_done.tt2> template seen in earlier examples.  Finally, click "Return to list" to view the full list of books.
+
+B<Note:> Having the user enter the primary key ID for the author is obviously a bit crude; we will address this concern with a drop-down list in Part 8.
+
+
+
+=head1 A SIMPLE DELETE FEATURE
+
+Turning out attention to the delete portion of CRUD, this section illustrates some basic techniques that can be used to remove information from the database.
+
+
+=head2 Include a Delete Link in the List
+
+Edit C<root/src/books/list.tt2> and update it to the following (two sections have changed: 1) the additional '<th>Links</th>' table header, and 2) the four lines for the Delete link near the bottom).
+
+    [% # This is a TT comment.  The '-' at the end "chomps" the newline.  You won't -%]
+    [% # see this "chomping" in your browser because HTML ignores blank lines, but  -%]
+    [% # it WILL eliminate a blank line if you view the HTML source.  It's purely   -%]
+    [%- # optional, but both the beginning and the ending TT tags support chomping. -%]
+    
+    [% # Provide a title to root/lib/site/header -%]
+    [% META title = 'Book List' -%]
+    
+    <table>
+    <tr><th>Title</th><th>Rating</th><th>Author(s)</th><th>Links</th></tr>
+    [% # Display each book in a table row %]
+    [% FOREACH book IN books -%]
+      <tr>
+        <td>[% book.title %]</td>
+        <td>[% book.rating %]</td>
+        <td>
+          [% # Print author count in parens. 'book.authors' uses the 'many_to_many' -%]
+          [% # relationship to retrieve all of the authors of a book. 'size' is a   -%]
+          [% # TT VMethod to get the number of elements in a list.                  -%]
+          ([% book.authors.size %])
+          [% # Use an alternate form of a FOREACH loop to display authors.          -%]
+          [% # _ below is the TT string concatenation operator.                     -%]
+          [% author.last_name _' ' FOREACH author = book.authors %]
+          [% # Note: if many_to_many relationship not used in Authors.pm, you could -%]
+          [% # have used the following to 'walk' through the 'join table objects'   -%]
+          [% # bk_author.author.last_name _' ' FOREACH bk_author = book.book_authors %]
+        </td>
+        <td>
+          [% # Add a link to delete a book %]
+          <a href="[% Catalyst.uri_for('delete/') _ book.id %]">Delete</a>
+        </td>
+      </tr>
+    [% END -%]
+    </table>
+
+The additional code is obviously designed to add a new column to the right side of the table with a C<Delete> "button" (for simplicity, links will be used instead of full HTML buttons).
+
+
+=head2 Add a Delete Action to the Controller
+
+Open C<lib/MyApp/Controller/Books.pm> in your editor and add the following method:
+
+    =head2 Delete 
+    
+    Delete a book
+        
+    =cut
+    
+    sub delete : Local {
+        # $id = primary key of book to delete
+        my ($self, $c, $id) = @_;
+    
+        # Search for the book and then delete it
+        $c->model('MyAppDB::Book')->search({id => $id})->delete_all;
+    
+        # Set a status message to be displayed at the top of the view
+        $c->stash->{status_msg} = "Book deleted.";
+    
+        # Forward to the list action/method in this controller
+        $c->forward('list');
+    }
+
+This method first deletes the book with the specified primary key ID.  However, it also removes the corresponding entry from the C<book_authors> table.  Note that C<delete_all> was used instead of C<delete>: whereas C<delete_all> also removes the join table entries in C<book_authors>, C<delete> does not.
+
+Then, rather than forwarding to a "delete done" page as we did with the earlier create example, it simply sets the C<status_msg> to display a notification to the user as the normal list view is rendered.  
+
+The C<delete> action uses the context C<forward> method to return the user to the book list.  The C<detach> method could have also been used.  Whereas C<forward> I<returns> to the original action once it is completed, C<detach> does I<not> return.  Other than that, the two are equivalent.
+
+Another alternative to C<forward> would be to use C<$c-E<gt>response-E<gt>redirect($c-E<gt>uri_for('/books/list'))>.  The C<forward> and C<redirect> operations differ in several important respects that stem from the fact that redirects cause the client browser to issue an entirely new HTTP request.  In doing so, this results in a new URL showing in the browser window.  And, because the stash information is reset for every request, the "Book deleted" message would not be displayed.
+
+
+=head2 Try the Delete Feature
+
+If the application is still running from before, use C<Ctrl-C> to kill it.  Then restart the server:
+
+    $ script/myapp_server.pl
+
+Then point your browser to L<http://localhost:3000/books/list> and click the "Delete" link next to "TCPIP_Illustrated_Vol-2".  A green "Book deleted" status message should display at the top of the page, along with a list of the six remaining books.
+
+
+
+=head1 AUTHOR
+
+Kennedy Clark, C<hkclark@gmail.com>
+
+Please report any errors, issues or suggestions to the author.
+
+Copyright 2006, Kennedy Clark. All rights reserved.
+
+This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+Version: .94
+
diff --git a/lib/Catalyst/Manual/Tutorial/CatalystBasics.pod b/lib/Catalyst/Manual/Tutorial/CatalystBasics.pod
new file mode 100644 (file)
index 0000000..f378f13
--- /dev/null
@@ -0,0 +1,752 @@
+=head1 NAME
+
+Catalyst::Manual::Tutorial::CatalystBasics - Catalyst Tutorial – Part 2: Catalyst Application Development Basics
+
+
+
+=head1 OVERVIEW
+
+This is B<Part 2 of 9> for the Catalyst tutorial.
+
+L<Totorial Overview|Catalyst::Manual::Tutorial>
+
+=over 4
+
+=item 1
+
+L<Introduction|Catalyst::Manual::Tutorial::Intro>
+
+=item 2
+
+B<Catalyst Basics>
+
+=item 3
+
+L<Basic CRUD|Catalyst::Manual::Tutorial03_BasicCRUD>
+
+=item 4
+
+L<Authentication|Catalyst::Manual::Tutorial::Authentication>
+
+=item 5
+
+L<Authorization|Catalyst::Manual::Tutorial::Authorization>
+
+=item 6
+
+L<Debugging|Catalyst::Manual::Tutorial::Debugging>
+
+=item 7
+
+L<Testing|Catalyst::Manual::Tutorial::Testing>
+
+=item 8
+
+L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
+
+=item 9
+
+L<Appendicies|Catalyst::Manual::Tutorial::Appendicies>
+
+=back
+
+
+
+=head1 DESCRIPTION
+
+In this part of the tutorial, we will create a very basic Catalyst web application.  Though simple in many respects, this section will already demonstrate a number of powerful capabilities such as:
+
+=over 4
+
+=item * Helper Scripts
+
+Catalyst helper scripts that can be used to rapidly bootstrap the skeletal structure of an application.
+
+=item * MVC
+
+Model/View/Controller (MVC) provides an architecture that facilitates a clean "separation of control" between the different portions of your application.  Given that many other documents cover this subject in detail, MVC will not be discussed in depth here (for an excellent introduction to MVC and general Catalyst concepts, please see L<Catalyst::Manual::About|Catalyst::Manual::About>.  In short:
+
+=over 4
+
+=item * Model
+
+In most applications, the model equates to the objects that are created from and saved to your SQL database.
+
+=item * View
+
+The view takes model objects and renders them into something for the end user to look at.  Normally this involves a template-generation tool that creates HTML for the user's web browser, but it could easily be code that generates other forms such as PDF documents or Excel spreadsheets.
+
+=item * Controller
+
+As suggested by its name, the controller takes user requests and routes them to the necessary model and view.
+
+=back
+
+=item * ORM
+
+The use Object-Relational Mapping (ORM) technology for database access (specifically, ORM provides an automated means to persist and restore objects to/from a relational database).
+
+=back
+
+B<TIP>: Note that all of the code for this part of the tutorial can be pulled from the Catalyst Subversion repository in one step with the following command:
+
+    svn checkout http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/Tutorial@###
+    IMPORTANT: Does not work yet.  Will be completed for final version.
+
+
+
+=head1 CREATE A CATALYST PROJECT
+
+Catalyst provides a number of helper scripts that can be used to quickly flesh out the basic structure of your application.  All Catalyst projects begin with the C<catalyst.pl> helper.
+
+In the case of this tutorial, use the Catalyst C<catalyst.pl> script to initialize the framework for an application called C<MyApp>:
+
+    $ catalyst.pl MyApp
+    $ cd MyApp
+
+The C<catalyst.pl> helper script will display the names of the directories and files it creates.
+
+Though it's obviously too early for any significant celebration, we already have a functioning application.  Run the following command to run this application with the built-in development web server:
+
+       $ script/myapp_server.pl
+
+Point your web browser to L<http://localhost:3000> (substituting a different hostname or IP address as appropriate) and you should be greeted by the Catalyst welcome screen.  Press Ctrl-C to break out of the development server.
+
+
+
+=head1 CREATE A SQLITE DATABASE
+
+In this step, we make a text file with the required SQL commands to create a database table and load some sample data.  Open C<myapp01.sql> in your editor and enter:
+
+    --
+    -- Create a very simple database to hold book and author information
+    --
+    CREATE TABLE books (
+            id          INTEGER PRIMARY KEY,
+            title       TEXT ,
+            rating      INTEGER
+    );
+    -- 'book_authors' is a many-to-many join table between books & authors
+    CREATE TABLE book_authors (
+            book_id     INTEGER,
+            author_id   INTEGER,
+            PRIMARY KEY (book_id, author_id)
+    );
+    CREATE TABLE authors (
+            id          INTEGER PRIMARY KEY,
+            first_name  TEXT,
+            last_name   TEXT
+    );
+    ---
+    --- Load some sample data
+    ---
+    INSERT INTO books VALUES (1, 'CCSP SNRS Exam Certification Guide', 5);
+    INSERT INTO books VALUES (2, 'TCP/IP Illustrated, Volume 1', 5);
+    INSERT INTO books VALUES (3, 'Internetworking with TCP/IP Vol.1', 4);
+    INSERT INTO books VALUES (4, 'Perl Cookbook', 5);
+    INSERT INTO books VALUES (5, 'Designing with Web Standards', 5);
+    INSERT INTO authors VALUES (1, 'Greg', 'Bastien');
+    INSERT INTO authors VALUES (2, 'Sara', 'Nasseh');
+    INSERT INTO authors VALUES (3, 'Christian', 'Degu');
+    INSERT INTO authors VALUES (4, 'Richard', 'Stevens');
+    INSERT INTO authors VALUES (5, 'Douglas', 'Comer');
+    INSERT INTO authors VALUES (6, 'Tom', 'Christiansen');
+    INSERT INTO authors VALUES (7, ' Nathan', 'Torkington');
+    INSERT INTO authors VALUES (8, 'Jeffrey', 'Zeldman');
+    INSERT INTO book_authors VALUES (1, 1);
+    INSERT INTO book_authors VALUES (1, 2);
+    INSERT INTO book_authors VALUES (1, 3);
+    INSERT INTO book_authors VALUES (2, 4);
+    INSERT INTO book_authors VALUES (3, 5);
+    INSERT INTO book_authors VALUES (4, 6);
+    INSERT INTO book_authors VALUES (4, 7);
+    INSERT INTO book_authors VALUES (5, 8);
+
+B<TIP>: See Appendix 1 for tips on removing the leading spaces when cutting and pasting example code from Pod documents.
+
+Then use the following command to build a C<myapp.db> SQLite database:
+
+    $ sqlite3 myapp.db < myapp01.sql
+
+If you need to create the database more than once, you probably want to issue the C<rm myapp.db> command to delete the database before you use the C<sqlite3 myapp.db < myapp01.sql> command.
+
+Once the C<myapp.db> database file has been created and initialized, you can use the SQLite command line environment to do a quick dump of the database contents:
+
+    $ sqlite3 myapp.db
+    SQLite version 3.2.2
+    Enter ".help" for instructions
+    sqlite> select * from books;
+    1|CCSP SNRS Exam Certification Guide|5
+    2|TCP/IP Illustrated, Volume 1|5
+    3|Internetworking with TCP/IP Vol.1|4
+    4|Perl Cookbook|5
+    5|Designing with Web Standards|5
+    sqlite> .q
+    $
+
+Or:
+
+    $ sqlite3 myapp.db "select * from books"
+    1|CCSP SNRS Exam Certification Guide|5
+    2|TCP/IP Illustrated, Volume 1|5
+    3|Internetworking with TCP/IP Vol.1|4
+    4|Perl Cookbook|5
+    5|Designing with Web Standards|5
+
+As with most other SQL tools, if you are using the full "interactive" environment you need to terminate your SQL commands with a ";" (it's not required if you do a single SQL statement on the command line).  Use ".q" to exit from SQLite from the SQLite interactive mode and return to your OS command prompt.
+
+
+
+=head1 EDIT THE LIST OF CATALYST PLUGINS
+
+One of the greatest benefits of Catalyst is that it has such a large library of plugins available.  Plugins are used to seamlessly integrate existing Perl modules into the overall Catalyst framework.  In general, they do this by adding additional methods to the C<context> object (generally written as C<$c>) that Catalyst passes to every component throughout the framework.
+
+By default, Catalyst enables three plugins/flags:
+
+=over 4
+
+=item * 
+
+C<-Debug> Flag
+
+Enables the Catalyst debug output you saw when we started the C<script/myapp_server.pl> development server earlier.  You can remove this plugin when you place your application into production.
+
+As you may have noticed, C<-Debug> is not a plugin, but a I<flag>.  Although most of the items specified on the C<use Catalyst> line of your application class will be plugins, Catalyst supports a limited number of flag options (of these, C<-Debug> is the most common).  
+
+If you prefer, you can use the C<$c-E<gt>debug> method to enable debug messages.
+
+=item *
+
+L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader>
+
+C<ConfigLoader> provides an automatic way to load your configurable parameters for your application from a central YAML file (versus having the values hard-coded inside your Perl modules).  If you have not been exposed to YAML before, it is a human-readable data serialization format that can be used to read (and write) values to/from text files.  We will see how to use this feature of Catalyst during the authentication and authorization sections (Part 4 and Part 5).
+
+
+=item *
+
+L<Catalyst::Plugin::Static::Simple|Catalyst::Plugin::Static::Simple>
+
+C<Static::Simple> provides an easy method of serving static content such as images and CSS files under the development server.
+
+=back
+
+To modify the list of plugins, edit C<lib/MyApp.pm> (this file is generally referred to as your I<application class>) and delete the line with:
+
+    use Catalyst qw/-Debug ConfigLoader Static::Simple/;
+
+Replace it with:
+
+    use Catalyst qw/
+            -Debug
+            ConfigLoader
+            Static::Simple
+            
+            Dumper
+            StackTrace
+            DefaultEnd
+            /;
+
+This tells Catalyst to start using three new plugins:
+
+=over 4
+
+=item * 
+
+L<Catalyst::Plugin::Dumper|Catalyst::Plugin::Dumper>
+
+Allows you to easily use L<Data::Dumper|Data::Dumper> to dump variables to the logs, for example:
+
+    $c->log->dumper($myvar);
+
+When running your application under the development server, the logs will be printed to your screen along with the other debug information generated by the C<-Debug> flag.
+
+=item * 
+
+L<Catalyst::Plugin::StackTrace|Catalyst::Plugin::StackTrace>
+
+Adds a stack trace to the standard Catalyst "debug screen" (this is the screen Catalyst sends to your browser when an error occurs).
+
+Note: L<Dumper|Catalyst::Plugin::Dumper> output appears on the console/telnet/SSH window where you issue the C<script/myapp_server.pl> command.  L<StackTrace|Catalyst::Plugin::StackTrace> output appears in your browser.
+
+=item * 
+
+L<Catalyst::Plugin::DefaultEnd|Catalyst::Plugin::DefaultEnd>
+
+Automatically provides a Catalyst "end action" that invokes your view at the end of each request.  Also allows you to add "dump_info=1" (precede with "?" or "&" depending on where it is in the URL) to I<force> the debug screen at the end of the Catalyst request processing cycle.  
+
+TIP: Many Catalyst-related documents predate L<DefaultEnd|Catalyst::Plugin::DefaultEnd> and suggest that you add an C<end> action to your application class (C<MyApp.pm>) or Root.pm (C<MyApp/Controller/Root.pm>).  In most of these cases, you can convert to L<DefaultEnd|Catalyst::Plugin::DefaultEnd> by deleting the C<end> action and using the plugin instead.
+
+=back
+
+Note that when specifying plugins on the C<use Catalyst> line, you can omit C<Catalyst::Plugin> from the name.  Additionally, you can spread the plugin names across multiple lines as shown here, or place them all on one (or more) lines as with the default configuration.
+
+
+
+=head1 DATABASE ACCESS WITH C<DBIx::Class>
+
+Catalyst can be used with virtually any form of persistent datastore available via Perl.  For example, L<Catalyst::Model::DBI|Catalyst::Model::DBI> can be used to easily access databases through the traditional Perl DBI interface.  However, most Catalyst applications use some form of ORM technology to automatically create and save model objects as they are used.  Although Tony Bowden's L<Class::DBI|Class::DBI> has been the traditional Perl ORM engine, Matt Trout's L<DBIx::Class|DBIx::Class> (abbreviated as "DBIC") has rapidly emerged as the Perl-based ORM technology of choice.  Most new Catalyst applications rely on DBIC, as will this tutorial.
+
+Note: See L<Catalyst::Model::CDBI| Catalyst:: Model::CDBI > for more information on using Catalyst with L<Class::DBI|Class::DBI>.  Catalyst can also be used with "plain old DBI"; see L<Catalyst::Model::DBI| Catalyst::Model::DBI>.
+
+
+=head2 Create a DBIC Schema File
+
+DBIx::Class uses a schema file to load other classes that represent the tables in your database (DBIC refers to these "table objects" as "result sources," see L<DBIx::Class::ResultSource|DBIx::Class::ResultSource>).  In this case, we want to load the model object for the C<books>, C<book_authors>, and C<authors> tables created in the previous step.
+
+Open C<lib/MyAppDB.pm> in your editor and insert:
+
+    package MyAppDB;
+    
+    =head1 NAME 
+    
+    MyAppDB -- DBIC Schema Class
+    
+    =cut
+    
+    # Our schema needs to inherit from 'DBIx::Class::Schema'
+    use base qw/DBIx::Class::Schema/;
+    
+    # Need to load the DB Model classes here.
+    # You can use this syntax if you want:
+    #    __PACKAGE__->load_classes(qw/Book BookAuthor Author/);
+    # Also, if you simply want to load all of the classes in a directory
+    # of the same name as your schema class (as we do here) you can use:
+    #    __PACKAGE__->load_classes(qw//);
+    # But the variation below is more flexible in that it can be used to 
+    # load from multiple namespaces.
+    __PACKAGE__->load_classes({
+        MyAppDB => [qw/Book BookAuthor Author/]
+    });
+    
+    1;
+
+B<Note:> C<__PACKAGE__> is just a shorthand way of referencing the name of the package where it is used.  Therefore, in C<MyAppDB.pm>, C<__PACKAGE> is equivalent to C<MyAppDB>
+
+
+=head2 Create the DBIC "Result Source" Files
+
+In this step, we create "table classes" (again, these are called a "result source" classes in DBIC) that acts as model objects for the C<books>, C<book_authors>, and C<authors> tables in our database.
+
+First, create a directory to hold the class:
+
+    $ mkdir lib/MyAppDB
+
+Then open C<lib/MyAppDB/Book.pm> in your editor and enter:
+
+    package MyAppDB::Book;
+    
+    use base qw/DBIx::Class/;  
+    
+    # Load required DBIC stuff
+    __PACKAGE__->load_components(qw/PK::Auto Core/);
+    # Set the table name
+    __PACKAGE__->table('books');
+    # Set columns in table
+    __PACKAGE__->add_columns(qw/id title rating/);
+    # Set the primary key for the table
+    __PACKAGE__->set_primary_key(qw/id/);
+    
+    #
+    # Set relationships:
+    #
+    
+    # has_many():
+    #   args:
+    #     1) Name of relationship, DBIC will create accessor with this name
+    #     2) Name of the model class referenced by this relationship
+    #     3) Column name in *foreign* table
+    __PACKAGE__->has_many(book_authors => 'MyAppDB::BookAuthor', 'book_id');
+    
+    # many_to_many():
+    #   args:
+    #     1) Name of relationship, DBIC will create accessor with this name
+    #     2) Name of has_many() relationship this many_to_many() is shortcut for
+    #     3) Name of belongs_to() relationship in model class of has_many() above 
+    #   You must already have the has_many() defined to use a many_to_many().
+    __PACKAGE__->many_to_many(authors => 'book_authors', 'author');
+    
+    
+    =head1 NAME
+    
+    MyAppDB::Book - A model object representing a book.
+    
+    =head1 DESCRIPTION
+    
+    This is an object that represents a row in the 'books' table of your application
+    database.  It uses DBIx::Class (aka, DBIC) to do ORM.
+    
+    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
+    Offline utilities may wish to use this class directly.
+    
+    =cut
+    
+    1;
+
+This defines both a C<has_many> and a C<many_to_many> relationship.  The C<many_to_many> relationship is optional, but it makes it easier to map a book to its collection of authors.  Without it, we would have to "walk" though the C<book_authors> table as in C<$book-E<gt>book_authors-E<gt>first-E<gt>author-E<gt>last_name> (we will see examples on how to use DBIC objects in your code soon, but note that because C<$book-E<gt>book_authors> can return multiple authors, we have to use C<first> to display a single author).  C<many_to_many> allows us to use the shorter C<$book-E<gt>authors-E<gt>first-E<gt>last_name>.  Note that you cannot define a C<many_to_many> relationship without also having the C<has_many> relationship in place.
+
+Next, open C<lib/MyAppDB/Author.pm> in your editor and enter:
+
+    package MyAppDB::Author;
+    
+    use base qw/DBIx::Class/;
+    
+    # Load required DBIC stuff
+    __PACKAGE__->load_components(qw/PK::Auto Core/);
+    # Set the table name
+    __PACKAGE__->table('authors');
+    # Set columns in table
+    __PACKAGE__->add_columns(qw/id first_name last_name/);
+    # Set the primary key for the table
+    __PACKAGE__->set_primary_key(qw/id/);
+
+    #
+    # Set relationships:
+    #
+    
+    # has_many():
+    #   args:
+    #     1) Name of relationship, DBIC will create accessor with this name
+    #     2) Name of the model class referenced by this relationship
+    #     3) Column name in *foreign* table
+    __PACKAGE__->has_many(book_author => 'MyAppDB::BookAuthor', 'author_id');
+    
+    # many_to_many():
+    #   args:
+    #     1) Name of relationship, DBIC will create accessor with this name
+    #     2) Name of has_many() relationship this many_to_many() is shortcut for
+    #     3) Name of belongs_to() relationship in model class of has_many() above 
+    #   You must already have the has_many() defined to use a many_to_many().
+    __PACKAGE__->many_to_many(books => 'book_author', 'book');
+    
+    
+    =head1 NAME
+    
+    MyAppDB::Author - A model object representing an author of a book (if a book has 
+    multiple authors, each will be represented be separate Author object).
+    
+    =head1 DESCRIPTION
+    
+    This is an object that represents a row in the 'authors' table of your application
+    database.  It uses DBIx::Class (aka, DBIC) to do ORM.
+    
+    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
+    Offline utilities may wish to use this class directly.
+    
+    =cut
+    
+    1;
+
+Finally, open C<lib/MyAppDB/BookAuthor.pm> in your editor and enter:
+
+    package MyAppDB::BookAuthor;
+    
+    use base qw/DBIx::Class/;
+    
+    # Load required DBIC stuff
+    __PACKAGE__->load_components(qw/PK::Auto Core/);
+    # Set the table name
+    __PACKAGE__->table('book_authors');
+    # Set columns in table
+    __PACKAGE__->add_columns(qw/book_id author_id/);
+    # Set the primary key for the table
+    __PACKAGE__->set_primary_key(qw/book_id author_id/);
+    
+    #
+    # Set relationships:
+    #
+    
+    # belongs_to():
+    #   args:
+    #     1) Name of relationship, DBIC will create accessor with this name
+    #     2) Name of the model class referenced by this relationship
+    #     3) Column name in *this* table
+    __PACKAGE__->belongs_to(book => 'MyAppDB::Book', 'book_id');
+
+    # belongs_to():
+    #   args:
+    #     1) Name of relationship, DBIC will create accessor with this name
+    #     2) Name of the model class referenced by this relationship
+    #     3) Column name in *this* table
+    __PACKAGE__->belongs_to(author => 'MyAppDB::Author', 'author_id');
+    
+    
+    =head1 NAME
+    
+    MyAppDB::BookAuthor - A model object representing the JOIN between an author and 
+    a book.
+    
+    =head1 DESCRIPTION
+    
+    This is an object that represents a row in the 'book_authors' table of your 
+    application database.  It uses DBIx::Class (aka, DBIC) to do ORM.
+
+    You probably won't need to use this class directly -- it will be automatically
+    used by DBIC where joins are needed.
+    
+    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
+    Offline utilities may wish to use this class directly.
+    
+    =cut
+    
+    1;
+
+B<Note:> This sample application uses a plural form for the database tables (e.g., C<books> and C<authors>) and a singular form for the model objects (e.g., C<Book> and C<Author>); however, Catalyst places no restrictions on the naming conventions you wish to use.
+
+
+=head2 Use C<Catalyst::Model::DBIC::Schema> To Load The Model Class
+
+When L<Catalyst::Model::DBIC::Schema|Catalyst::Model::DBIC::Schema> is in use, Catalyst essentially reads an existing copy of your database model and creates a new set of objects under C<MyApp::Model> for use inside of Catalyst.
+
+B<Note:> With L<Catalyst::Model::DBIC::Schema|Catalyst::Model::DBIC::Schema> you essentially end up with two sets of model classes (only one of which you write... the other set is created automatically in memory when your Catalyst application initializes).  For this tutorial application, the important points to remember are: you write the I<result source> files in C<MyAppDB>, but I<within Catalyst> you use the I<automatically created model classes> in C<MyApp::Model>.
+
+Use the L<Catalyst::Helper::Model::DBIC::Schema| Catalyst::Helper::Model::DBIC::Schema > helper script to create the model class that loads up the model we created in the previous step:
+
+    $ script/myapp_create.pl model MyAppDB DBIC::Schema MyAppDB dbi:SQLite:myapp.db '' '' '{ AutoCommit => 1 }'
+
+Where the first C<MyAppDB> is the name of the class to be created by the helper in C<lib/MyApp/Model> and the second C<MyAppDB> is the name of existing schema file we created (in C<lib/MyAppDB.pm>).  You can see that the helper creates a model file under C<lib/MyApp/Model> (Catalyst has a separate directory under C<lib/MyApp> for each of the three parts of MVC: C<Model>, C<View>, and C<Controller> [although older Catalyst applications often use the directories C<M>, C<V>, and C<C>]).
+
+
+
+=head1 CREATE A CATALYST CONTROLLER
+
+Controllers are where you write methods that respond to C<GET> and C<POST> messages from the user's web browser.
+
+Use the Catalyst C<create> script to add a controller for book-related actions:
+
+    $ script/myapp_create.pl controller Books
+
+Then edit C<lib/MyApp/Controller/Books.pm> and add the following method to the controller:
+
+    =head2 list
+    
+    Fetch all book objects and pass to books/list.tt2 in stash to be displayed
+    
+    =cut
+     
+    sub list : Local {
+        # Retrieve the usual perl OO '$self' for this object. $c is the Catalyst
+        # 'Context' that's used to 'glue together' the various components
+        # that make up the application
+        my ($self, $c) = @_;
+    
+        # Retrieve all of the book records as book model objects and store in the
+        # stash where they can be accessed by the TT template
+        $c->stash->{books} = [$c->model('MyAppDB::Book')->all];
+        
+        # Set the TT template to use.  You will almost always want to do this
+        # in your action methods.
+        $c->stash->{template} = 'books/list.tt2';
+    }
+
+B<Note:> Programmers experienced with object-oriented Perl should recognize C<$self> as a reference to the object where this method was called.  On the other hand, C<$c> will be new to many Perl programmers who have not used Catalyst before (it's sometimes written as C<$context>).  The Context object is automatically passed to all Catalyst components.  It is used to pass information between components and provide access to Catalyst and plugin functionality.
+
+B<TIP>: You may see the C<$c-E<gt>model('MyAppDB::Book')> used above written as C<$c-E<gt>model('MyAppDB')-E<gt>resultset('Book)>.  The two are equivalent.
+
+B<Note:> Catalyst actions are regular Perl methods, but they make use of Nicholas Clark's C<attributes> module to provide additional information to the Catalyst dispatcher logic.
+
+
+=head1 CATALYST VIEWS
+
+Views are where you render output for display in the user's web browser (or possibly using other display technology).  As with virtually every aspect of Catalyst, options abound when it comes to the specific view technology you adopt inside your application.  However, most Catalyst applications use the Template Toolkit, known as TT (for more information on TT, see L<http://www.template-toolkit.org>).  Other popular View technologies include Mason (L<http://www.masonhq.com> and L<http://www.masonbook.com>) and L<HTML::Template|HTML::Template> (L<http://html-template.sourceforge.net>).
+
+
+=head2 Create a Catalyst View Using C<TTSITE>
+
+When using TT for the Catalyst view, there are two main helper scripts:
+
+=over 4
+
+=item *
+
+L<Catalyst::Helper::View::TT|Catalyst::Helper::View::TT>
+
+=item *
+
+L<Catalyst::Helper::View::TTSite|Catalyst::Helper::View::TTSite>
+
+=back
+
+Both are similar, but C<TT> merely creates the C<lib/MyApp/View/TT.pm> file and leaves the creation of any hierarchical template organization entirely up to you (it also creates a C<t/view_TT.t> file for testing; test cases will be discussed in Part 7).  Conversely, the C<TTSite> helper creates a modular and hierarchical view layout with separate Template Toolkit (TT) files for common header and footer information, configuration values, a CSS stylesheet, etc.
+
+Enter the following command to enable the C<TTSite> style of view rendering for the tutorial:
+
+    $ script/myapp_create.pl view TT TTSite
+
+This puts a number of files in the C<root/lib> and C<root/src> directories that can be used to customize the look and feel of your application.  Also take a look at C<lib/MyApp/View/TT.pm> for config values set by the C<TTSite> helper.
+
+B<TIP>: Note that TTSite does one thing that could confuse people who are used to the normal C<TT> Catalyst View: it redefines the Catalyst context object in templates from its usual C<c> to C<Catalyst>.  Also keep this in mind when looking at other Catalyst examples (they almost always use C<c>).  Note that Catalyst and TT I<do not complain> when you use the wrong name to access the context... it simply outputs blanks for that bogus logic.  Finally, be aware that this change in name I<only> applies to how the context object is accessed inside your TT templates, your controllers will continue to use C<$c> (or whatever name you use when fetching the reference from C<@_> inside your methods).  (You can change back to the "default" behavior be removing the C<CATALYST_VAR> line from C<lib/MyApp/View/TT.pm>, but you will also have to edit C<root/lib/config/main> and C<root/lib/config/url>.  If you do this, be careful not to have a collision between your own C<c> variable and the Catalyst C<c> variable.)
+
+
+
+=head2 Globally Customize Every View
+
+When using TTSite, files in the subdirectories of C<root/lib> can be used to make changes that will appear in every view.  For example, to display optional status and error messages in every view, edit C<root/lib/site/layout> update it to match the following (the two HTML C<span> elements are new):
+
+    <div id="header">[% PROCESS site/header %]</div>
+    
+    <div id="content">
+    <span class="message">[% status_msg %]</span>
+    <span class="error">[% error_msg %]</span>
+    [% content %]
+    </div>
+    
+    <div id="footer">[% PROCESS site/footer %]</div>
+
+If we set either message in the Catalyst stash (e.g., C<$c-E<gt>stash-E<gt>{status_msg} = 'Hello world'>) it will be displayed whenever any view used by that request is rendered.  The C<message> and C<error> CSS styles are automatically defined in C<root/src/ttsite.css> and can be customized to suit your needs.
+
+B<Note:> The Catalyst stash only lasts for a single HTTP request.  If you need to retain information across requests you can use L<Catalyst::Plugin::Session|Catalyst::Plugin::Session> (we will use Catalyst sessions in the Authentication part).
+
+
+=head2 Create a TT Template Page
+
+To add a new page of content to the TTSite view hierarchy, just create a new C<.tt2> file in C<root/src>.  Only include HTML markup that goes inside the HTML <body> and </body> tags, TTSite will use the contents of C<root/lib/site> to add the top and bottom.
+
+First create a directory for book-related TT templates:
+
+    $ mkdir root/src/books
+
+Then open C<root/src/books/list.tt2> in your editor and enter:
+
+    [% # This is a TT comment.  The '-' at the end "chomps" the newline.  You won't -%]
+    [% # see this "chomping" in your browser because HTML ignores blank lines, but  -%]
+    [% # it WILL eliminate a blank line if you view the HTML source.  It's purely   -%]
+    [%- # optional, but both the beginning and the ending TT tags support chomping. -%]
+    
+    [% # Provide a title to root/lib/site/header -%]
+    [% META title = 'Book List' -%]
+    
+    <table>
+    <tr><th>Title</th><th>Rating</th><th>Author(s)</th></tr>
+    [% # Display each book in a table row %]
+    [% FOREACH book IN books -%]
+      <tr>
+        <td>[% book.title %]</td>
+        <td>[% book.rating %]</td>
+        <td>
+          [% # Print author count in parens. 'book.authors' uses the 'many_to_many' -%]
+          [% # relationship to retrieve all of the authors of a book. 'size' is a   -%]
+          [% # TT VMethod to get the number of elements in a list.                  -%]
+          ([% book.authors.size %])
+          [% # Use an alternate form of a FOREACH loop to display authors.          -%]
+          [% # _ below is the TT string concatenation operator.                     -%]
+          [% author.last_name _' ' FOREACH author = book.authors %]
+          [% # Note: if many_to_many relationship not used in Authors.pm, you could -%]
+          [% # have used the following to 'walk' through the 'join table objects'   -%]
+          [% # bk_author.author.last_name _' ' FOREACH bk_author = book.book_authors %]
+        </td>
+      </tr>
+    [% END -%]
+    </table>
+
+As indicated by the inline comments above, the C<META title> line uses TT's META feature to provide a title to C<root/lib/site/header>.  Meanwhile, the outer C<FOREACH> loop iterates through each C<book> model object and prints the C<title> and C<rating> fields.  An inner C<FOREACH> loop prints the last name of each author in a single table cell (a simple space is used between the names; in reality you would probably want to modify the code to use a comma as a separator).  
+
+If you are new to TT, the [% and %] tags are used to delimit "variable text".  TT supports a wide variety of directives for "calling" other files, looping, conditional logic, etc.  In general, TT simplifies the usual range of Perl operators down to the single dot (C<.>) operator.  This applies to operations as diverse as method calls, hash lookups, and list index values (see L<http://www.template-toolkit.org/docs/default/Manual/Variables.html> for details and examples).  In addition to the usual C<Template> module Pod documentation, you can access the TT manual at L<http://www.template-toolkit.org/docs/default/>.
+
+B<NOTE>: The C<TTSite> helper creates several TT files using an extension of C<.tt2>. Most other Catalyst and TT examples use an extension of C<.tt>.  You can use either extension (or no extension at all) with TTSite and TT, just be sure to use the appropriate extension for both the file itself I<and> the C<$c-E<gt>stash-E<gt>{template} = ...> line in your controller.  This document will use C<.tt2> for consistency with the files already created by the C<TTSite> helper.
+
+
+
+=head1 RUN THE APPLICATION
+
+First, let's enable an environment variable option that causes DBIx::Class to dump the SQL statements it's using to access the database (this option can provide extremely helpful troubleshooting information):
+
+    $ export DBIX_CLASS_STORAGE_DBI_DEBUG=1
+
+B<NOTE>: You can also set this in your code using C<$class-E<gt>storage-E<gt>debug(1);>.  See L<DBIx::Class::Manual::Troubleshooting|DBIx::Class::Manual::Troubleshooting> for details (including options to log to file vs. the Catalyst development server log.
+
+Then run the Catalyst "demo server" script:    
+
+    $ script/myapp_server.pl
+
+You should get something like this:
+
+    $ script/myapp_server.pl
+    [Tue May 16 12:51:33 2006] [catalyst] [debug] Debug messages enabled
+    [Tue May 16 12:51:33 2006] [catalyst] [debug] Loaded plugins:
+    .------------------------------------------------------------------------------.
+    | Catalyst::Plugin::ConfigLoader 0.07                                          |
+    | Catalyst::Plugin::Static::Simple 0.14                                        |
+    | Catalyst::Plugin::Dumper 0.000002                                            |
+    | Catalyst::Plugin::StackTrace 0.04                                            |
+    | Catalyst::Plugin::DefaultEnd 0.06                                            |
+    '------------------------------------------------------------------------------'
+    
+    [Tue May 16 12:51:33 2006] [catalyst] [debug] Loaded dispatcher "Catalyst::Dispatcher"
+    [Tue May 16 12:51:33 2006] [catalyst] [debug] Loaded engine "Catalyst::Engine::HTTP"
+    [Tue May 16 12:51:33 2006] [catalyst] [debug] Found home "/home/me/MyApp"
+    [Tue May 16 12:51:37 2006] [catalyst] [debug] Loaded components:
+    .-------------------------------------------------------------------+----------.
+    | Class                                                             | Type     |
+    +-------------------------------------------------------------------+----------+
+    | MyApp::Controller::Books                                          | instance |
+    | MyApp::Controller::Root                                           | instance |
+    | MyApp::Model::MyAppDB                                             | instance |
+    | MyApp::Model::MyAppDB::Author                                     | class    |
+    | MyApp::Model::MyAppDB::Book                                       | class    |
+    | MyApp::Model::MyAppDB::BookAuthor                                 | class    |
+    | MyApp::View::TT                                                   | instance |
+    '-------------------------------------------------------------------+----------'
+    
+    [Tue May 16 12:51:37 2006] [catalyst] [debug] Loaded Private actions:
+    .----------------------+----------------------------------------+--------------.
+    | Private              | Class                                  | Method       |
+    +----------------------+----------------------------------------+--------------+
+    | /default             | MyApp::Controller::Root                | default      |
+    | /end                 | MyApp                                  | end          |
+    | /books/list          | MyApp::Controller::Books               | list         |
+    '----------------------+----------------------------------------+--------------'
+    
+    [Tue May 16 12:51:37 2006] [catalyst] [debug] Loaded Path actions:
+    .--------------------------------------+---------------------------------------.
+    | Path                                 | Private                               |
+    +--------------------------------------+---------------------------------------+
+    | /books/list                          | /books/list                           |
+    '--------------------------------------+---------------------------------------'
+    
+    [Tue May 16 12:51:37 2006] [catalyst] [info] MyApp powered by Catalyst 5.6902
+    You can connect to your server at http://localhost:3000
+
+Some things you should note in the output above:
+
+=over 4
+
+=item * 
+
+Catalyst::Model::DBIC::Schema took our C<MyAppDB::Book> and made it C<MyApp::Model::MyAppDB::Book> (and similar actions were performed on C<MyAppDB::Author> and C<MyAppDB::BookAuthor>).
+
+=item * 
+
+The "list" action in our Books controller showed up with a path of C</books/list>.
+
+=back
+
+
+Point your browser to L<http://localhost:3000> and you should still get the Catalyst welcome page.
+
+Next, to view the book list, change the URL in your browser to L<http://localhost:3000/books/list>. You should get a list of the five books loaded by the C<myapp01.sql> script above, with TTSite providing the formatting for the very simple output we generated in our template.  The count and space-separated list of author last names appear on the end of each row.
+
+Also notice in the output of the C<script/myapp_server.pl> that DBIC used the following SQL to retrieve the data:
+
+    SELECT me.id, me.title, me.rating FROM books me
+
+Along with a list of the following commands to retrieve the authors for each book (the lines have been "word wrapped" here to improve legibility):
+
+    SELECT author.id, author.first_name, author.last_name 
+        FROM book_authors me  
+        JOIN authors author ON ( author.id = me.author_id ) 
+        WHERE ( me.book_id = ? ): `1'
+
+You should see 10 such lines of debug output, two for each of the five author_id values (it pulls the data once for the count logic and another time to actually display the list).
+
+
+
+=head1 AUTHOR
+
+Kennedy Clark, C<hkclark@gmail.com>
+
+Please report any errors, issues or suggestions to the author.
+
+Copyright 2006, Kennedy Clark, under Creative Commons License (L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
+
+Version: .94
+
diff --git a/lib/Catalyst/Manual/Tutorial/Debugging.pod b/lib/Catalyst/Manual/Tutorial/Debugging.pod
new file mode 100644 (file)
index 0000000..59791b4
--- /dev/null
@@ -0,0 +1,218 @@
+=head1 NAME
+
+Catalyst::Manual::Tutorial::Debugging - Catalyst Tutorial – Part 6: Debugging
+
+
+
+=head1 OVERVIEW
+
+This is B<Part 6 of 9> for the Catalyst tutorial.
+
+L<Totorial Overview|Catalyst::Manual::Tutorial>
+
+=over 4
+
+=item 1
+
+L<Introduction|Catalyst::Manual::Tutorial::Intro>
+
+=item 2
+
+L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
+
+=item 3
+
+L<Basic CRUD|Catalyst::Manual::Tutorial03_BasicCRUD>
+
+=item 4
+
+L<Authentication|Catalyst::Manual::Tutorial::Authentication>
+
+=item 5
+
+L<Authorization|Catalyst::Manual::Tutorial::Authorization>
+
+=item 6
+
+B<Debugging>
+
+=item 7
+
+L<Testing|Catalyst::Manual::Tutorial::Testing>
+
+=item 8
+
+L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
+
+=item 9
+
+L<Appendicies|Catalyst::Manual::Tutorial::Appendicies>
+
+=back
+
+
+
+=head1 DESCRIPTION
+
+This part of the tutorial takes a brief look at the primary options available for troubleshooting Catalyst applications.
+
+Note that when it comes to debugging and troubleshooting, there are two camps:
+
+=over 4
+
+=item * 
+
+Fans of C<log> and C<print> statements embedded in the code.
+
+=item * 
+
+Fans of interactive debuggers.
+
+=back
+
+Catalyst is able to easily accommodate both styles of debugging.
+
+
+
+=head1 LOG STATEMENTS
+
+Folks in the former group can use Catalyst's C<$c-E<gt>log> facility.  For example, if you add the following code to a controller action method:
+
+    $c->log->debug("This is a test log message");
+
+Then the Catalyst development server will display your message along with the other debug output.  To accomplish the same thing in a TTSite view use:
+
+    [% Catalyst.log.debug("This is a test log message") %]
+
+You can also use L<Data::Dumper|Data::Dumper> in both Catalyst code 
+(C<$c-E<gt>log-E<gt>dumper($myvar)>) and TT templates (C<[% Dumper.dump(book) %]> as discussed in earlier parts of the tutorial.
+
+
+
+=head1 RUNNING CATALYST UNDER THE PERL DEBUGGER
+
+Members of the interactive debuggers fan club will also be at home with Catalyst applications.  One approach to this style of Perl debugging is to embed breakpoints in your code.  For example, open C<lib/MyApp/Controller/Books.pm> in your editor and add the C<DB::single=1> line as follows inside the C<list> method (I like to "left-justify" my debug statements so I don't forget to remove them, but you can obviously indent them if you prefer):
+
+    sub list : Local {
+        # Retrieve the usual perl OO '$self' for this object. $c is the Catalyst
+        # 'Context' that's used to 'glue together' the various components
+        # that make up the application
+        my ($self, $c) = @_;
+    
+    $DB::single=1;
+            
+        # Retrieve all of the book records as book model objects and store in the
+        # stash where they can be accessed by the TT template
+        $c->stash->{books} = [$c->model('MyAppDB::Book')->all];
+            
+        # Set the TT template to use.  You will almost always want to do this
+        # in your action methods.
+        $c->stash->{template} = 'books/list.tt2';
+    }
+
+This causes the Perl Debugger to enter "single step mode" when this command is 
+encountered (it has no effect when Perl is run without the C<-d> flag).
+
+To now run the Catalyst development server under the Perl debugger, simply 
+prepend C<perl -d> to the front of C<script/myapp_server.pl>:
+
+    $ perl -d script/myapp_server.pl
+
+This will start the interactive debugger and produce output similar to:
+
+    $ perl -d script/myapp_server.pl  
+    
+    Loading DB routines from perl5db.pl version 1.27
+    Editor support available.
+    
+    Enter h or `h h' for help, or `man perldebug' for more help.
+    
+    main::(script/myapp_server.pl:14):      my $debug         = 0;
+    
+      DB<1> 
+
+Press the C<c> key and hit C<Enter> to continue executing the Catalyst development server under the debugger.  Although execution speed will be slightly slower than normal, you should soon see the usual Catalyst startup debug information.
+
+Now point your browser to L<http://localhost:3000/books/list> and log in.  Once the breakpoint is encountered in the C<MyApp::Controller::list> method, the console session running the development server will drop to the Perl debugger prompt:
+
+    MyApp::Controller::Books::list(/home/me/MyApp/script/../lib/MyApp/Controller/Books.pm:40):
+    40:         $c->stash->{books} = [$c->model('MyAppDB::Book')->all];
+    
+      DB<1>
+
+You now have the full Perl debugger at your disposal.  First use the C<next> feature by typing C<n> to execute the C<all> method on the Book model (C<n> jumps over method/subroutine calls; you can also use C<s> to C<single-step> into methods/subroutines):
+
+      DB<1> n
+    SELECT me.id, me.authors, me.title, me.rating FROM books me:
+    MyApp::Controller::Books::list(/home/me/MyApp/script/../lib/MyApp/Controller/Books.pm:44):
+    44:         $c->stash->{template} = 'books/list.tt2';
+    
+      DB<1>
+
+This takes you to the next line of code where the template name is set.  Notice that because we enabled C<DBIX_CLASS_STORAGE_DBI_DEBUG=1> earlier, SQL debug output also shows up in the development server debug output.
+
+Next, list the methods available on our C<Book> model:
+
+      DB<1> m $c->model('MyAppDB::Book')
+    ()
+    (0+
+    (bool
+    MODIFY_CODE_ATTRIBUTES
+    _attr_cache
+    _collapse_result
+    _construct_object
+    _count
+    _result_class_accessor
+    _result_source_accessor
+    all
+    carp
+    <lines removed for brevity>
+    
+      DB<2>
+
+We can also play with the model directly:
+
+      DB<2> x ($c->model('MyAppDB::Book')->all)[1]->title
+    SELECT me.id, me.title, me.rating FROM books me:
+    0  'TCP/IP Illustrated, Volume 1'
+
+This uses the Perl debugger C<x> command to display the title of a book.
+
+Next we inspect the C<books> element of the Catalyst C<stash> (the C<4> argument to the C<x> command limits the depth of the dump to 4 levels):
+
+      DB<3> x 4 $c->stash->{books}
+    0  ARRAY(0xa8f3b7c)
+       0  MyApp::Model::MyAppDB::Book=HASH(0xb8e702c)
+          '_column_data' => HASH(0xb8e5e2c)
+             'id' => 1
+             'rating' => 5
+             'title' => 'CCSP SNRS Exam Certification Guide'
+          '_in_storage' => 1
+    <lines removed for brevity>
+
+Then enter the C<c> command to continue processing until the next breakpoint is hit (or the application exits):
+
+      DB<4> c
+    SELECT author.id, author.first_name, author.last_name FROM ...
+
+Finally, press C<Ctrl+C> to break out of the development server.  Because we are running inside the Perl debugger, you will drop to the debugger prompt.  Press C<q> to exit the debugger and return to your OS shell prompt:
+
+      DB<4> q
+    $
+
+For more information on using the Perl debugger, please see C<perldebug> and C<perldebtut>.  You can also type C<h> or C<h h> at the debugger prompt to view the built-in help screens.
+
+
+
+=head1 AUTHOR
+
+Kennedy Clark, C<hkclark@gmail.com>
+
+Please report any errors, issues or suggestions to the author.
+
+Copyright 2006, Kennedy Clark. All rights reserved.
+
+This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+Version: .94
+
diff --git a/lib/Catalyst/Manual/Tutorial/Intro.pod b/lib/Catalyst/Manual/Tutorial/Intro.pod
new file mode 100644 (file)
index 0000000..881a017
--- /dev/null
@@ -0,0 +1,270 @@
+=head1 NAME
+
+Catalyst::Manual::Tutorial::Intro - Catalyst Tutorial – Part 1: Introduction
+
+
+
+=head1 OVERVIEW
+
+This is B<Part 1 of 9> for the Catalyst tutorial.
+
+L<Totorial Overview|Catalyst::Manual::Tutorial>
+
+=over 4
+
+=item 1
+
+B<Introduction>
+
+=item 2
+
+L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
+
+=item 3
+
+L<Basic CRUD|Catalyst::Manual::Tutorial03_BasicCRUD>
+
+=item 4
+
+L<Authentication|Catalyst::Manual::Tutorial::Authentication>
+
+=item 5
+
+L<Authorization|Catalyst::Manual::Tutorial::Authorization>
+
+=item 6
+
+L<Debugging|Catalyst::Manual::Tutorial::Debugging>
+
+=item 7
+
+L<Testing|Catalyst::Manual::Tutorial::Testing>
+
+=item 8
+
+L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
+
+=item 9
+
+L<Appendicies|Catalyst::Manual::Tutorial::Appendicies>
+
+=back
+
+
+
+=head1 DESCRIPTION
+
+This tutorial provides a nine-part introduction to the Catalyst web framework.  It seeks to provide a rapid overview of many of its most commonly used features.  The focus is on the real-world best practices required in the construction of nearly all Catalyst applications.
+
+Although the primary target of the tutorial is users new to the Catalyst framework, experienced users may wish to review specific sections (for example, how to use DBIC for their model classes or how to add authentication and authorization to an existing application).
+
+Subjects covered include:
+
+=over 4
+
+=item * 
+
+A simple application that lists and adds books.
+
+=item *
+
+The use of C<DBIx::Class> (DBIC) for the model.
+
+=item * 
+
+How to write CRUD (Create, Read, Update and Delete) operations in Catalyst.
+
+=item *
+
+Authentication ("auth").
+
+=item * 
+
+Role-based authorization ("authz").
+
+=item * 
+
+Attempts to provide an example showing current Catalyst 5.70 practices. For example, the use of C<Catalyst::Plugin::DefaultEnd>, DBIC, C<Catalyst::Plugin::ConfigLoader> with myapp.yml, the use of C<lib/MyApp/Controller/Root.pm> vs. C<lib/MyApp.pm>, etc.
+
+=item * 
+
+The use of Template Toolkit (TT) and the C<Catalyst::Helper::View::TTSite> view helper.
+
+=item * 
+
+Useful techniques for troubleshooting and debugging Catalyst applications.
+
+=item * 
+
+The use of SQLite as a database (with code also provided for MySQL and PostgreSQL).
+
+=item * 
+
+How to use HTML::Widget for automated form processing and validation.
+
+=back
+
+This tutorial intentionally seeks to make the learning process its main priority.  For example, the level of comments in the code found here would like be considered excessive in a "normal project".  Because of their contextual value, this tutorial will generally favor inline comments over a separate discussion in the text.  It also deliberately tries to demonstrate multiple approaches to various features (in general, you should try to be as consistent as possible with your own production code).
+
+Furthermore, this tutorial tries to minimize the number of controllers, models, TT templates, and database tables.  Although this does result in things being a bit contrived at times, the concepts should be applicable to more complex environments.  More complete and complicated example applications can be found in the C<examples> area of the Catalyst Subversion repository at L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/>.
+
+B<Note:> There are a variety of other introductory materials available through the Catalyst web site and at L<http://dev.catalyst.perl.org/wiki/UserIntroductions> and L<http://dev.catalyst.perl.org/>.
+
+
+
+=head1 VERSIONS AND CONVENTIONS USED IN THIS TUTORIAL
+
+This tutorial was built using the following resources.  Please note that you will need to make adjustments for different environments and versions:
+
+=over 4
+
+=item * 
+
+OS = CentOS 4 Linux (RHEL 4)
+
+=item * 
+
+Catalyst v5.67
+
+=item * 
+
+DBIx::Class v0.06002
+
+=item * 
+
+Catalyst Plugins
+
+You shouldn't be overly concerned about plugin version numbers, but there could be cases where the tutorial is impacted by what version plugins you use.  The plugins used in this tutorial are:
+
+=over 4
+
+=item * 
+
+Catalyst::Plugin::Authentication -- 0.07
+
+=item *
+
+Authentication::Credential::Password -- 0.07
+
+=item *
+
+Catalyst::Plugin::Authentication::Store::DBIC -- 0.06
+
+=item *
+
+Catalyst::Plugin::Authorization::ACL -- 0.06
+
+=item *
+
+Catalyst::Plugin::Authorization::Roles -- 0.04
+
+=item *
+
+Catalyst::Plugin::ConfigLoader -- 0.07
+
+=item *
+
+Catalyst::Plugin::DefaultEnd -- 0.06
+
+=item *
+
+Catalyst::Plugin::Dumper -- 0.000002
+
+=item *
+
+Catalyst::Plugin::HTML::Widget -- 1.1
+
+=item *
+
+Catalyst::Plugin::Session -- 0.05
+
+=item *
+
+Catalyst::Plugin::Session::FastMmap -- 0.12
+
+=item *
+
+Catalyst::Plugin::Session::State::Cookie -- 0.02
+
+=item *
+
+Catalyst::Plugin::Session::Store::FastMmap -- 0.0
+
+=item *
+
+Catalyst::Plugin::StackTrace -- 0.0
+
+=item *
+
+Catalyst::Plugin::Static::Simple -- 0.14
+
+=back
+
+=item * 
+
+Since the web browser is being used on the same box where Perl and the Catalyst development server is running, the URL of C<http://localhost:3000> will be used (the Catalyst development server defaults to port 3000).  If you are running Perl on a different box than where your web browser is located (or using a different port number via the C<-p> I<port_number> option to the development server), then you will need to update the URL you use accordingly.
+
+=item * 
+
+Depending on the web browser you are using, you might need to hit C<Shift+Reload> to pull a fresh page when testing your application at various points.  Also, the C<-k> keepalive option to the development server can be necessary with some browsers (especially Internet Explorer).
+
+=back
+
+
+
+=head1 CATALYST INSTALLATION
+
+Unfortunately, one of the most daunting tasks faced by newcomers to Catalyst is getting it installed.  Although a compelling strength of Catalyst is that it can easily make full use of CPAN, a vast repository of Perl modules, this can result in initial installations that are both time consuming and frustrating.  However, there are a growing number of methods that can dramatically ease this undertaking.  Of these, the following are likely to be applicable to the largest number of potential new users:
+
+=over 4
+
+=item * 
+
+Matt Trout’s C<cat-install>
+
+Available at L<http://www.shadowcatsystems.co.uk/static/cat-install>, C<cat-install> can be a quick and painless way to get Catalyst up and running.  Just download the script from the link above and type C<perl cat-install>.
+
+=item * 
+
+Chris Laco's CatInABox
+
+Download the tarball from L<http://handelframework.com/downloads/CatInABox.tar.gz> and unpack it on your machine.  Depending on your OS platform, either run C<start.bat> or C<start.sh>.
+
+=item * 
+
+Pre-Built VMWare Images
+
+Under the VMWare community program, work is ongoing to develop a number of VMWare images where an entire Catalyst development environment has already been installed, complete with database engines and a full complement of Catalyst plugins.
+
+=back
+
+B<IMPORTANT:> For additional information and recommendations on Catalyst installation, please refer to L<Catalyst::Manual::Installation|Catalyst::Manual::Installation>.
+
+B<IMPORTANT:> Step-by-step instructions to replicate the environment on which this tutorial was developed can be found at L<Catalyst::Manual::Installation::CentOSTuorial|Catalyst::Manual::Installation::CentOSTuorial>.  Using these instructions, you should be able to build a complete CentOS 4.X server with Catalyst and all the plugins required to run this tutorial.
+
+
+
+=head1 DATABASES
+
+This tutorial will primarily focus on SQLite because of its simplicity; however, modifications in the script required to support MySQL and PostgreSQL will be presented in Appendix 2.
+
+B<Note:> One of the advantages of the MVC design patterns is that applications become much more database independent.  As such, you will notice that only the C<.sql> files used to initialize the database change between database systems... the Catalyst code generally remains the same.
+
+
+
+=head1 WHERE TO GET WORKING CODE
+
+Each part of the tutorial has complete code available in the main Catalyst Subversion repository (see the note at the beginning of each part for the appropriate svn command to use).  Additionally, the final code is available as a ready-to-run tarball at TO_BE_ADDED_TO_FINAL_VERSION.
+
+
+
+=head1 AUTHOR
+
+Kennedy Clark, C<hkclark@gmail.com>
+
+Please report any errors, issues or suggestions to the author.
+
+Copyright 2006, Kennedy Clark, under Creative Commons License (L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
+
+Version: .94
+
diff --git a/lib/Catalyst/Manual/Tutorial/Testing.pod b/lib/Catalyst/Manual/Tutorial/Testing.pod
new file mode 100644 (file)
index 0000000..0e81fdb
--- /dev/null
@@ -0,0 +1,271 @@
+=head1 NAME
+
+Catalyst::Manual::Tutorial::Testing - Catalyst Tutorial – Part 7: Testing
+
+
+
+=head1 OVERVIEW
+
+This is B<Part 7 of 9> for the Catalyst tutorial.
+
+L<Totorial Overview|Catalyst::Manual::Tutorial>
+
+=over 4
+
+=item 1
+
+L<Introduction|Catalyst::Manual::Tutorial::Intro>
+
+=item 2
+
+L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
+
+=item 3
+
+L<Basic CRUD|Catalyst::Manual::Tutorial03_BasicCRUD>
+
+=item 4
+
+L<Authentication|Catalyst::Manual::Tutorial::Authentication>
+
+=item 5
+
+L<Authorization|Catalyst::Manual::Tutorial::Authorization>
+
+=item 6
+
+L<Debugging|Catalyst::Manual::Tutorial::Debugging>
+
+=item 7
+
+B<Testing>
+
+=item 8
+
+L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
+
+=item 9
+
+L<Appendicies|Catalyst::Manual::Tutorial::Appendicies>
+
+=back
+
+
+
+=head1 DESCRIPTION
+
+
+You may have noticed that the Catalyst Helper scripts automatically create C<.t> test scripts under the C<t> directory.  This part of the tutorial briefly looks at how these tests can be used to not only ensure that your application is working correctly at the present time, but also provide automated regression testing as you upgrade various pieces of your application over time.
+
+B<TIP>: Note that all of the code for this part of the tutorial can be pulled from the Catalyst Subversion repository in one step with the following command:
+
+    svn checkout http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/Tutorial@###
+    IMPORTANT: Does not work yet.  Will be completed for final version.
+
+
+
+=head1 RUNNING THE "CANNED" CATALYST TESTS
+
+There are a variety of ways to run Catalyst and Perl tests (for example, C<perl Makefile.PL> and C<make test>, but one of the easiest is with the C<prove> command.  For example, to run all of the tests in the C<t> directory, enter:
+
+    $ prove --lib lib t
+
+The redirection used by the Authentication plugins will cause the default C<t/01app.t> to fail.  You can fix this by changing the line in C<t/01app.t> that read:
+
+    ok( request('/')->is_success, 'Request should succeed' );
+
+to:
+
+    ok( request('/login')->is_success, 'Request should succeed' );
+
+So that a redirect is not necessary.  Also, the C<t/controller_Books.t> and C<t/controller_Logout.t> default test cases will fail because of the authorization.  You can delete these two files to prevent false error messages:
+
+    $ rm t/controller_Books.t
+    $ rm t/controller_Logout.t
+
+As you can see in the C<prove> command line above, the C<--lib> option is used to set the location of the Catalyst C<lib> directory.  With this command, you will get all of the usual development server debug output, something most people prefer to disable while running tests cases.  Although you can edit the C<lib/MyApp.pm> to comment out the C<-Debug> plugin, it's generally easier to simply set the C<CATALYST_DEBUG=0> environment variable.  For example:
+
+    CATALYST_DEBUG=0 prove --lib lib t
+
+During the C<t/02pod> and C<t/03podcoverage> tests, you might notice the C<all skipped: set TEST_POD to enable this test> warning message.  To execute the Pod-related tests, add C<TEST_POD=1> to the C<prove> command:
+
+    $ CATALYST_DEBUG=0 TEST_POD=1 prove --lib lib t
+
+If you omitted the Pod comments from any of the methods that were inserted, you might have to go back and fix them to get these tests to pass. :-)
+
+Another useful option is the C<verbose> (C<-v>) option to C<prove>.  It prints the name of each test case as it is being run:
+
+    $ CATALYST_DEBUG=0 TEST_POD=1 prove --lib lib -v t
+
+
+
+=head1 RUNNING A SINGLE TEST
+
+You can also run a single script by appending its name to the C<prove> command. For example:
+
+    $ CATALYST_DEBUG=0 prove --lib lib t/01app.t
+
+Note that you can also run tests directly from Perl without C<prove>.  For example:
+
+    $ CATALYST_DEBUG=0 perl -Ilib t/01app.t
+
+
+=head1 ADDING YOUR OWN TEST SCRIPT
+
+Although the Catalyst helper scripts provide a basic level of checks "for free," testing can become significantly more helpful when you write your own script to exercise the various parts of your application.  The L<Test::WWW::Mechanize::Catalyst|Test::WWW::Mechanize::Catalyst> module is very popular for writing these sorts of test cases.  This module extends L<Test::WWW::Mechanize|Test::WWW::Mechanize> (and therefore L<WWW::Mechanize|WWW::Mechanize>) to allow you to automate the action of a user "clicking around" inside your application.  It gives you all the benefits of testing on a live system without the messiness of having to use an actual web server.
+
+To create a sample test case, open the C<t/live_app01.t> file in your editor and enter the following:
+
+    #!/usr/bin/perl
+    
+    use strict;
+    use warnings;
+    
+    # Load testing framework and use 'no_plan' to dynamically pick up all tests.  Better
+    # to replace "'no_plan'" with "tests => 30" so it knows exactly how many tests need
+    # to be run (and will tell you if not), but 'no_plan' is nice for quick & dirty tests
+    use Test::More 'no_plan';
+    
+    # Need to specify the name of your app as arg on next line
+    # Can also do:
+    #   use Test::WWW::Mechanize::Catalyst "MyApp";
+    use ok "Test::WWW::Mechanize::Catalyst" => "MyApp";
+    
+    
+    # Create two 'user agents' to simulate two different users ('test01' & 'test02')
+    my $ua1 = Test::WWW::Mechanize::Catalyst->new;
+    my $ua2 = Test::WWW::Mechanize::Catalyst->new;
+    
+    # Use a simplified for loop to do tests that are common to both users
+    # Use get_ok() to make sure we can hit the base URL
+    # Second arg = optional description of test (will be displayed for failed tests)
+    # Note that in test scripts you send everything to 'http://localhost'
+    $_->get_ok("http://localhost/", "Check redirect of base URL") for $ua1, $ua2;
+    # Use title_is() to check the contents of the <title>...</title> tags
+    $_->title_is("Login", "Check for login title") for $ua1, $ua2;
+    # Use content_contains() to match on test in the html body
+    $_->content_contains("You need to log in to use this application",
+        "Check we are NOT logged in") for $ua1, $ua2;
+    
+    # Log in as each user
+    $ua1->get_ok("http://localhost/login?username=test01&password=mypass", "Login 'test01'");
+    $ua2->get_ok("http://localhost/login?username=test02&password=mypass", "Login 'test02'");
+    
+    # Go back to the login page and it should show that we are already logged in
+    $_->get_ok("http://localhost/login", "Return to '/login'") for $ua1, $ua2;
+    $_->title_is("Login", "Check for login page") for $ua1, $ua2;
+    $_->content_contains("Please Note: You are already logged in as ",
+        "Check we ARE logged in" ) for $ua1, $ua2;
+    
+    # 'Click' the 'Logout' link
+    $_->follow_link_ok({n => 1}, "Logout via first link on page") for $ua1, $ua2;
+    $_->title_is("Login", "Check for login title") for $ua1, $ua2;
+    $_->content_contains("You need to log in to use this application",
+        "Check we are NOT logged in") for $ua1, $ua2;
+    
+    # Log back in
+    $ua1->get_ok("http://localhost/login?username=test01&password=mypass", "Login 'test01'");
+    $ua2->get_ok("http://localhost/login?username=test02&password=mypass", "Login 'test02'");
+    # Should be at the Book List page... do some checks to confirm
+    $_->title_is("Book List", "Check for book list title") for $ua1, $ua2;
+    
+    $ua1->get_ok("http://localhost/books/list", "'test01' book list");
+    $ua1->get_ok("http://localhost/login", "Login Page");
+    $ua1->get_ok("http://localhost/books/list", "'test01' book list");
+    
+    $_->content_contains("Book List", "Check for book list title") for $ua1, $ua2;
+    # Make sure the appropriate logout buttons are displayed
+    $_->content_contains("/logout\">Logout</a>",
+        "Both users should have a 'User Logout'") for $ua1, $ua2;
+    $ua1->content_contains("/books/form_create\">Create</a>",
+        "Only 'test01' should have a create link");
+    
+    $ua1->get_ok("http://localhost/books/list", "View book list as 'test01'");
+    
+    # User 'test01' should be able to create a book with the "formless create" URL
+    $ua1->get_ok("http://localhost/books/url_create/TestTitle/2/4",
+        "'test01' formless create");
+    $ua1->title_is("Book Created", "Book created title");
+    $ua1->content_contains("Added book 'TestTitle' by 'Stevens'", "Check added OK");
+    $ua1->content_contains("a rating of 2.", "Check rating added");
+    
+    # Make sure the new book shows in the list
+    $ua1->get_ok("http://localhost/books/list", "'test01' book list");
+    $ua1->title_is("Book List", "Check logged in and at book list");
+    $ua1->content_contains("Book List", "Book List page test");
+    $ua1->content_contains("TestTitle", "Look for 'TestTitle'");
+    
+    # Make sure the new book can be deleted
+    # Get all the Delete links on the list page
+    my @delLinks = $ua1->find_all_links(text => 'Delete');
+    # Use the final link to delete the last book
+    $ua1->get_ok($delLinks[$#delLinks]->url, 'Delete last book');
+    # Check that delete worked
+    $ua1->content_contains("Book List", "Book List page test");
+    $ua1->content_contains("Book deleted.", "Book was deleted");
+    
+    # User 'test02' should not be able to add a book
+    $ua2->get_ok("http://localhost/books/url_create/TestTitle2/2/5", "'test02' add");
+    $ua2->content_contains("Unauthorized!", "Check 'test02' cannot add");
+
+The C<live_app.t> test cases uses copious comments to explain each step of the process.  In addition to the techniques shown here, there are a variety of other methods available in L<Test::WWW::Mechanize::Catalyst|Test::WWW::Mechanize::Catalyst> (for example, regex-based matching).  Consult the documentation for more detail.
+
+B<TIP>: For I<unit tests> vs. the "full application tests" approach used by L<Test::WWW::Mechanize::Catalyst|Test::WWW::Mechanize::Catalyst>, see L<Catalyst::Test|Catalyst::Test>.
+
+B<Note:> The test script does not test the C<form_create> and C<form_create_do> actions.  That is left as an exercise for the reader (you should be able to complete that logic using the existing code as a template).
+
+To run the new test script, use a command such as:
+
+    $ CATALYST_DEBUG=0 prove --lib lib -v t/live_app01.t
+
+or
+
+    $ DBIX_CLASS_STORAGE_DBI_DEBUG=0 CATALYST_DEBUG=0 prove --lib lib -v t/live_app01.t
+
+Experiment with the C<DBIX_CLASS_STORAGE_DBI_DEBUG>, C<CATALYST_DEBUG> and C<-v> settings.  If you find that there are errors, use the techniques discussed in the "Catalyst Debugging" section (Part 6) to isolate and fix the problem.
+
+If you want to run the test case under the Perl interactive debugger, try a command such as:
+
+    $ DBIX_CLASS_STORAGE_DBI_DEBUG=0 CATALYST_DEBUG=0 perl -d -Ilib t/live_app01.t
+
+Note that although the tutorial uses a single custom test case for simplicity, you may wish to break your tests into different files for better organization.
+
+
+
+=head1 SUPPORTING BOTH PRODUCTION AND TEST DATABASES
+
+You may wish to leverage the techniques discussed in this tutorial to maintain both a "production database" for your live application and a "testing database" for your test cases.  One advantage to L<Test::WWW::Mechanize::Catalyst|Test::WWW::Mechanize::Catalyst> is that it runs your full application; however, this can complicate things when you want to support multiple databases.  One solution is to allow the database specification to be overridden with an environment variable.  For example, open C<lib/MyApp/Model/MyAppDB.pm> in your editor and change the C<__PACKAGE__-E<gt>config(...> declaration to resemble:
+
+    my $dsn = $ENV{MYAPP_DSN} ||= 'dbi:SQLite:myapp.db';
+    __PACKAGE__->config(
+        schema_class => 'MyAppDB',
+        connect_info => [
+            $dsn,
+            '',
+            '',
+            { AutoCommit => 1 },
+    
+        ],
+    );
+
+Then, when you run your test case, you can use commands such as:
+
+    $ cp myapp.db myappTEST.db
+    $ CATALYST_DEBUG=0 MYAPP_DSN="dbi:SQLite:myappTEST.db" prove --lib lib -v t/live_app01.t
+
+This will modify the DSN only while the test case is running.  If you launch your normal application without the C<MYAPP_DSN> environment variable defined, it will default to the same C<dbi:SQLite:myapp.db> as before.
+
+
+
+=head1 AUTHOR
+
+Kennedy Clark, C<hkclark@gmail.com>
+
+Please report any errors, issues or suggestions to the author.
+
+Copyright 2006, Kennedy Clark. All rights reserved.
+
+This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+Version: .94
+