Merge branch 'tutorial_update_1108'
hkclark [Sat, 3 Sep 2011 02:30:05 +0000 (22:30 -0400)]
20 files changed:
Changes
MANIFEST.SKIP
_UPDATERS_PLEASE_READ_THIS_.txt [new file with mode: 0644]
lib/Catalyst/Manual.pm
lib/Catalyst/Manual/Deployment/Apache/mod_perl.pod
lib/Catalyst/Manual/ExtendingCatalyst.pod
lib/Catalyst/Manual/Tutorial.pod
lib/Catalyst/Manual/Tutorial/.gitignore [new file with mode: 0644]
lib/Catalyst/Manual/Tutorial/01_Intro.pod
lib/Catalyst/Manual/Tutorial/02_CatalystBasics.pod
lib/Catalyst/Manual/Tutorial/03_MoreCatalystBasics.pod
lib/Catalyst/Manual/Tutorial/04_BasicCRUD.pod
lib/Catalyst/Manual/Tutorial/05_Authentication.pod
lib/Catalyst/Manual/Tutorial/06_Authorization.pod
lib/Catalyst/Manual/Tutorial/07_Debugging.pod
lib/Catalyst/Manual/Tutorial/08_Testing.pod
lib/Catalyst/Manual/Tutorial/09_AdvancedCRUD.pod
lib/Catalyst/Manual/Tutorial/09_AdvancedCRUD/09_FormFu.pod
lib/Catalyst/Manual/Tutorial/09_AdvancedCRUD/09_FormHandler.pod
lib/Catalyst/Manual/Tutorial/10_Appendices.pod

diff --git a/Changes b/Changes
index 2487cca..d4dc85b 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,7 +1,20 @@
 Revision history for Catalyst-Manual
 
-5.9000 16 Aug 2011
+5.9001 2 Sept 2011
+    - Tutorial:
+        - Migrate Tutorial to use of a downloadable Virtual Machine
+        - Switch tutorial to Catalyst::Plugin::StatusMessage (vs. flash
+            and query parameters)
+        - Switch to use of local::lib
+        - Add "next chapter links" (RT #31164)
+        - Test all the tutorial code and make sure it's all working
+        - Lots of other Tutorial cleanup
+        - Update to Catalyst 5.9 and latest versions of all modules
+        - Clean up RT ticket queue (#68379, 68377, 68376, etc.)
+    - Other misc fixes/changes
 
+
+5.9000 16 Aug 2011
     - Copy editing to make more sense in deployment documentation.
 
 5.8901 7 Aug 2011 - TRIAL RELEASE
index 3880d66..2969338 100644 (file)
@@ -29,3 +29,7 @@
 
 # No tarballs!
 \.gz$
+
+# Author/Updater-Only Notes
+^_UPDATERS_PLEASE_READ_THIS_.txt$
+_ignore
diff --git a/_UPDATERS_PLEASE_READ_THIS_.txt b/_UPDATERS_PLEASE_READ_THIS_.txt
new file mode 100644 (file)
index 0000000..2d46d75
--- /dev/null
@@ -0,0 +1,24 @@
+=============================================================
+=============================================================
+ IMPORTANT NOTES FOR PEOPLE MAKING UPDATES TO THE *TUTORIAL*
+=============================================================
+=============================================================
+
+If you are updating **TUTORIAL** and doing more than a **TYPO FIX**,
+please be careful to not update the configuration code without also:
+
+    * Testing the code not only for that chapter, but all
+        chapters that follow (the chapters build on each other).
+    * Make sure the changes you make in a subroutine are reflected
+        in later examples of the same subroutine in that chapter
+        and all the chapters that follow.
+    * Updating the code in the /root/Final directory of the Tutorial
+        Virtual Machine image.
+
+Feel free to ping Kennedy Clark at hkclark@gmail.com or hkclark@cpan.org
+for help.
+
+If you are only updating non-code typo fixes or updating Catalyst::Manual
+outside of the Tutorial, then the information above does not apply. :-)
+
+Thanks
index 36fd880..f3e83bd 100644 (file)
@@ -6,7 +6,7 @@ package Catalyst::Manual;
 use strict;
 use warnings;
 
-our $VERSION = '5.9000';
+our $VERSION = '5.9001';
 
 =head1 NAME
 
index 9fd9e29..7296890 100644 (file)
@@ -92,9 +92,23 @@ controller, model, and view classes and configuration.  If you have -Debug
 mode enabled, you will see the startup output scroll by when you first
 start Apache.
 
+Also, there have been reports that the block above should instead be (but
+this has not been confirmed):
+
+    <Perl>
+        use lib '/var/www/MyApp/lib';
+        use MyApp;
+    </Perl>
+    
+    <Location />
+        SetHandler          modperl
+        PerlResponseHandler MyApp
+    </Location>
+
 For an example Apache 1.3 configuration, please see the documentation for
 L<Catalyst::Engine::Apache::MP13>.
 
+
 =head2 Test It
 
 That's it, your app is now a full-fledged mod_perl application!  Try it out
index bdfba30..9e4a4e4 100644 (file)
@@ -246,7 +246,7 @@ L<Catalyst::Component> uses L<Class::Accessor::Fast> for accessor
 creation. Please refer to the modules documentation for usage
 information.
 
-=head2 Component configuration
+=head2 Component Configuration
 
 At creation time, the class configuration of your component (the one
 available via C<$self-E<gt>config>) will be merged with possible
index 1fd3280..60571fe 100644 (file)
@@ -11,7 +11,7 @@ commonly used features while focusing on real-world best practices.
 
 We suggest that you read this introduction on the web.  Make sure you are
 reading the latest version of the tutorial by visiting
-L<http://search.cpan.org/perldoc?Catalyst::Manual::Tutorial>.  Alternatively
+L<https://metacpan.org/module/Catalyst::Manual::Tutorial>.  Alternatively
 you can use CPAN modules like L<Pod::Webserver>, L<Pod::POM::Web>,
 L<Pod::Browser> (Catalyst based), or L<CPAN::Mini::Webserver> to read a local
 copy of the tutorial.
@@ -726,8 +726,12 @@ name should be here).
 
 Kennedy Clark, C<hkclark@gmail.com>
 
-Please report any errors, issues or suggestions to the author.  The
-most recent version of the Catalyst Tutorial can be found at
+Feel free to contact the author for any errors or suggestions, but the
+best way to report issues is via the CPAN RT Bug system at
+<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
+
+***TODO: Update
+The most recent version of the Catalyst Tutorial can be found at
 L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
 
 Copyright 2006-2010, Kennedy Clark, under the
diff --git a/lib/Catalyst/Manual/Tutorial/.gitignore b/lib/Catalyst/Manual/Tutorial/.gitignore
new file mode 100644 (file)
index 0000000..2e854ec
--- /dev/null
@@ -0,0 +1 @@
+_ignore
index c711c35..297e0b9 100644 (file)
@@ -56,50 +56,54 @@ L<Appendices|Catalyst::Manual::Tutorial::10_Appendices>
 
 =head1 DESCRIPTION
 
-This tutorial provides a multi-part introduction to the Catalyst web
-framework. It seeks to provide a rapid overview of many of its most
+This tutorial provides a multi-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, how to add
-authentication and authorization to an existing application, or form
-management).
+authentication and authorization to an existing application, and/or
+form management).
 
-You can obtain the code for all the tutorial examples from the
-catalyst subversion repository by issuing the command:
+The most recent code for the tutorial is included on the Tutorial Virtual
+Machine you can download from:
+
+L<http://cattut.shadowcat.co.uk/>
+
+See L</STARTING WITH THE TUTORIAL VIRTUAL MACHINE> below for
+instructions getting and using the VM.
+
+Should you wish to download the code directly, you get pull it via the
+following command (note: will probably be switching to git soon):
 
     svn co http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/Tutorial/ CatalystTutorial
 
 This will download the most recent code for each chapter of the
 tutorial into the CatalystTutorial directory on your machine.
 
-B<These reference implementations are provided so that when you follow
-the tutorial, you can use the code from the subversion repository to
-ensure that your system is set up correctly, and that you have not
-inadvertently made any typographic errors, or accidentally skipped
-part of the tutorial.>
-
-B<NOTE: You can use any Perl-supported OS and environment to run 
-Catalyst.> It should make little or no difference to Catalyst's 
-operation, B<but this tutorial has been written using the Debian 5 
-live CD> because that represents a quick and easy way for most people to 
-try out Catalyst with virtually zero setup time and hassles.  Also, 
-the tutorial has been tested to work correctly with the versions of 
-Catalyst and all the supporting modules in Debian 5 (see "VERSIONS 
-AND CONVENTIONS USED IN THIS TUTORIAL" below for the specific versions 
-for some of the key modules), so B<if you think you might be running 
-into an issue related to versions> (for example, a module changed its 
-behavior in a newer version or a bug was introduced), B<it might be 
-worth giving Debian 5 a try>.
-
-If you plan to follow along with Debian 5, you can jump down to the 
-"Debian" section under L</"CATALYST INSTALLATION"> below and it will walk you 
-though the setup of a fully functional Catalyst environment. If you 
-would prefer to install directly from CPAN, you can download the example 
-program and all the necessary dependencies to your local machine by 
-installing the C<Task::Catalyst::Tutorial> distribution: 
+These reference implementations are provided so that when you follow
+the tutorial, you can use the code to ensure that your system is set up
+correctly (which shouldn't be an issue if you use the Tutorial Virtual
+Machine), :-) and that you have not inadvertently made any typographic
+errors, or accidentally skipped part of the tutorial.
+
+B<NOTE: You can use any Perl-supported OS and environment to run
+Catalyst.> It should make little or no difference to Catalyst's
+operation, B<but this tutorial has been written using the Debian-based
+Tutorial Virtual Machine> that you can download and use to work through
+the full tutorial step by step.  B<WE STRONGLY RECOMMEND THAT YOU USE
+THE VIRTUAL MACHINE IMAGE TO WORK THROUGH THE TUTORIAL> to avoid issues
+that may crop up if you are working with a different configuration.  We
+have tested the Tutorial Virtual Machine to make sure all of the
+examples work correctly, but it is hard to guarantee this on other
+platforms and versions.
+
+If you would prefer to install directly from CPAN and not use the
+Tutorial Virtual machine, you can download the example program and all
+the necessary dependencies to your local machine by installing the
+C<Task::Catalyst::Tutorial> distribution:
 
      cpan Task::Catalyst::Tutorial
 
@@ -117,8 +121,8 @@ A simple application that lists and adds books.
 
 =item *
 
-The use of L<DBIx::Class|DBIx::Class> (DBIC) for the model (including 
-some of the more advanced techniques you will probably want to use in 
+The use of L<DBIx::Class> (DBIC) for the model (including
+some of the more advanced techniques you will probably want to use in
 your applications).
 
 =item * 
@@ -136,12 +140,8 @@ Role-based authorization ("authz").
 
 =item * 
 
-Attempts to provide an example showing current (5.8XXX) Catalyst
-practices. For example, the use of 
-L<Catalyst::Action::RenderView|Catalyst::Action::RenderView>,
-DBIC, L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader> 
-with C<myapp.conf>, the use of C<lib/MyApp/Controller/Root.pm> 
-vs. C<lib/MyApp.pm>, etc.
+Attempts to provide an example showing current (5.9) Catalyst
+practices.
 
 =item * 
 
@@ -155,14 +155,14 @@ applications.
 =item * 
 
 The use of SQLite as a database (with code also provided for MySQL and
-PostgreSQL).  (Note: Because we make use of the DBIx::Class Object 
+PostgreSQL).  (Note: Because we make use of the DBIx::Class Object
 Relational Mapping [ORM] layer, out our application will be database
-agnostic and can easily be used by any of the databases supported
-by DBIx::Class.)
+agnostic and can easily be used by any of the databases supported by
+DBIx::Class.)
 
 =item * 
 
-The use of L<HTML::FormFu|HTML::FormFu> or L<HTML::FormHandler|HTML::FormHandler>
+The use of L<HTML::FormFu> or L<HTML::FormHandler>
 for automated form processing and validation.
 
 =back
@@ -180,587 +180,348 @@ 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
+applications can be found at
+L<http://wiki.catalystframework.org/wiki/resources/catalystexamples> and
+in the C<examples> area of the Catalyst Subversion repository at
 L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/>.
 
 
-=head1 QUICK START
+=head1 STARTING WITH THE TUTORIAL VIRTUAL MACHINE
 
-For those who want to get going quickly, here is a short "cookbook-style 
-recipe" to quickly get you up and running. Although there are many 
-different ways to get a Catalyst environment going, this tutorial has 
-been written with and tested against Debian 5 Live CD, using the steps 
-in this Quick Start. 
+The steps below briefly outline how you can download the Tutorial
+Virtual Machine.  This document uses the term "host machine" to refer to
+the physical machine where you will run the virtualization software and
+boot up the VM.  The terms "guest machine" or just "VM" refer to the
+virtual machine itself -- the thing where you actually do the tutorial
+and that you boot up on the "host machine".
 
-If you want, you can follow the directions in this section and then jump 
-right to L<Chapter 2|Catalyst::Manual::Tutorial::02_CatalystBasics> of 
-the tutorial. However, it would be a good idea to come back and read the 
-sections below the Quick Start when you have time. Or, continue reading 
-those other sections for suggestions if you do not wish to use the 
-Debian 5 Live CD. 
+Also, to reduce download size, the Tutorial VM just includes a minimal
+command-line environment.  In general, it is expected that people will
+boot up the Tutorial VM on their main desktop (the "host machine" using
+the terminology above) and then use that main desktop machine to SSH and
+web browse into the "guest VM" as they work through the tutorial.  If
+you wish to install X-Windows (or any other packages), just use the
+C<aptitude> (or C<apt-get>) Debian commands.
 
 
 =over 4
 
 =item 1 
 
-Download the C<debian-live-503-i386-rescue.iso> image from
-L<http://cdimage.debian.org/cdimage/release/current-live/i386/iso-cd/>.
+Download a Tutorial Virtual Machine image from
+L<http://cattut.shadowcat.co.uk/>
+
+B<A big thanks to Shadowcat Systems for hosting the virtual machines>
+B<(and everything else they do for the Perl community)!>
 
 =item 2
 
-Boot this disk, either in a physical machine, or possibly some sort
-of virtual machine (can be a very handy way to practice).
+Uncompress the image:
+
+    tar zxvf CatalystTutorial.tgz
 
 =item 3
 
-Select "C<Live>" from the initial boot menu.
+Boot the virtual machine using a tool like VMWare Player
+L<http://www.vmware.com/products/player> or VirtualBox
+L<http://www.virtualbox.org/>.
 
 =item 4
 
-At the "C<user@debian:~$>" prompt, type:
+Once you get a login prompt, enter the username B<root> and a password
+for C<catalyst>.  You should now be at a prompt that looks like:
 
-    sudo aptitude -y install subversion
+    catalyst login: root
+    Password: catalyst
+    ...
+    root@catalyst:~#
 
 =item 5
 
-If you want to be able to remotely SSH to this system, set a
-password for root:
+Type "C<ifconfig>" to get the IP address assigned to the virtual
+machine.  You should get output along the lines of:
 
-    sudo passwd
+    eth0  Link encap:Ethernet  HWaddr 00:01:22:3b:45:69
+          inet addr:192.168.0.12  Bcast:192.168.0.255  Mask:255.255.255.0
     ...
 
-=item 6
-
-Add the "unstable" Debian package repository:
-
-    sudo vi /etc/apt/sources.list
-
-Add the following line to the bottom of this file:
-
-    deb http://ftp.us.debian.org/debian/ unstable main
-
-=item 7
-
-Install Catalyst and related libraries:
-
-    sudo aptitude update
-    sudo aptitude -y install sqlite3 libdbd-sqlite3-perl libcatalyst-perl \
-        libcatalyst-modules-perl libdbix-class-timestamp-perl \
-        libdatetime-format-sqlite-perl libconfig-general-perl \
-        libhtml-formfu-model-dbic-perl libterm-readline-perl-perl \
-        libdbix-class-encodedcolumn-perl libperl6-junction-perl \
-        libtest-pod-perl
-    sudo aptitude clean
-
-=item 8
-
-Test example code:
-
-    mkdir test
-    cd test
-    svn co http://dev.catalystframework.org/repos/Catalyst/trunk/examples/Tutorial/MyApp_Chapter8
-    cd MyApp_Chapter8/MyApp
-    CATALYST_DEBUG=0 prove -wl t
-    cd
-
-=back
-
-
-=head1 VERSIONS AND CONVENTIONS USED IN THIS TUTORIAL
-
-This tutorial was built using the following resources. Please note that
-you may need to make adjustments for different environments and
-versions (note that trailing zeros in version numbers are not 
-significant and may get dropped with techniques for viewing them;
-for example, Catalyst v5.80020 might show up as 5.8002):
-
-=over 4
-
-=item * 
-
-Debian 5 (Lenny)
-
-=item * 
-
-Catalyst v5.80020 (note: may show up as '5.8002' without the trailing zero)
-
-=item *
-
-Catalyst::Devel v1.26
-
-=item * 
-
-DBIx::Class v0.08115
-
-=item *
-
-Catalyst::Model::DBIC::Schema v0.40
-
-=item *
-
-Template Toolkit v2.20
-
-
-=item * 
-
-Catalyst Plugins
-
-The plugins used in this tutorial all have sufficiently stable APIs that
-you shouldn't need to worry about versions. However, there could be
-cases where the tutorial is affected by what version of plugins you
-use. This tutorial has been tested against the following set of plugins:
-
-=over 4
-
-=item * 
+You want the IP address on the second line below the C<eth0> interface.
+The image it design to automatically use a DHCP-assigned address.
 
-Catalyst::Plugin::Authentication -- v0.10016
 
-=item *
-
-Catalyst::Plugin::Authorization::Roles -- v0.08
-
-=item *
-
-Catalyst::Plugin::ConfigLoader -- v0.27
+Try to ping this IP address from your "host machine" (main desktop):
 
-=item *
 
-Catalyst::Plugin::Session -- v0.29
+    MainComputer:~$ ping 192.168.0.12
+    PING 192.168.0.12 (192.168.0.12) 56(84) bytes of data.
+    64 bytes from 192.168.0.12: icmp_req=1 ttl=255 time=4.97 ms
+    64 bytes from 192.168.0.12: icmp_req=2 ttl=255 time=3.43 ms
+    ...
 
-=item *
 
-Catalyst::Plugin::Session::State::Cookie -- v0.17
+B<Note:> The ping above is being originated B<from> your B<host machine>
+(main desktop) and going B<to> your guest B<virtual machine>, not the
+other way around.
 
-=item *
+If you are not seeing a valid IP address or it's not responding to pings
+(for example, you get error messages along the lines of "Request timed
+out", "100% packet loss", or "Destination Host Unreachable"), there
+could be a few network-related issues you might need to sort out.  See
+the section below L</Sorting Out Virtual Machine Network-Related Issues>
+for additional information and troubleshooting advice.
 
-Catalyst::Plugin::Session::Store::File -- v0.18
+B<Note:> Remember this IP address... you will be using it throughout the
+tutorial.
 
-=item *
 
-Catalyst::Plugin::StackTrace -- v0.11
+=item 6
 
-=item *
+B<From your main desktop machine>, open an SSH client and connect to the
+IP address found in the previous step.  You should get a login prompt
+(accept the SSH key if you get a warning message about that).  Login
+with the same username and password as we used in Step 4: B<root> /
+B<catalyst>
 
-Catalyst::Plugin::Static::Simple -- v0.29
+    catalyst login: root
+    Password: catalyst
+    ...
+    root@catalyst:~#
 
-=back
 
-=item *
+=item 7
 
-HTML::FormFu -- v0.06001
+B<Using the SSH session>, change to the sample code directory for
+Chapter 3 included with the Tutorial Virtual Machine and start the
+Catalyst Development Server:
 
-=item * 
+    $ cd Final/Chapter03/MyApp
+    $ perl scripts/myapp_server
 
-B<NOTE:> You can check the versions you have installed with the
-following command:
+=item 8
 
-    perl -M<_mod_name_> -e 'print "$<_mod_name_>::VERSION\n"'
+B<From your main desktop machine> (the "host machine"), open a web
+browser and go to B<http://A.B.C.D:3000/>, where C<A.B.C.D> is the IP
+address to your virtual machine that you looked up in Step 5.  For
+example, if your virtual machine is using the IP address
+C<192.168.0.12>, you would put the following URL into your web browser:
 
-For example:
+    http://192.168.0.12:3000/
 
-    perl -MCatalyst -e 'print "$Catalyst::VERSION\n";'
+Make sure you don't forget the B<:3000> to use port 3000 instead of the
+usual port 80 that is used by HTTP by default.
 
-or:
+You should get a Catalyst Welcome Screen.  If you do, feel free to jump
+right in to L<Chapter 2|Catalyst::Manual::Tutorial::02_CatalystBasics>
+of the tutorial.  If you don't go get the Catalyst Welcome Screen, go
+back and carefully check each of the steps above.
 
-    perl -MCatalyst::Devel -e 'print "$Catalyst::Devel::VERSION\n";'
+=item 9
 
-=item * 
+B<Optional:> The VI/VIM editor is already installed on the Tutorial
+Virtual Machine.  In order to reduce the size of the download, Emacs is
+not pre-installed.  Since people obviously have very strong opinions
+about which editor is best, :-) Debian fortunately make it very easy to
+install Emacs:
 
-This tutorial will assume that the web browser is located on the same 
-system where the Catalyst development server is running, and 
-therefore use a URL of C<http://localhost:3000> (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.
-
-Please Note: Depending on the web browser you are using, you might 
-need to hit C<Shift+Reload> or C<Ctrl+Reload> to pull a fresh page 
-when testing your application at various points (see 
-L<http://en.wikipedia.org/wiki/Bypass_your_cache> for a comprehensive 
-list of options for each browser).  Also, the C<-k> keepalive option 
-to the development server can be necessary with some browsers 
-(especially Internet Explorer).
+    $ aptitude install emacs
 
 =back
 
 
-=head1 CATALYST INSTALLATION
-
-Although Catalyst installation has been a challenge in the past, the 
-good news is that there are a growing number of options to eliminate 
-(or at least dramatically simplify) this concern.  Although a 
-compelling strength of Catalyst is that it makes use of many of the 
-modules in the vast repository that is CPAN, this can complicate the 
-installation process if you approach it in the wrong way.  Consider 
-the following suggestions on the most common ways to get started with 
-a Catalyst development environment:
+You may note that the Tutorial Virtual Machine uses L<local::lib> so
+that the Perl modules are run from ~/perl5 (in this case, /root/perl5)
+vs. the usual location of your "system Perl".  We recommend that you
+also consider using this very handy module.  It can greatly ease the
+process of maintaining and testing different combinations or Perl
+modules across development, staging, and production servers.  (The
+"relocatable Perl" feature can also be used to to run both the modules
+B<and> Perl itself from your home directory [or any other directory you
+chose]).
+
+B<Note>: Please provide feedback on how the Virtual Machine approach for
+the tutorial works for you.  If you have suggestions or comments, you
+can reach the author through the email address at the bottom of this
+page or via an RT ticket at
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
+
+
+
+=head2 Sorting Out Virtual Machine Network-Related Issues
+
+In general, using a virtual machine to work through the tutorial is
+*much* easier than trying to do it in other environments, especially if
+you are new to Catalyst (or Perl or CPAN or ...).  However, it's
+possible that you could run into a few network-related issues.  The good
+news is that there is lots of information about the issue available via
+search engines on the Internet.  Here is some background information to
+get you started.
+
+In Step 5 of the prior section above, we assumed that a "Bridged Mode"
+configuration and DHCP will work (it should for most people).  If DHCP
+is not working or is not available in your location, most virtual
+machine "host" environments let you select between one of several
+different types of networking between the "guest" and the "host"
+machine.
+
+    1) Bridged
+    2) NAT
+    3) Local host only
+
+The Tutorial Virtual Machine defaults to "Bridged" -- this should result
+in the VM acting like another device on your network that will get a
+different DHCP IP address than the host machine.  The advantage of this
+approach, is that you can easily SSH and web browse to the guest virtual
+machine.  In general, this is the best option if you want to be able to
+boot up the VM and then use your SSH client and web browser from your
+main machine to connect into the virtual machine.
+
+In some environments, you might have better luck with "NAT" (Network
+Address Translation) mode.  With this configuration, the guest VM shares
+the same IP address as the host machine.  The downside of this approach
+is that special configuration is required if you want to be able to SSH
+or web browse to the guest VM.  The NAT option should automatically
+allow the VM "outbound connection" (e.g., to the Internet if you want to
+install additional Debian packages), but it requires special
+configuration if you want to get "inbound connections" that go from some
+other machine (including the "host machine") into the VM.  Some virtual
+machine host environments let you configure a "static NAT" or "port
+forwarding" to reach the guest OS, but others omit this functionality.
+
+"Local host only" mode let's the guest VM and the host machine talk on a
+"private subnet" that other devices in your network cannot reach.  This
+can work as long as you don't need to go from the VM to the Internet
+(for example, to install other Debian packages).
+
+Consult the documentation on your virtual machine host environment for
+help configuring the options above.  Here are some links that might
+help:
 
 =over 4
 
 =item *
 
-Debian
-
-The Debian 5 live CD represents a great way for newcomers to 
-experiment with Catalyst.  As a "live CD," you can simple boot from 
-the CD, run a few commands, and in a matter of minutes you should have 
-a fully function environment in which do this tutorial. B<The tutorial 
-was fully tested to work under Debian 5.  Although it SHOULD work 
-under any Catalyst installation method you might choose, it can be 
-hard to guarantee this.>
-
-=over 4
-
-=item * 
-
-Download one of the ISO files from 
-L<http://cdimage.debian.org/cdimage/release/current-live/i386/iso-cd/>
-(the current version at the time this was written was 5.0.3). 
-You can pick any one of the live CD variations will work, but 
-you may wish to consider the following points:
-
-=over 4
-
-=item *
-
-"C<debian-live-503-i386-rescue.iso>" is probably the best all-around 
-option for most people because it includes many extra tools such as 
-the GCC compiler, therefore saving RAM (every package you need to 
-install when running from live CD consumes memory because RAM disk is 
-being used in lieu of real disk space).  When initially booting under 
-this image, you may see some cryptic warning messages having to do 
-with various diagnostic tools it tries to load or enable, but you 
-should be able to safely ignore these.
+L<http://vmfaq.com/entry/34/>
 
 =item *
 
-"C<debian-live-503-i386-standard.iso>" is a great option because of 
-its compact size, but you will probably need approximately 1 GB of RAM 
-in the computer where you will run the tutorial.  Because the 
-"standard" live CD comes with with a minimal set of tools, we will 
-have to install extra packages (such as the GCC compiler), all of 
-which will require RAM when running from a live CD. 
+L<http://www.vmware.com/support/pubs/player_pubs.html>
 
 =item *
 
-The other ISO images include different flavors of X-Windows desktop 
-managers.  You can select one of these if you don't mind the larger 
-download size and prefer a graphical environment.  Be aware that these 
-disks do not come with the extra tools found on the "rescue" image, so 
-you will need adequate RAM to be able to install them just as you 
-would under the "standard" image. B<Use one of the "graphical" ISO 
-images if you want a graphical web browser on the same machine as 
-where you will run the tutorial.>  (If you are using one of the non-
-graphical images discussed above, you can still use a graphical web 
-browser from another machine and point it to your Catalyst development 
-machine.)
+L<http://www.virtualbox.org/manual/ch06.html>
 
 =back
 
-=item *
-
-Boot off the CD.
-
-=item *
-
-Select "C<Live>" from the initial boot menu.
-
-=item *
-
-Once the system has booted to a "C<user@debian:~$>" prompt, first
-install the Subversion client in case you want to check out the
-completed chapter example code:
-
-    sudo aptitude -y install subversion
-
-If you want to be able to remotely SSH to this system, set a
-password for root:
-
-    sudo passwd
-    ...
-
-Then enter the following command to add the more current "unstable" 
-package repository so we get the latest versions of Catalyst and
-related packages:
-
-    sudo vi /etc/apt/sources.list
-
-Add the following line to the bottom of this file:
-
-    deb http://ftp.us.debian.org/debian/ unstable main
-
-If you are not familiar with VI, you can move to the bottom of this 
-file and press the "o" key to insert a new line and type the line 
-above.  Then press the "Esc" key followed by a colon (":"), the 
-letters "wq" and then the "Enter" key.  The rest of the tutorial will 
-assume that you know how to use some editor that is available from the 
-Linux command-line environment.
-
-=item *
-
-Install Catalyst:
-
-    sudo aptitude update
-    sudo aptitude -y install sqlite3 libdbd-sqlite3-perl libcatalyst-perl \
-        libcatalyst-modules-perl libdbix-class-timestamp-perl \
-        libdatetime-format-sqlite-perl libconfig-general-perl \
-        libhtml-formfu-model-dbic-perl libterm-readline-perl-perl \
-        libdbix-class-encodedcolumn-perl libperl6-junction-perl \
-        libtest-pod-perl
-
-Let it install (normally about a 30 to 90-second operaton) and you are 
-done. (Note the '\' above.  Depending on your environment, you might 
-be able to cut and paste the text as shown or need to remove the '\' 
-characters to that the command is all on a single line.)
-
-If you are using an image other than the "rescue" ISO, you will also need
-to run the following command to install additional packages:
-
-    sudo aptitude -y install gcc make libc6-dev
 
-If you are running from the Live CD, you probably also want to free up 
-some RAM disk space with the following:
 
-    sudo aptitude clean
 
-NOTE: While the instructions above mention the Live CD because that 
-makes it easy for people new to Linux, you can obviously pick a 
-different Debian ISO image and install it to your hard drive. 
-Although there are many different ways to download and install Debian, 
-the "netinst" ISO image (such as "C<debian-500-i386-netinst.iso>" 
-represents a great option because it keeps your initial download small 
-(but still lets you install anything you want "over the network").
+=head1 VERSIONS AND CONVENTIONS USED IN THIS TUTORIAL
 
-Here are some tips if you are running from a live CD and are running
-out of disk space (which really means you are running out of RAM):
+This tutorial was built using the following resources. Please note that
+you may need to make adjustments for different environments and versions
+(note that trailing zeros in version numbers are not significant and may
+get dropped with some techniques for viewing them; for example, Catalyst
+v5.80020 might show up as 5.8002):
 
 =over 4
 
-=item *
-
-Always run "C<aptitude clean>" after you install new packages to 
-delete the original .deb files (the files installed B<by> the .deb 
-package B<will> remain available, just the .deb package itself is 
-deleted).
-
-=item *
-
-If you are installing modules from CPAN, you can free up some space 
-with "C<rm -rf /root/.cpan/*>" (change "/root/" in the previous 
-command to match your home directory or the location where CPAN
-has been configured to perform build operations).
-
-=item *
+=item * 
 
-If necessary, you can remove the cached package information with the 
-command "C<rm -f /var/lib/apt/lists/*>".  You can later pull this 
-information again via the command "C<aptitude update>".
+Debian 6 (Squeeze)
 
 =item * 
 
-You can save a small amount of space by commenting out the lines in 
-C</etc/apt/sources.list> that reference "deb-src" and 
-"security.debian.org".  If you have already done an "C<aptitude 
-update>" with these repositories enabled, you can use the tip in the 
-previous bullet to free the space up (and then do another "C<aptitude 
-update>").
+Catalyst v5.90002
 
 =item *
 
-Although you can free up space by removing packages you installed 
-since you last booted (check out "C<aptitude remove _pkg_name>"), 
-don't bother trying to remove packages already available at the time 
-of boot. Instead of freeing up space, it will actual I<consume> some 
-space. (The live CD uses these "burn in" packages right from the CD 
-disk vs. first loading them on the virtual RAM disk. However, if you 
-remove them, the system has to update various files, something that 
-I<does> consume some space on the virtual RAM disk.)
-
-=back
-
-=back
-
-=item *
-
-Ubuntu
-
-Ubuntu is an extremely popular offshoot of Debian.  It provides 
-cutting edge versions of many common tools, application and libraries 
-in an easy-to-run live CD configuration (and because a single download 
-option can be used for both live CD and install-to-disk usage, it 
-keeps your download options nice and simple).  As with Debian 5, you 
-should be able to generate a fully function Catalyst environment in a 
-matter of minutes.  Here are quick instructions on how to use Ubuntu 
-to prepare for the tutorial:
-
-=over 4
+Catalyst::Devel v1.34
 
 =item * 
 
-Download the Ubuntu Desktop edition and boot from the CD and/or image 
-file, select your language, and then "Try Ubuntu without any changes 
-to your computer."
+DBIx::Class v0.08195
 
 =item *
 
-Open a terminal session (click "Applications" in the upper-left 
-corner, then "Accessories," then "Terminal").
+Catalyst::Model::DBIC::Schema v0.54
 
 =item *
 
-Add the 'universe' repositories:
-
-    sudo gedit /etc/apt/sources.list
+Template Toolkit v2.22
 
-And remove the comments from the lines under the comments about the
-'universe' repositories.
 
 =item *
 
-Install Catalyst:
-
-    sudo aptitude update
-    sudo aptitude install libdbd-sqlite3-perl libcatalyst-perl libcatalyst-modules-perl libconfig-general-perl
-
-Accept all of the dependencies.  Done.  
-
-If you are running from the Live CD, you probably also want to free up 
-some disk space with the following:
-
-    sudo aptitude clean
-
-NOTE: While the instructions above mention the live CD because that 
-makes it easy for people new to Linux, you can obviously also use one 
-of the options to install Ubuntu on your drive.
-
-=back
-
-=item * 
-
-Matt Trout's C<cat-install>
-
-Available at L<http://www.shadowcat.co.uk/static/cat-install>, 
-C<cat-install> can be a fairly painless way to get Catalyst up and 
-running.  Just download the script from the link above and type C<perl 
-cat-install>.  Depending on the speed of your Internet connection and 
-your computer, it will probably take 30 to 60 minutes to install because 
-it downloads, makes, compiles, and tests every module.  But this is an 
-excellent way to automate the installation of all the latest modules 
-used by Catalyst from CPAN.
-
+HTML::FormFu -- v0.09004
 
 =item * 
 
-Other Possibilities
-
-=over 4
-
-=item *
-
-OpenBSD Packages
+B<NOTE:> You can check the versions you have installed with the
+following command (note the slash before the space):
 
-The 2008 Advent Day 4 entry has more information on using OpenBSD 
-packages to quickly build a system: 
-L<http://www.catalystframework.org/calendar/2008/4>.
+    perl -M<_mod_name_>\ 999
 
-=item *
+or:
 
-NetBSD Package Collection on Solaris
+    perl -M<_mod_name_> -e 'print "$<_mod_name_>::VERSION\n"'
 
-The 2008 Advent Day 15 entry has more information on using C<pkgsrc> and 
-NetBSD packages on Solaris: 
-L<http://www.catalystframework.org/calendar/2008/15>.
+For example:
 
-=item * 
+    perl -MCatalyst::Devel\ 999
 
-CatInABox
+or:
 
-You can get more information at 
-L<http://www.catalystframework.org/calendar/2008/7>
-or L<Perl::Dist::CatInABox|Perl::Dist::CatInABox>.
+    perl -MCatalyst::Devel -e 'print "$Catalyst::Devel::VERSION\n";'
 
 =item * 
 
-Frank Speiser's Amazon EC2 Catalyst SDK
-
-There are currently two flavors of publicly available Amazon Machine
-Images (AMI) that include all the elements you'd need to begin
-developing in a fully functional Catalyst environment within minutes.
-See L<Catalyst::Manual::Installation|Catalyst::Manual::Installation>
-for more details.
-
-=back
+This tutorial will show URLs in the format of C<http://localhost:3000>,
+but if you are running your web browser from outside the Tutorial
+Virtual Machine, you will want to substitute the IP address of your VM
+for the C<localhost> in the URLs (again, you can get the IP address for
+eth0 from the C<ifconfig> command).  For example, if your VM has an
+IP address of 192.168.0.12, you will want to use a base URL of
+C<http://192.168.0.12:3000>.  Note that the development server
+defaults to port 3000 (you can change with with the "-p" option on the
+command line.
+
+B<Please Note:> Depending on the web browser you are using, you might
+need to hit C<Shift+Reload> or C<Ctrl+Reload> to pull a fresh page when
+testing your application at various points (see
+L<http://en.wikipedia.org/wiki/Wikipedia:Bypass_your_cache> for a
+comprehensive list of options for each browser).
+
+Also, the C<-k> B<keepalive option> to the development server can be
+necessary with some browsers (B<especially Internet Explorer>).
 
 =back
 
-For additional information and recommendations on Catalyst installation,
-please refer to 
-L<Catalyst::Manual::Installation|Catalyst::Manual::Installation>.
-
 
 =head1 DATABASES
 
 This tutorial will primarily focus on SQLite because of its simplicity
 of installation and use; however, modifications in the script required
-to support MySQL and PostgreSQL will be presented in Appendix.
+to support MySQL and PostgreSQL will be presented in the Appendix.
 
-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
+B<Note:> One of the advantages of using tools like Catalyst and DBIC 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: most of the code generally remains the
 same.
 
 
-=head1 WHERE TO GET WORKING CODE
-
-Each chapter of the tutorial has complete code available as a tarball in 
-the main Catalyst Subversion repository (see the note at the beginning 
-of each part for the appropriate svn command to use).
-
-B<NOTE:> You can run the test cases for the final code through Chapter 8 
-with the following commands:
-
-    svn co http://dev.catalystframework.org/repos/Catalyst/trunk/examples/Tutorial/MyApp_Chapter8
-    cd MyApp_Chapter8/MyApp
-    CATALYST_DEBUG=0 prove -wl t
-
-If you wish to include the L<HTML::FormFu|HTML::FormFu> section in your tests,
-substitute C<MyApp_Chapter9_FormFu> for C<MyApp_Chapter8> in the URL
-above (don't forget to "cd" out of the Ch8 directory if you ran the code above).
-
-    svn co http://dev.catalystframework.org/repos/Catalyst/trunk/examples/Tutorial/MyApp_Chapter9_FormFu
-    cd MyApp_Chapter9_FormFu/MyApp
-    CATALYST_DEBUG=0 prove -wl t
-
-You can also fire up the application under the development server that is conveniently
-built in to Catalyst.  Just issue this command from the C<MyApp> directory where you
-ran the test suite above:
-
-    script/myapp_server.pl
-
-And the application will start.  You can try out the application by 
-pulling up C<http://localhost:3000> in your web browser (as mentioned 
-earlier, change C<localhost> to a different IP address or DNS name if 
-you are running your web browser and your Catalyst development on 
-different boxes).  We will obviously see more about how to use the 
-application as we go through the remaining chapters of the tutorial, but 
-for now you can log in using the username "test01" and a password of 
-"mypass".
+You can jump to the next chapter of the tutorial here:
+L<Catalyst Basics|Catalyst::Manual::Tutorial::02_CatalystBasics>
 
 
 =head1 AUTHOR
 
 Kennedy Clark, C<hkclark@gmail.com>
 
-Please report any errors, issues or suggestions to the author.  The
-most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
+Feel free to contact the author for any errors or suggestions, but the
+best way to report issues is via the CPAN RT Bug system at
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
 
-Copyright 2006-2010, Kennedy Clark, under the
+Copyright 2006-2011, Kennedy Clark, under the
 Creative Commons Attribution Share-Alike License Version 3.0
 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).
index 78158e6..c263c4b 100644 (file)
@@ -56,8 +56,8 @@ L<Appendices|Catalyst::Manual::Tutorial::10_Appendices>
 
 =head1 DESCRIPTION
 
-In this chapter of the tutorial, we will create a very basic Catalyst 
-web application, demonstrating a number of powerful capabilities, such 
+In this chapter of the tutorial, we will create a very basic Catalyst
+web application, demonstrating a number of powerful capabilities, such
 as:
 
 =over 4
@@ -74,7 +74,7 @@ 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:
+L<Catalyst::Manual::About>). In short:
 
 =over 4
 
@@ -89,7 +89,7 @@ database.
 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, e-mails, spreadsheets, 
+that generates other forms such as PDF documents, e-mails, spreadsheets,
 or even "behind the scenes" formats such as XML and JSON.
 
 =item * Controller
@@ -110,20 +110,20 @@ automatically create our Catalyst model for use with a database.
 
 You can checkout the source code for this example from the catalyst
 subversion repository as per the instructions in
-L<Catalyst::Manual::Tutorial::01_Intro|Catalyst::Manual::Tutorial::01_Intro>.
+L<Catalyst::Manual::Tutorial::01_Intro>.
 
 
 =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 (see 
-L<Catalyst::Helper|Catalyst::Helper> for more information on helpers). 
-Also note that as of Catalyst 5.7000, you will not have the helper 
-scripts unless you install both L<Catalyst::Runtime|Catalyst::Runtime> 
-and L<Catalyst::Devel|Catalyst::Devel>.
+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 (see
+L<Catalyst::Helper> for more information on helpers).
+Also note that as of Catalyst 5.7000, you will not have the helper
+scripts unless you install both L<Catalyst::Runtime>
+and L<Catalyst::Devel>.
 
-In this first chapter of the tutorial, use the Catalyst C<catalyst.pl> 
+In this first chapter of the tutorial, use the Catalyst C<catalyst.pl>
 script to initialize the framework for an application called C<Hello>:
 
     $ catalyst.pl Hello
@@ -169,45 +169,44 @@ directories and files it creates:
         03podcoverage.t 
 
 
-Catalyst will "auto-discover" modules in the Controller, Model, and 
-View directories. When you use the hello_create.pl script it will 
-create Perl module scaffolds in those directories, plus test files in 
-the "t" directory. The default location for templates is in the "root" 
-directory. The scripts in the script directory will always start with 
-the lowercased version of your application name. If your app is 
-MaiTai, then the create script would be "maitai_create.pl".
+Catalyst will "auto-discover" modules in the Controller, Model, and View
+directories. When you use the C<hello_create.pl> script it will create Perl
+module scaffolds in those directories, plus test files in the "t"
+directory. The default location for templates is in the "root"
+directory. The scripts in the script directory will always start with
+the lowercased version of your application name. If your app is MaiTai,
+then the create script would be "maitai_create.pl".
 
-Though it's too early for any significant celebration, we already have 
-a functioning application. We can use the Catalyst supplied script to 
-start up a development server and view the default Catalyst page in 
-your browser. All scripts in the script directory should be run from 
-the base directory of your application, so change to the Hello 
-directory.
+Though it's too early for any significant celebration, we already have a
+functioning application. We can use the Catalyst supplied script to
+start up a development server and view the default Catalyst page in your
+browser. All scripts in the script directory should be run from the base
+directory of your application, so change to the Hello directory.
 
-Run the following command to start up the built-in development web 
-server (make sure you didn't forget the "C<cd Hello>" from the 
-previous step):
+Run the following command to start up the built-in development web
+server (make sure you didn't forget the "C<cd Hello>" from the previous
+step):
 
-B<Note>: The "-r" argument enables reloading on code changes so you 
-don't have to stop and start the server when you update code. See 
+B<Note>: The "-r" argument enables reloading on code changes so you
+don't have to stop and start the server when you update code. See
 C<perldoc script/hello_server.pl> or C<script/hello_server.pl --help>
 for additional options you might find helpful. Most of the rest of the
-tutorial will assume that you are using "-r" when you start the development
-server, but feel free to manually start and stop it (use C<Ctrl-C> to
-breakout of the dev server) if you prefer. 
+tutorial will assume that you are using "-r" when you start the
+development server, but feel free to manually start and stop it (use
+C<Ctrl-C> to breakout of the dev server) if you prefer.
 
     $ script/hello_server.pl -r
     [debug] Debug messages enabled
     [debug] Statistics enabled
     [debug] Loaded plugins:
     .----------------------------------------------------------------------------.
-    | Catalyst::Plugin::ConfigLoader  0.27                                       |
+    | Catalyst::Plugin::ConfigLoader  0.30                                       |
     '----------------------------------------------------------------------------'
     
     [debug] Loaded dispatcher "Catalyst::Dispatcher"
-    [debug] Loaded engine "Catalyst::Engine::HTTP"
-    [debug] Found home "/home/me/Hello"
-    [debug] Loaded Config "/home/me/Hello/hello.conf"
+    [debug] Loaded engine "Catalyst::Engine"
+    [debug] Found home "/root/Hello"
+    [debug] Loaded Config "/root/Hello/hello.conf"
     [debug] Loaded components:
     .-----------------------------------------------------------------+----------.
     | Class                                                           | Type     |
@@ -232,28 +231,31 @@ breakout of the dev server) if you prefer.
     | /                                   | /default                             |
     '-------------------------------------+--------------------------------------'
     
-    [info] Hello powered by Catalyst 5.80020
-    You can connect to your server at http://debian:3000
-
-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 (if you get some other welcome 
-screen or an "Index" screen, you probably forgot to specify port 3000 
-in your URL).  Information similar to the following should be appended 
-to the logging output of the development server:
-
-    [info] *** Request 1 (0.001/s) [23194] [Sat Jan 16 11:09:18 2010] ***
-    [debug] "GET" request for "/" from "127.0.0.1"
+    [info] Hello powered by Catalyst 5.90002
+    HTTP::Server::PSGI: Accepting connections at http://0:3000/
+
+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 (if you get some other welcome
+screen or an "Index" screen, you probably forgot to specify port 3000 in
+your URL).  Information similar to the following should be appended to
+the logging output of the development server:
+
+    [info] Hello powered by Catalyst 5.90002
+    HTTP::Server::PSGI: Accepting connections at http://0:3000/
+    [info] *** Request 1 (0.067/s) [19026] [Tue Aug 30 17:24:32 2011] ***
+    [debug] "GET" request for "/" from "192.168.245.2"
     [debug] Path is "/"
-    [info] Request took 0.004851s (206.143/s)
+    [debug] Response Code: 200; Content-Type: text/html; charset=utf-8; Content-Length: 5613
+    [info] Request took 0.040895s (24.453/s)
     .------------------------------------------------------------+-----------.
     | Action                                                     | Time      |
     +------------------------------------------------------------+-----------+
-    | /index                                                     | 0.000395s |
-    | /end                                                       | 0.000425s |
+    | /index                                                     | 0.000916s |
+    | /end                                                       | 0.000877s |
     '------------------------------------------------------------+-----------'
 
-B<Note>: Press C<Ctrl-C> to break out of the development server if 
+B<Note>: Press C<Ctrl-C> to break out of the development server if
 necessary.
 
 
@@ -261,77 +263,73 @@ necessary.
 
 =head2 The Simplest Way
 
-The Root.pm controller is a place to put global actions that usually 
-execute on the root URL. Open the C<lib/Hello/Controller/Root.pm> file in 
-your editor. You will see the "index" subroutine, which is 
-responsible for displaying the welcome screen that you just saw in 
-your browser. 
+The Root.pm controller is a place to put global actions that usually
+execute on the root URL. Open the C<lib/Hello/Controller/Root.pm> file
+in your editor. You will see the "index" subroutine, which is
+responsible for displaying the welcome screen that you just saw in your
+browser.
 
     sub index :Path :Args(0) {
         my ( $self, $c ) = @_;
-        
+    
         # Hello World
         $c->response->body( $c->welcome_message );
     }
 
-Later on you'll want to change that to something more reasonable, such 
-as a "404" message or a redirect, but for now just leave it alone. 
-
-The "C<$c>" here refers to the Catalyst context, which is used to 
-access the Catalyst application. In addition to many other things, 
-the Catalyst context provides access to "response" and "request" 
-objects. (See L<Catalyst|Catalyst>, 
-L<Catalyst::Response|Catalyst::Response>, and 
-L<Catalyst::Request|Catalyst::Request>) 
-
-C<$c-E<gt>response-E<gt>body> sets the HTTP response (see 
-L<Catalyst::Response|Catalyst::Response>), while 
-C<$c-E<gt>welcome_message> is a special method that returns the 
-welcome message that you saw in your browser.
-
-The ":Path :Args(0)" after the method name are attributes which 
-determine which URLs will be dispatched to this method. (You might see 
-":Private" if you are using an older version of Catalyst, but using 
-that with "default" or "index" is currently deprecated.  If so, you 
-should also probably upgrade before continuing the tutorial.)
-
-Some MVC frameworks handle dispatching in a central place. Catalyst, 
-by policy, prefers to handle URL dispatching with attributes on 
-controller methods. There is a lot of flexibility in specifying which 
-URLs to match.  This particular method will match all URLs, because it 
-doesn't specify the path (nothing comes after "Path"), but will only 
-accept a URL without any args because of the ":Args(0)". 
-
-The default is to map URLs to controller names, and because of the way 
-that Perl handles namespaces through package names, it is simple to 
-create hierarchical structures in Catalyst. This means that you can 
-create controllers with deeply nested actions in a clean and logical 
-way. For example, the URL C<http://hello.com/admin/articles/create> 
-maps to the package C<Hello::Controller::Admin::Articles>, and the 
-C<create> method. 
-
-Add the following subroutine to your C<lib/Hello/Controller/Root.pm> 
-file:
+Later on you'll want to change that to something more reasonable, such
+as a "404" message or a redirect, but for now just leave it alone.
+
+The "C<$c>" here refers to the Catalyst context, which is used to access
+the Catalyst application. In addition to many other things, the Catalyst
+context provides access to "response" and "request" objects. (See
+L<Catalyst::Runtime>, L<Catalyst::Response>, and
+L<Catalyst::Request>)
+
+C<$c-E<gt>response-E<gt>body> sets the HTTP response (see
+L<Catalyst::Response>), while
+C<$c-E<gt>welcome_message> is a special method that returns the welcome
+message that you saw in your browser.
+
+The ":Path :Args(0)" after the method name are attributes which
+determine which URLs will be dispatched to this method. (You might see
+":Private" if you are using an older version of Catalyst, but using that
+with "default" or "index" is currently deprecated.  If so, you should
+also probably upgrade before continuing the tutorial.)
+
+Some MVC frameworks handle dispatching in a central place. Catalyst, by
+policy, prefers to handle URL dispatching with attributes on controller
+methods. There is a lot of flexibility in specifying which URLs to
+match.  This particular method will match all URLs, because it doesn't
+specify the path (nothing comes after "Path"), but will only accept a
+URL without any args because of the ":Args(0)".
+
+The default is to map URLs to controller names, and because of the way
+that Perl handles namespaces through package names, it is simple to
+create hierarchical structures in Catalyst. This means that you can
+create controllers with deeply nested actions in a clean and logical
+way. For example, the URL C<http://hello.com/admin/articles/create> maps
+to the package C<Hello::Controller::Admin::Articles>, and the C<create>
+method.
+
+While you leave the C<script/hello_server.pl -r> command running the
+development server in one window (don't forget the "-r" on the end!),
+open another window and add the following subroutine to your
+C<lib/Hello/Controller/Root.pm> file:
 
     sub hello :Global {
         my ( $self, $c ) = @_;
-        
+    
         $c->response->body("Hello, World!");
     }
 
-Once you restart the server, you will need to use the new address
-L<http://localhost:3000/hello> instead of L<http://localhost:3000/> to see the
-changes.
-
 B<TIP>: See Appendix 1 for tips on removing the leading spaces when
 cutting and pasting example code from POD-based documents.
 
-Here you're sending your own string to the webpage.
-
-Save the file, and you should notice the following in your server output:
+Notice in the window running the Development Server that you should
+get output similar to the following:
 
     Saw changes to the following files:
-     - /home/me/Hello/lib/Hello/Controller/Root.pm (modify)
+     - /root/Hello/lib/Hello/Controller/Root.pm (modify)
     
     Attempting to restart the server
     ...
@@ -344,37 +342,31 @@ Save the file, and you should notice the following in your server output:
     | /index               | Hello::Controller::Root              | index        |
     | /hello               | Hello::Controller::Root              | hello        |
     '----------------------+--------------------------------------+--------------'
-    
-    [debug] Loaded Path actions:
-    .-------------------------------------+--------------------------------------.
-    | Path                                | Private                              |
-    +-------------------------------------+--------------------------------------+
-    | /                                   | /index                               |
-    | /                                   | /default                             |
-    | /hello                              | /hello                               |
-    '-------------------------------------+--------------------------------------'
     ...
 
-Go to L<http://localhost:3000/hello> to see "Hello, World!".   Also 
-notice that the newly defined 'hello' action is listed under "Loaded 
+The development server noticed the change in C<Hello::Controller::Root>
+and automatically restarted itself.
+
+Go to L<http://localhost:3000/hello> to see "Hello, World!".   Also
+notice that the newly defined 'hello' action is listed under "Loaded
 Private actions" in the development server debug output.
 
 
 =head2 Hello, World! Using a View and a Template
 
-In the Catalyst world a "View" itself is not a page of XHTML or a 
-template designed to present a page to a browser. Rather, it is the 
-module that determines the I<type> of view -- HTML, pdf, XML, etc. For 
-the thing that generates the I<content> of that view (such as the a 
-Toolkit Template template file), the actual templates go under the 
+In the Catalyst world a "View" itself is not a page of XHTML or a
+template designed to present a page to a browser. Rather, it is the
+module that determines the I<type> of view -- HTML, pdf, XML, etc. For
+the thing that generates the I<content> of that view (such as a
+Toolkit Template template file), the actual templates go under the
 "root" directory.
 
 To create a TT view, run:
 
     $ script/hello_create.pl view HTML TT
 
-This creates the C<lib/Hello/View/HTML.pm> module, which is a subclass of 
-C<Catalyst::View::TT>. 
+This creates the C<lib/Hello/View/HTML.pm> module, which is a subclass
+of C<Catalyst::View::TT>.
 
 =over 4
 
@@ -387,70 +379,70 @@ The "view" keyword tells the create script that you are creating a view.
 The first argument "HTML" tells the script to name the View module "HTML.pm",
 which is a commonly used name for TT views.  You can name it anything you want,
 such as "MyView.pm". If you have more than one view, be sure to set the
-default_view in Hello.pm (See L<Catalyst::View::TT|Catalyst::View::TT> for more
+default_view in Hello.pm (See L<Catalyst::View::TT> for more
 details on setting this).
 
 =item *
 
-The final "TT" tells Catalyst the I<type> of the view, with "TT" 
+The final "TT" tells Catalyst the I<type> of the view, with "TT"
 indicating that you want to a Template Toolkit view.
 
 =back
 
-If you look at C<lib/Hello/View/HTML.pm> you will find that it only 
+If you look at C<lib/Hello/View/HTML.pm> you will find that it only
 contains a config statement to set the TT extension to ".tt".
 
-Now that the HTML.pm "View" exists, Catalyst will autodiscover it and be 
-able to use it to display the view templates using the "process" 
-method that it inherits from the C<Catalyst::View::TT> class.
+Now that the HTML.pm "View" exists, Catalyst will autodiscover it and be
+able to use it to display the view templates using the "process" method
+that it inherits from the C<Catalyst::View::TT> class.
 
-Template Toolkit is a very full featured template facility, with 
-excellent documentation at L<http://template-toolkit.org/>, 
-but since this is not a TT tutorial, we'll stick to only basic TT 
-usage here (and explore some of the more common TT features in later 
-chapters of the tutorial).
+Template Toolkit is a very full featured template facility, with
+excellent documentation at L<http://template-toolkit.org/>, but since
+this is not a TT tutorial, we'll stick to only basic TT usage here (and
+explore some of the more common TT features in later chapters of the
+tutorial).
 
-Create a C<root/hello.tt> template file (put it in the C<root> under 
-the C<Hello> directory that is the base of your application). Here is 
-a simple sample:
+Create a C<root/hello.tt> template file (put it in the C<root> under the
+C<Hello> directory that is the base of your application). Here is a
+simple sample:
 
     <p>
         This is a TT view template, called '[% template.name %]'.
     </p>
 
-[% and %] are markers for the TT parts of the template. Inside you can 
-access Perl variables and classes, and use TT directives. In this 
-case, we're using a special TT variable that defines the name of the 
-template file (C<hello.tt>).  The rest of the template is normal HTML. 
+[% and %] are markers for the TT parts of the template. Inside you can
+access Perl variables and classes, and use TT directives. In this case,
+we're using a special TT variable that defines the name of the template
+file (C<hello.tt>).  The rest of the template is normal HTML.
 
-Change the hello method in C<lib/Hello/Controller/Root.pm> to the 
+Change the hello method in C<lib/Hello/Controller/Root.pm> to the
 following:
 
     sub hello :Global {
         my ( $self, $c ) = @_;
-        
+    
         $c->stash(template => 'hello.tt');
     }
 
-This time, instead of doing C<$c-E<gt>response-E<gt>body()>, you are 
-setting the value of the "template" hash key in the Catalyst "stash", 
-an area for putting information to share with other parts of your 
-application. The "template" key determines which template will be 
-displayed at the end of the request cycle. Catalyst controllers have a 
-default "end" action for all methods which causes the first (or 
-default) view to be rendered (unless there's a C<$c-E<gt>response-
-E<gt>body()> statement). So your template will be magically displayed 
-at the end of your method.
+This time, instead of doing C<$c-E<gt>response-E<gt>body()>, you are
+setting the value of the "template" hash key in the Catalyst "stash", an
+area for putting information to share with other parts of your
+application. The "template" key determines which template will be
+displayed at the end of the request cycle. Catalyst controllers have a
+default "end" action for all methods which causes the first (or default)
+view to be rendered (unless there's a C<$c-E<gt>response- E<gt>body()>
+statement). So your template will be magically displayed at the end of
+your method.
 
 After saving the file, the development server should automatically
-restart (again, the tutorial is written to assume that you are
-using the "-r" option -- manually restart it if you aren't), 
-and look at L<http://localhost:3000/hello> in your again. You 
-should see the template that you just made.
-
-B<TIP:> If you keep the server running with "-r" in a "background 
-window," don't let that window get totally hidden... if you have an 
-syntax error in your code, the debug server output will contain the 
+restart (again, the tutorial is written to assume that you are using the
+"-r" option -- manually restart it if you aren't), and look at
+L<http://localhost:3000/hello> in your again. You should see the
+template that you just made.
+
+B<TIP:> If you keep the server running with "-r" in a "background
+window," don't let that window get totally hidden... if you have an
+syntax error in your code, the debug server output will contain the
 error information.
 
 B<Note:> You will probably run into a variation of the "stash"
@@ -471,8 +463,8 @@ You can also set multiple stash values with a hashref:
               another_thing => 1});
 
 Any of these formats work, but the C<$c-E<gt>stash(name =E<gt> value);>
-style is growing in popularity -- you may wish to use it all the 
-time (even when you are only setting a single value).
+style is growing in popularity -- you may wish to use it all the time
+(even when you are only setting a single value).
 
 
 =head1 CREATE A SIMPLE CONTROLLER AND AN ACTION
@@ -481,9 +473,9 @@ Create a controller named "Site" by executing the create script:
 
     $ script/hello_create.pl controller Site
 
-This will create a C<lib/Hello/Controller/Site.pm> file (and a test 
-file). Bring Site.pm up in your editor, and you can see that there's 
-not much there. 
+This will create a C<lib/Hello/Controller/Site.pm> file (and a test
+file). Bring Site.pm up in your editor, and you can see that there's not
+much there.
 
 In C<lib/Hello/Controller/Site.pm>, add the following method:
 
@@ -494,36 +486,43 @@ In C<lib/Hello/Controller/Site.pm>, add the following method:
                   template => 'site/test.tt');
     }
 
-Notice the "Local" attribute on the C<test> method. This will cause 
-the C<test> action (now that we have assigned an "action type" to the 
-method it appears as a "controller action" to Catalyst) to be executed 
-on the "controller/method" URL, or, in this case, "site/test".  We 
-will see additional information on controller actions throughout the 
-rest of the tutorial, but if you are curious take a look at 
+Notice the "Local" attribute on the C<test> method. This will cause the
+C<test> action (now that we have assigned an "action type" to the method
+it appears as a "controller action" to Catalyst) to be executed on the
+"controller/method" URL, or, in this case, "site/test".  We will see
+additional information on controller actions throughout the rest of the
+tutorial, but if you are curious take a look at
 L<Catalyst::Manual::Intro/Actions>.
 
-It's not actually necessary to set the template value as we do here. 
-By default TT will attempt to render a template that follows the 
-naming pattern "controller/method.tt", and we're following that 
-pattern here. However, in other situations you will need to specify 
-the template (such as if you've "forwarded" to the method, or if it 
-doesn't follow the default naming convention).
+It's not actually necessary to set the template value as we do here.  By
+default TT will attempt to render a template that follows the naming
+pattern "controller/method.tt", and we're following that pattern here.
+However, in other situations you will need to specify the template (such
+as if you've "forwarded" to the method, or if it doesn't follow the
+default naming convention).
 
-We've also put the variable "username" into the stash, for use in the 
+We've also put the variable "username" into the stash, for use in the
 template.
 
-Make a subdirectory "site" in the "root" directory. Copy the hello.tt 
-file into the directory as C<root/site/test.tt>, or create a new 
-template file at that location. Include a line like: 
+Make a subdirectory "site" in the "root" directory.
+
+    $ mkdir root/site
+
+Create a new template file in that direction named C<root/site/test.tt>
+and include a line like:
 
     <p>Hello, [% username %]!</p>
 
-You should see your test.tt file displayed, including the name "John"
-that you set in the controller.
+Once the server automatically restarts, notice in the server output that
+C</site/test> is listed in the Loaded Path actions.  Go to
+L<http://localhost:3000/site/test> in your browser and you should see
+your test.tt file displayed, including the name "John" that you set in
+the controller.
+
+
+You can jump to the next chapter of the tutorial here:
+L<More Catalyst Basics|Catalyst::Manual::Tutorial::03_MoreCatalystBasics>
 
-Once the server automatically restarts, notice in the server 
-output that C</site/test> is listed in the Loaded Path actions. 
-Go to L<http://localhost:3000/site/test> in your browser.
 
 
 =head1 AUTHORS
@@ -531,10 +530,10 @@ Go to L<http://localhost:3000/site/test> in your browser.
 Gerda Shank, C<gerda.shank@gmail.com>
 Kennedy Clark, C<hkclark@gmail.com>
 
-Please report any errors, issues or suggestions to the author.  The
-most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
+Feel free to contact the author for any errors or suggestions, but the
+best way to report issues is via the CPAN RT Bug system at
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
 
-Copyright 2006-2010, Kennedy Clark, under the
+Copyright 2006-2011, Kennedy Clark, under the
 Creative Commons Attribution Share-Alike License Version 3.0
 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).
index 4b97984..56691be 100644 (file)
@@ -56,24 +56,27 @@ L<Appendices|Catalyst::Manual::Tutorial::10_Appendices>
 
 =head1 DESCRIPTION
 
-This chapter of the tutorial builds on the work done in Chapter 2 to 
-explore some features that are more typical of "real world" web 
-applications. From this chapter of the tutorial onward, we will be 
-building a simple book database application.  Although the application 
-will be too limited to be of use to anyone, it should provide a basic 
-environment where we can explore a variety of features used in 
-virtually all web applications.
-
-You can check out the source code for this example from the Catalyst
-Subversion repository as per the instructions in
-L<Catalyst::Manual::Tutorial::01_Intro|Catalyst::Manual::Tutorial::01_Intro>.
-
-Please take a look at 
-L<Catalyst::Manual::Tutorial::01_Intro/CATALYST INSTALLATION> before 
-doing the rest of this tutorial.  Although the tutorial should work 
-correctly under most any recent version of Perl running on any 
-operating system, the tutorial has been written using Debian 5 and 
-tested to be sure it runs correctly in this environment.  
+This chapter of the tutorial builds on the work done in Chapter 2 to
+explore some features that are more typical of "real world" web
+applications. From this chapter of the tutorial onward, we will be
+building a simple book database application.  Although the application
+will be too limited to be of use to anyone, it should provide a basic
+environment where we can explore a variety of features used in virtually
+all web applications.
+
+Source code for the tutorial in included in the F</root/Final> directory
+of the Tutorial Virtual machine (one subdirectory per chapter).  There
+are also instructions for downloading the code in
+L<Catalyst::Manual::Tutorial::01_Intro>.
+
+Please take a look at
+L<Catalyst::Manual::Tutorial::01_Intro/STARTING WITH THE TUTORIAL VIRTUAL MACHINE>
+before doing the rest of this tutorial.  Although the tutorial should
+work correctly under most any recent version of Perl running on any
+operating system, the tutorial has been written using the virtual
+machine that is available for download.  The entire tutorial has been
+tested to be sure it runs correctly in this environment, so it is
+the most trouble-free way to get started with Catalyst.
 
 
 =head1 CREATE A NEW APPLICATION
@@ -92,25 +95,28 @@ tutorial or in a directory that already has a "MyApp" subdirectory):
     ...
     created "MyApp/script/myapp_create.pl"
     Change to application directory and Run "perl Makefile.PL" to make sure your install is complete
+
+And change the "MyApp" directory the helper created:
+
     $ cd MyApp
 
 This creates a similar skeletal structure to what we saw in Chapter 2 of
-the tutorial, except with C<MyApp> and C<myapp> substituted for
-C<Hello> and C<hello>.  (As noted in Chapter 2, omit the ".pl" from 
-the command if you are using Strawberry Perl.)
+the tutorial, except with C<MyApp> and C<myapp> substituted for C<Hello>
+and C<hello>.  (As noted in Chapter 2, omit the ".pl" from the command
+if you are using Strawberry Perl.)
 
 
 =head1 EDIT THE LIST OF CATALYST PLUGINS
 
-One of the greatest benefits of Catalyst is that it has such a large 
-library of bases classes and plugins available that you can use easily 
-add functionality to your application. 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. 
-
+One of the greatest benefits of Catalyst is that it has such a large
+library of bases classes and plugins available that you can use easily
+add functionality to your application. 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.
 
+Take a look at the file C<lib/MyApp.pm> that the helper created above.
 By default, Catalyst enables three plugins/flags:
 
 =over 4
@@ -123,12 +129,13 @@ Enables the Catalyst debug output you saw when we started the
 C<script/myapp_server.pl> development server earlier.  You can remove
 this item when you place your application into production.
 
-To be technically correct, it turns out that C<-Debug> is not a plugin, but a I<flag>. 
-Although most of the items specified on the C<__PACKAGE__-E<gt>setup> 
-line of your application class will be plugins, Catalyst supports a 
-limited number of flag options (of these, C<-Debug> is the most 
-common).  See the documentation for C<Catalyst.pm> to get details on 
-other flags (currently C<-Engine>, C<-Home>, and C<-Log>).
+To be technically correct, it turns out that 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).  See the documentation for
+C<https://metacpan.org/module/Catalyst|Catalyst.pm> to get details on
+other flags (currently C<-Engine>, C<-Home>, C<-Log>, and C<-Stats>).
 
 If you prefer, there are several other ways to enable debug output:
 
@@ -136,7 +143,7 @@ If you prefer, there are several other ways to enable debug output:
 
 =item *
 
-Use the C<$c-E<gt>debug> method
+Use the C<$c-E<gt>debug> method on the C<$c> Catalyst context object
 
 =item *
 
@@ -144,42 +151,43 @@ The C<-d> option to C<script/myapp_server.pl>
 
 =item *
 
-The C<CATALYST_DEBUG=1> environment variable (or set it to
-zero to temporarily disable debug output).
+The C<CATALYST_DEBUG=1> environment variable (or use C<CATALYST_DEBUG=0>
+to temporarily disable debug output).
 
 =back
 
-B<TIP>: Depending on your needs, it can be helpful to permanently
-remove C<-Debug> from C<lib/MyApp.pm> and then use the C<-d> option
-to C<script/myapp_server.pl> to re-enable it just for the development
-server.  We will not be using that approach in the tutorial, but feel
-free to make use of it in your own projects.
+B<TIP>: Depending on your needs, it can be helpful to permanently remove
+C<-Debug> from C<lib/MyApp.pm> and then use the C<-d> option to
+C<script/myapp_server.pl> to re-enable it when needed.  We will not be
+using that approach in the tutorial, but feel free to make use of it in
+your own projects.
 
 =item *
 
-L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader>
+L<Catalyst::Plugin::ConfigLoader>
 
 C<ConfigLoader> provides an automatic way to load configurable
 parameters for your application from a central
-L<Config::General|Config::General> file (versus having the values
-hard-coded inside your Perl modules).  Config::General uses syntax
-very similar to Apache configuration files.  We will see how to use
-this feature of Catalyst during the authentication and authorization
-sections (Chapter 5 and Chapter 6).
-
-B<IMPORTANT NOTE:> If you are using a version of 
-L<Catalyst::Devel|Catalyst::Devel> prior to version 1.06, be aware 
-that Catalyst changed the default format from YAML to the more 
-straightforward C<Config::General> style.  This tutorial uses the 
-newer C<myapp.conf> file for C<Config::General>. However, Catalyst 
-supports both formats and will automatically use either C<myapp.conf> 
-or C<myapp.yml> (or any other format supported by 
-L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader> and 
-L<Config::Any|Config::Any>).  If you are using a version of 
-Catalyst::Devel prior to 1.06, you can convert to the newer format by 
-simply creating the C<myapp.conf> file manually and deleting 
-C<myapp.yml>.  The default contents of the C<myapp.conf> you create 
-should only consist of one line: 
+L<Config::General> file (versus having the values
+hard-coded inside your Perl modules).  Config::General uses syntax very
+similar to Apache configuration files.  We will see how to use this
+feature of Catalyst during the authentication and authorization sections
+(L<Chapter 5|Catalyst::Manual::Tutorial::05_Authentication> and
+L<Chapter 6|Catalyst::Manual::Tutorial::06_Authorization>).
+
+B<IMPORTANT NOTE:> If you are using a version of
+L<Catalyst::Devel> prior to version 1.06, be aware that
+Catalyst changed the default format from YAML to the more
+straightforward C<Config::General> style.  This tutorial uses the newer
+C<myapp.conf> file for C<Config::General>. However, Catalyst supports
+both formats and will automatically use either C<myapp.conf> or
+C<myapp.yml> (or any other format supported by
+L<Catalyst::Plugin::ConfigLoader> and
+L<Config::Any>).  If you are using a version of
+Catalyst::Devel prior to 1.06, you can convert to the newer format by
+simply creating the C<myapp.conf> file manually and deleting
+C<myapp.yml>.  The default contents of the C<myapp.conf> you create
+should only consist of one line:
 
     name MyApp
 
@@ -191,16 +199,16 @@ formats:
 
 =item *
 
-L<Catalyst::Plugin::Static::Simple|Catalyst::Plugin::Static::Simple>
+L<Catalyst::Plugin::Static::Simple>
 
-C<Static::Simple> provides an easy way to serve static content, such
-as images and CSS files, from the development server.
+C<Static::Simple> provides an easy way to serve static content, such as
+images and CSS files, from the development server.
 
 =back
 
-For our application, we want to add one new plugin into the mix.  To 
-do this, edit C<lib/MyApp.pm> (this file is generally referred to as 
-your I<application class>) and delete the lines with:
+For our application, we want to add one new plugin into the mix.  To do
+this, edit C<lib/MyApp.pm> (this file is generally referred to as your
+I<application class>) and delete the lines with:
 
     use Catalyst qw/
         -Debug
@@ -219,53 +227,65 @@ Then replace it with:
         StackTrace
     /;
 
-B<Note:> Recent versions of C<Catalyst::Devel> have used a variety of 
-techniques to load these plugins/flags.  For example, you might see
-the following:
+B<Note:> Recent versions of C<Catalyst::Devel> have used a variety of
+techniques to load these plugins/flags.  For example, you might see the
+following:
 
     __PACKAGE__->setup(qw/-Debug ConfigLoader Static::Simple/);
 
-Don't let these variations confuse you -- they all accomplish the same 
+Don't let these variations confuse you -- they all accomplish the same
 result.
 
-This tells Catalyst to start using one additional plugin, 
-L<Catalyst::Plugin::StackTrace>, to add a 
-stack trace to the standard Catalyst "debug screen" (the screen 
-Catalyst sends to your browser when an error occurs). Be aware that 
-L<StackTrace|Catalyst::Plugin::StackTrace> output appears in your 
-browser, not in the console window from which you're running your 
+This tells Catalyst to start using one additional plugin,
+L<Catalyst::Plugin::StackTrace>, to add a stack trace near the top of
+the standard Catalyst "debug screen" (the screen Catalyst sends to your
+browser when an error occurs). Be aware that
+L<StackTrace|Catalyst::Plugin::StackTrace> output appears in your
+browser, not in the console window from which you're running your
 application, which is where logging output usually goes.
 
 Make sure when adding new plugins you also include them as a new
-dependency within the Makefile.PL file. For example, after adding
-the StackTrace plugin the Makefile.PL should include the following
-line:
+dependency within the Makefile.PL file. For example, after adding the
+StackTrace plugin the Makefile.PL should include the following line:
 
     requires 'Catalyst::Plugin::StackTrace';
 
 
-B<Notes:> 
+B<Notes:>
 
 =over 4
 
 =item *
 
-C<__PACKAGE__> is just a shorthand way of referencing the name of the 
-package where it is used.  Therefore, in C<MyApp.pm>, C<__PACKAGE__> 
-is equivalent to C<MyApp>.
+C<__PACKAGE__> is just a shorthand way of referencing the name of the
+package where it is used.  Therefore, in C<MyApp.pm>, C<__PACKAGE__> is
+equivalent to C<MyApp>.
 
 =item *
 
-You will want to disable L<StackTrace|Catalyst::Plugin::StackTrace> 
-before you put your application into production, but it can be helpful 
+You will want to disable L<StackTrace|Catalyst::Plugin::StackTrace>
+before you put your application into production, but it can be helpful
 during development.
 
 =item *
 
-When specifying plugins, you can omit C<Catalyst::Plugin::> from the 
-name.  Additionally, you can spread the plugin names across multiple 
+When specifying plugins, 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 line.
 
+=item *
+
+If you want to see what the StackTrace error screen looks like, edit
+C<lib/MyApp/Controller/Root.pm> and put a C<die "Oops";> command in the
+C<sub index :Path :Args(0)> method.  Then start the development server
+and open C<http://localhost:3000/> in your browser.  You should get a
+screen that starts with "Caught exception in
+MyApp::Controller::Root->index" with sections showing a stacktrace,
+information about the Request and Response objects, the stash (something
+we will learn about soon), the applications configuration configuration.
+B<Just don't forget to remove the die before you continue the tutorial!>
+:-)
+
 =back
 
 
@@ -279,12 +299,13 @@ Use the Catalyst C<create> script to add a controller for book-related
 actions:
 
     $ script/myapp_create.pl controller Books
-     exists "/home/me/MyApp/script/../lib/MyApp/Controller"
-     exists "/home/me/MyApp/script/../t"
-    created "/home/me/MyApp/script/../lib/MyApp/Controller/Books.pm"
-    created "/home/me/MyApp/script/../t/controller_Books.t"
+     exists "/root/MyApp/script/../lib/MyApp/Controller"
+     exists "/root/MyApp/script/../t"
+    created "/root/MyApp/script/../lib/MyApp/Controller/Books.pm"
+    created "/root/MyApp/script/../t/controller_Books.t"
 
-Then edit C<lib/MyApp/Controller/Books.pm> (as discussed in Chapter 2 of
+Then edit C<lib/MyApp/Controller/Books.pm> (as discussed in
+L<Chapter 2|Catalyst::Manual::Tutorial::02_CatalystBasics> of
 the Tutorial, Catalyst has a separate directory under C<lib/MyApp> for
 each of the three parts of MVC: C<Model>, C<View>, and C<Controller>)
 and add the following method to the controller:
@@ -313,37 +334,41 @@ and add the following method to the controller:
         $c->stash(template => 'books/list.tt2');
     }
 
-B<TIP>: See Appendix 1 for tips on removing the leading spaces when
-cutting and pasting example code from POD-based documents.
-
-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.
-
-Catalyst actions are regular Perl methods, but they make use of 
-attributes (the "C<:Local>" next to the "C<sub list>" in the code 
-above) to provide additional information to the Catalyst dispatcher 
-logic (note that the space between the colon and the attribute name is 
-optional; you will see attributes written both ways).  Most Catalyst 
-Controllers use one of five action types:
+B<TIP>: See L<Appendix 1|Catalyst::Manual::Tutorial::10_Appendices> for
+tips on removing the leading spaces when cutting and pasting example
+code from POD-based documents.
+
+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.  This is the "Catalyst Context object", and it is
+automatically passed as the second argument to all Catalyst action
+methods.  It is used to pass information between components and provide
+access to Catalyst and plugin functionality.
+
+Catalyst Controller actions are regular Perl methods, but they make use
+of attributes (the "C<:Local>" next to the "C<sub list>" in the code
+above) to provide additional information to the Catalyst dispatcher
+logic (note that there can be an optional space between the colon and
+the attribute name; you will see attributes written both ways).  Most
+Catalyst Controllers use one of five action types:
 
 =over 4
 
 =item *
 
-B<:Private> -- Use C<:Private> for methods that you want to make into 
-an action, but you do not want Catalyst to directly expose  the method 
-to your users.  Catalyst will not map C<:Private> methods to a URI. 
-Use them for various sorts of "special" methods (the C<begin>, 
-C<auto>, etc. discussed below) or for methods you want to be able to 
-C<forward> or C<detach> to.  (If the method is a plain old "helper 
-method" that you don't want to be an action at all, then just define 
-the method without any attribute -- you can call it in your code, but 
-the Catalyst dispatcher will ignore it.)
+B<:Private> -- Use C<:Private> for methods that you want to make into an
+action, but you do not want Catalyst to directly expose the method to
+your users.  Catalyst will not map C<:Private> methods to a URI.  Use
+them for various sorts of "special" methods (the C<begin>, C<auto>, etc.
+discussed below) or for methods you want to be able to C<forward> or
+C<detach> to.  (If the method is a "plain old method" that you
+don't want to be an action at all, then just define the method without
+any attribute -- you can call it in your code, but the Catalyst
+dispatcher will ignore it.  You will also have to manually include
+C<$c> if you want access to the context object in the method vs. having
+Catalyst automatically include C<$c> in the argument list for you
+if it's a full-fledged action.)
 
 There are five types of "special" built-in C<:Private> actions:
 C<begin>, C<end>, C<default>, C<index>, and C<auto>.
@@ -369,88 +394,91 @@ controller down through the most specific class>.
 
 =item *
 
-B<:Path> -- C<:Path> actions let you map a method to an explicit URI 
-path.  For example, "C<:Path('list')>" in 
-C<lib/MyApp/Controller/Books.pm> would match on the URL 
-C<http://localhost:3000/books/list>, but "C<:Path('/list')>" would 
-match on C<http://localhost:3000/list> (because of the leading slash). 
-You can use C<:Args()> to specify how many arguments an action should 
-accept.  See L<Catalyst::Manual::Intro/Action_types> for more 
+B<:Path> -- C<:Path> actions let you map a method to an explicit URI
+path.  For example, "C<:Path('list')>" in
+C<lib/MyApp/Controller/Books.pm> would match on the URL
+C<http://localhost:3000/books/list>, but "C<:Path('/list')>" would match
+on C<http://localhost:3000/list> (because of the leading slash).  You
+can use C<:Args()> to specify how many arguments an action should
+accept.  See L<Catalyst::Manual::Intro/Action_types> for more
 information and examples.
 
 =item *
 
-B<:Local> -- C<:Local> is merely a shorthand for 
+B<:Local> -- C<:Local> is merely a shorthand for
 "C<:Path('_name_of_method_')>".  For example, these are equivalent:
-"C<sub create_book :Local {...}>" and 
+"C<sub create_book :Local {...}>" and
 "C<sub create_book :Path('create_book') {...}>".
 
 =item *
 
-B<:Global> -- C<:Global> is merely a shorthand for 
+B<:Global> -- C<:Global> is merely a shorthand for
 "C<:Path('/_name_of_method_')>".  For example, these are equivalent:
-"C<sub create_book :Global {...}>" and 
-"C<sub create_book :Path('/create_book') {...}>".
+"C<sub create_book :Global {...}>" and "C<sub create_book
+:Path('/create_book') {...}>".
 
 =item *
 
-B<:Chained> -- Newer Catalyst applications tend to use the Chained 
-dispatch form of action types because of its power and flexibility.  
-It allows a series of controller methods to be automatically dispatched
-to service a single user request.  See 
-L<Catalyst::Manual::Tutorial::04_BasicCRUD|Catalyst::Manual::Tutorial::04_BasicCRUD> 
-and L<Catalyst::DispatchType::Chained|Catalyst::DispatchType::Chained> 
-for more information on chained actions.
+B<:Chained> -- Newer Catalyst applications tend to use the Chained
+dispatch form of action types because of its power and flexibility.  It
+allows a series of controller methods to be automatically dispatched
+when servicing a single user request.  See
+L<Catalyst::Manual::Tutorial::04_BasicCRUD> and
+L<Catalyst::DispatchType::Chained> for more information on chained
+actions.
 
 =back
 
-You should refer to L<Catalyst::Manual::Intro/Action_types> for 
-additional information and for coverage of some lesser-used action 
-types not discussed here (C<Regex> and C<LocalRegex>).
+You should refer to L<Catalyst::Manual::Intro/Action-types> for
+additional information and for coverage of some lesser-used action types
+not discussed here (C<Regex> and C<LocalRegex>).
 
 
 =head1 CATALYST VIEWS
 
-As mentioned in Chapter 2 of the tutorial, views are where you render
-output, typically for display in the user's web browser (but also
-possibly using into output-generation systems, such as PDF or JSON).
-The code in C<lib/MyApp/View> selects the I<type> of view to use, with
-the actual rendering template found in the C<root> directory.  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
-somewhat popular view technologies include Mason
-(L<http://www.masonhq.com> and L<http://www.masonbook.com>) and
-L<HTML::Template> (L<http://html-template.sourceforge.net>).
+As mentioned in L<Chapter 2|Catalyst::Manual::Tutorial::02_CatalystBasics>
+of the tutorial, views are where you render output, typically for
+display in the user's web browser (but can generate other types of
+output such as PDF or JSON).  The code in C<lib/MyApp/View> selects the
+I<type> of view to use, with the actual rendering template found in the
+C<root> directory.  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 somewhat popular view
+technologies include Mason (L<http://www.masonhq.com> and
+L<http://www.masonbook.com>) and L<HTML::Template>
+(L<http://html-template.sourceforge.net>).
 
 
 =head2 Create a Catalyst View
 
-When using TT for the Catalyst view, the main helper script
-is L<Catalyst::Helper::View::TT|Catalyst::Helper::View::TT>.
-You may also come across references to 
-L<Catalyst::Helper::View::TTSite|Catalyst::Helper::View::TTSite>,
-but its use is now deprecated.
+When using TT for the Catalyst view, the main helper script is
+L<Catalyst::Helper::View::TT>.  You may also come across references to
+L<Catalyst::Helper::View::TTSite>, but its use is now deprecated.
 
-Enter the following command to enable the C<TT> style of view
-rendering for this tutorial:
+For our book application, enter the following command to enable the
+C<TT> style of view rendering:
 
     $ script/myapp_create.pl view HTML TT
-     exists "/home/me/MyApp/script/../lib/MyApp/View"
-     exists "/home/me/MyApp/script/../t"
-     created "/home/me/MyApp/script/../lib/MyApp/View/HTML.pm"
-     created "/home/me/MyApp/script/../t/view_HTML.t"
-
-This simply creates a view called C<HTML> in a file called C<HTML.pm> (the first
-argument). It is now up to you to decide how you want to structure your view
-layout.  For the tutorial, we will start with a very simple TT template to
-initially demonstrate the concepts, but quickly migrate to a more typical
-"wrapper page" type of configuration (where the "wrapper" controls the overall
-"look and feel" of your site from a single file or set of files).
-
-Edit C<lib/MyApp/View/HTML.pm> and you should see
-something similar to the following:
+     exists "/root/MyApp/script/../lib/MyApp/View"
+     exists "/root/MyApp/script/../t"
+     created "/root/MyApp/script/../lib/MyApp/View/HTML.pm"
+     created "/root/MyApp/script/../t/view_HTML.t"
+
+This creates a view called C<HTML> (the first argument) in a file called
+C<HTML.pm> that uses L<Catalyst::View::TT> (the second argument) as the
+"rendering engine".
+
+It is now up to you to decide how you want to structure your view
+layout.  For the tutorial, we will start with a very simple TT template
+to initially demonstrate the concepts, but quickly migrate to a more
+typical "wrapper page" type of configuration (where the "wrapper"
+controls the overall "look and feel" of your site from a single file or
+set of files).
+
+Edit C<lib/MyApp/View/HTML.pm> and you should see something similar to
+the following:
 
     __PACKAGE__->config(
         TEMPLATE_EXTENSION => '.tt',
@@ -468,14 +496,28 @@ And update it to match:
 This changes the default extension for Template Toolkit from '.tt' to
 '.tt2'.
 
-You can also configure components in your application class. For example,
-Edit C<lib/MyApp.pm> and you should see that the default:
+You can also configure components in your application class. For
+example, Edit C<lib/MyApp.pm> and you should see the default
+configuration above the call to C<_PACKAGE__-E<gt>setup> (your defaults
+could be different depending on the version of Catalyst you are using):
+
+    __PACKAGE__->config(
+        name => 'MyApp',
+        # Disable deprecated behavior needed by old applications
+        disable_component_resolution_regex_fallback => 1,
+    );
 
-    __PACKAGE__->setup;
 
-Above this, add config:
+Change this to match the following (insert a new
+C<__PACKAGE__-E<gt>config> below the existing statement):
 
     __PACKAGE__->config(
+        name => 'MyApp',
+        # Disable deprecated behavior needed by old applications
+        disable_component_resolution_regex_fallback => 1,
+    );
+    __PACKAGE__->config(
+        # Configure the view
         'View::HTML' => {
             #Set the location for TT files
             INCLUDE_PATH => [
@@ -483,35 +525,21 @@ Above this, add config:
             ],
         },
     );
-    # This line was here already
-    __PACKAGE__->setup;
-
-This changes the base directory for your template files from
-C<root> to C<root/src>.
-
-The reason to do this outside the C<lib/MyApp/View/HTML.pm> file
-is that the template path is found with the C<path_to> method,
-to get a path relative to the application root (no matter where it
-is installed), but this requires the application to be loaded...
 
-Trying to set this setting in the view means that you have a chicken
-and egg problem, in that the view requires the application to be loaded,
-but loading the application loads the view.
+This changes the base directory for your template files from C<root> to
+C<root/src>.
 
-Putting the configuration which depends on the application class into
-that class is the neatest way to avoid this issue.
+Please stick with the settings above for the duration of the tutorial,
+but feel free to use whatever options you desire in your applications
+(as with most things Perl, there's more than one way to do it...).
 
-Please stick with the settings above for the duration of the
-tutorial, but feel free to use whatever options you desire in your
-applications (as with most things Perl, there's more than one way to
-do it...).
-
-B<Note:> We will use C<root/src> as the base directory for our 
-template files, with a full naming convention of 
-C<root/src/_controller_name_/_action_name_.tt2>.  Another popular option is to
-use C<root/> as the base (with a full filename pattern of 
+B<Note:> We will use C<root/src> as the base directory for our template
+files, with a full naming convention of
+C<root/src/_controller_name_/_action_name_.tt2>.  Another popular option
+is to use C<root/> as the base (with a full filename pattern of
 C<root/_controller_name_/_action_name_.tt2>).
 
+
 =head2 Create a TT Template Page
 
 First create a directory for book-related TT templates:
@@ -520,14 +548,16 @@ First create a directory for book-related TT templates:
 
 Then create 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. -%]
+    [% # This is a TT comment. -%]
     
-    [% # Provide a title -%]
+    [%- # Provide a title -%]
     [% META title = 'Book List' -%]
     
+    [% # Note That the '-' at the beginning or end of TT code  -%]
+    [% # "chomps" the whitespace/newline at that end of the    -%]
+    [% # output (use View Source in browser to see the effect) -%]
+    
+    [% # Some basic HTML with a loop to display books -%]
     <table>
     <tr><th>Title</th><th>Rating</th><th>Author(s)</th></tr>
     [% # Display each book in a table row %]
@@ -542,31 +572,31 @@ Then create C<root/src/books/list.tt2> in your editor and enter:
 
 As indicated by the inline comments above, the C<META title> line uses
 TT's META feature to provide a title to the "wrapper" that we will
-create later. Meanwhile, the C<FOREACH> loop iterates through each
-C<book> model object and prints the C<title> and C<rating> fields.
+create later (and essentially does nothing at the moment). Meanwhile,
+the C<FOREACH> loop iterates through each C<book> model object and
+prints the C<title> and C<rating> fields.
 
 The C<[%> and C<%]> tags are used to delimit Template Toolkit code.  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 (".") operator.  This
 applies to operations as diverse as method calls, hash lookups, and list
-index values (see
-L<http://search.cpan.org/perldoc?Template::Manual::Variables> for
-details and examples).  In addition to the usual L<Template> module Pod
+index values (see L<Template::Manual::Variables> for details and
+examples).  In addition to the usual L<Template::Toolkit> module Pod
 documentation, you can access the TT manual at
-L<http://search.cpan.org/perldoc?Template::Manual>.
+L<https://metacpan.org/module/Template::Manual>.
 
-B<TIP:> While you can build all sorts of complex logic into your TT 
-templates, you should in general keep the "code" part of your 
-templates as simple as possible.  If you need more complex logic, 
-create helper methods in your model that abstract out a set of code 
-into a single call from your TT template.  (Note that the same is true 
-of your controller logic as well -- complex sections of code in your 
-controllers should often be pulled out and placed into your model 
-objects.)  In Chapter 4 of the tutorial we will explore some extremely 
-helpful and powerful features of L<DBIx::Class> that allow you to pull 
-code out of your views and controllers and place it where it 
-rightfully belongs in a model class.
+B<TIP:> While you can build all sorts of complex logic into your TT
+templates, you should in general keep the "code" part of your templates
+as simple as possible.  If you need more complex logic, create helper
+methods in your model that abstract out a set of code into a single call
+from your TT template.  (Note that the same is true of your controller
+logic as well -- complex sections of code in your controllers should
+often be pulled out and placed into your model objects.)  In
+L<Chapter 4|Catalyst::Manual::Tutorial::04_BasicCRUD> of the tutorial we
+will explore some extremely helpful and powerful features of
+L<DBIx::Class> that allow you to pull code out of your views and
+controllers and place it where it rightfully belongs in a model class.
 
 
 =head2 Test Run The Application
@@ -575,13 +605,12 @@ To test your work so far, first start the development server:
 
     $ script/myapp_server.pl -r
 
-Then point your browser to L<http://localhost:3000> and you should
-still get the Catalyst welcome page.  Next, change the URL in your
-browser to L<http://localhost:3000/books/list>.  If you have
-everything working so far, you should see a web page that displays
-nothing other than our column headers for "Title", "Rating", and
-"Author(s)" -- we will not see any books until we get the database and
-model working below.
+Then point your browser to L<http://localhost:3000> and you should still
+get the Catalyst welcome page.  Next, change the URL in your browser to
+L<http://localhost:3000/books/list>.  If you have everything working so
+far, you should see a web page that displays nothing other than our
+column headers for "Title", "Rating", and "Author(s)" -- we will not see
+any books until we get the database and model working below.
 
 If you run into problems getting your application to run correctly, it
 might be helpful to refer to some of the debugging techniques covered in
@@ -592,10 +621,10 @@ tutorial.
 =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.  We will use 
-SQLite (L<http://www.sqlite.org>), a popular database that is
-lightweight and easy to use. Be sure to get at least version 3. Open
-C<myapp01.sql> in your editor and enter:
+create a database table and load some sample data.  We will use SQLite
+(L<http://www.sqlite.org>), a popular database that is lightweight and
+easy to use. Be sure to get at least version 3. Open C<myapp01.sql> in
+your editor and enter:
 
     --
     -- Create a very simple database to hold book and author information
@@ -655,7 +684,7 @@ can use the SQLite command line environment to do a quick dump of the
 database contents:
 
     $ sqlite3 myapp.db
-    SQLite version 3.6.22
+    SQLite version 3.7.3
     Enter ".help" for instructions
     Enter SQL statements terminated with a ";"
     sqlite> select * from book;
@@ -682,98 +711,69 @@ 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.
 
-Please note that here we have chosen to use 'singular' table names. This is
-because the default inflection code for older versions of
-L<DBIx::Class::Schema::Loader> does NOT handle plurals. There has been much
-philosophical discussion on whether table names should be plural or singular.
-There is no one correct answer, as long as one makes a choice and remains
-consistent with it. If you prefer plural table names (e.g. you think that they
-are easier to read) then see the documentation in
+Please note that here we have chosen to use 'singular' table names. This
+is because the default inflection code for older versions of
+L<DBIx::Class::Schema::Loader> does NOT handle plurals. There has been
+much philosophical discussion on whether table names should be plural or
+singular.  There is no one correct answer, as long as one makes a choice
+and remains consistent with it. If you prefer plural table names (e.g.
+you think that they are easier to read) then see the documentation in
 L<DBIx::Class::Schema::Loader::Base/naming> (version 0.05 or greater).
 
-For using other databases, such as PostgreSQL or MySQL, see 
+For using other databases, such as PostgreSQL or MySQL, see
 L<Appendix 2|Catalyst::Manual::Tutorial::10_Appendices>.
 
 
 =head1 DATABASE ACCESS WITH DBIx::Class
 
-Catalyst can be used with virtually any form of datastore available 
-via Perl.  For example, L<Catalyst::Model::DBI|Catalyst::Model::DBI> 
-can be used to access databases through the traditional Perl L<DBI> 
-interface or you can use a model to access files of any type on the 
-filesystem.  However, most Catalyst applications use some form of 
-object-relational mapping (ORM) technology to create objects 
-associated with tables in a relational database.  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 DBIx::Class, as will this tutorial.
-
-Although DBIx::Class has included support for a C<create=dynamic> mode 
-to automatically read the database structure every time the 
-application starts, its use is no longer recommended.  While it can
-make for "flashy" demos, the use of the C<create=static> mode we use 
-below can be implemented just as quickly and provides many advantages 
-(such as the ability to add your own methods to the overall DBIC 
-framework, a technique that we see in Chapter 4).
-
-
-=head2 Make Sure You Have a Recent Version of the DBIx::Class Model
-
-First, let's be sure we have a recent version of the DBIC helper,
-L<Catalyst::Model::DBIC::Schema>, so
-that we can take advantage of some recent enhancements in how
-foreign keys are handled with SQLite.  To check your version, 
-run this command:
-
-    $ perl -MCatalyst::Model::DBIC::Schema -e \
-        'print "$Catalyst::Model::DBIC::Schema::VERSION\n"'
-    0.4
-
-Please note the '\' above.  Depending on your environment, you might 
-be able to cut and paste the text as shown or need to remove the '\' 
-character to that the command is all on a single line.
+Catalyst can be used with virtually any form of datastore available via
+Perl.  For example, L<Catalyst::Model::DBI> can be used to access
+databases through the traditional Perl L<DBI> interface or you can use a
+model to access files of any type on the filesystem.  However, most
+Catalyst applications use some form of object-relational mapping (ORM)
+technology to create objects associated with tables in a relational
+database, and Matt Trout's L<DBIx::Class> (abbreviated as "DBIC") is the
+usual choice (this tutorial will use L<DBIx::Class>).
+
+Although DBIx::Class has included support for a C<create=dynamic> mode
+to automatically read the database structure every time the application
+starts, its use is no longer recommended.  While it can make for
+"flashy" demos, the use of the C<create=static> mode we use below can be
+implemented just as quickly and provides many advantages (such as the
+ability to add your own methods to the overall DBIC framework, a
+technique that we see in
+L<Chapter 4|Catalyst::Manual::Tutorial::04_BasicCRUD>).
 
-If you are following along in Debian 5, you should have version 0.40 or 
-higher (shown above as "0.4" with the tailing zero removed). If you have 
-less than v0.39, you will need to run this command to install it 
-directly from CPAN: 
 
-    $ cpan -i Catalyst::Model::DBIC::Schema
-
-And re-run the version print command to verify that you are now at 
-0.39 or higher.
-
-In addition, since we are using SQLite's foreign key support here,
-please be sure that you use version C<1.27> of L<DBD::SQLite> or later:
-
-    $ perl -MDBD::SQLite -e 'print "$DBD::SQLite::VERSION\n"'
-    1.29
-
-Upgrade if you are not at version C<1.27> or higher.
+=head2 Create Static DBIx::Class Schema Files
 
-Also, remember to put a line requiring the version of the module
-you just installed into your Makefile.PL
+B<Note:> If you are not following along in the Tutorial Virtual Machine,
+please be sure that you have version 1.27 or higher of DBD::SQLite and
+version 0.39 or higher of Catalyst::Model::DBIC::Schema.  (The Tutorial
+VM already has versions that are known to work.)  You can get your
+currently installed version numbers with the following commands.
 
-=head2 Create Static DBIx::Class Schema Files
+    $ perl -MCatalyst::Model::DBIC::Schema\ 999
+    $ perl -MDBD::SQLite\ 999
 
-Before you continue, make sure your C<myapp.db> database file is in 
-the application's topmost directory. Now use the model helper with 
-the C<create=static> option to read the database with 
-L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> and 
+Before you continue, make sure your C<myapp.db> database file is in the
+application's topmost directory. Now use the model helper with the
+C<create=static> option to read the database with
+L<DBIx::Class::Schema::Loader> and
 automatically build the required files for us:
 
     $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \
         create=static dbi:SQLite:myapp.db \
         on_connect_do="PRAGMA foreign_keys = ON"
-     exists "/home/me/MyApp/script/../lib/MyApp/Model"
-     exists "/home/me/MyApp/script/../t"
-    Dumping manual schema for MyApp::Schema to directory /home/me/MyApp/script/../lib ...
+     exists "/root/MyApp/script/../lib/MyApp/Model"
+     exists "/root/MyApp/script/../t"
+    Dumping manual schema for MyApp::Schema to directory /root/MyApp/script/../lib ...
     Schema dump completed.
-    created "/home/me/MyApp/script/../lib/MyApp/Model/DB.pm"
-    created "/home/me/MyApp/script/../t/model_DB.t"
+    created "/root/MyApp/script/../lib/MyApp/Model/DB.pm"
+    created "/root/MyApp/script/../t/model_DB.t"
 
-Please note the '\' above.  Depending on your environment, you might 
-be able to cut and paste the text as shown or need to remove the '\' 
+Please note the '\' above.  Depending on your environment, you might be
+able to cut and paste the text as shown or need to remove the '\'
 character to that the command is all on a single line.
 
 The C<script/myapp_create.pl> command breaks down like this:
@@ -782,12 +782,14 @@ The C<script/myapp_create.pl> command breaks down like this:
 
 =item *
 
-C<DB> is the name of the model class to be created by the helper in 
-C<lib/MyApp/Model>.
+C<DB> is the name of the model class to be created by the helper in
+the C<lib/MyApp/Model> directory.
 
 =item *
 
-C<DBIC::Schema> is the type of the model to create.
+C<DBIC::Schema> is the type of the model to create.  This equates to
+L<Catalyst::Model::DBIC::Schema>, the standard way to use a DBIC-based
+model inside of Catalyst.
 
 =item *
 
@@ -796,67 +798,75 @@ C<lib/MyApp/Schema.pm>.
 
 =item *
 
-C<create=static> causes 
-L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> to 
-load the schema as it runs and then write that information out
-into files.
+C<create=static> causes L<DBIx::Class::Schema::Loader> to load the
+schema as it runs and then write that information out into
+C<lib/MyApp/Schema.pm> and files under the C<lib/MyApp/Schema>
+directory.
 
 =item *
 
-C<dbi:SQLite:myapp.db> is the standard DBI connect string 
-for use with SQLite.
+C<dbi:SQLite:myapp.db> is the standard DBI connect string for use with
+SQLite.
 
 =item *
 
-And finally, the C<on_connect_do> string requests that 
-L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> create 
-foreign key relationships for us (this is not needed for databases such 
-as PostgreSQL and MySQL, but is required for SQLite). If you take a look 
-at C<lib/MyApp/Model/DB.pm>, you will see that the SQLite pragma is 
-propogated to the Model, so that SQLite's recent (and optional) foreign 
-key enforcement is enabled at the start of every database connection. 
-
-
+And finally, the C<on_connect_do> string requests that
+L<DBIx::Class::Schema::Loader> create
+foreign key relationships for us (this is not needed for databases such
+as PostgreSQL and MySQL, but is required for SQLite). If you take a look
+at C<lib/MyApp/Model/DB.pm>, you will see that the SQLite pragma is
+propagated to the Model, so that SQLite's recent (and optional) foreign
+key enforcement is enabled at the start of every database connection.
 
 =back
 
-If you look in the C<lib/MyApp/Schema.pm> file, you will find that it 
-only contains a call to the C<load_namespaces> method.  You will also 
-find that C<lib/MyApp> contains a C<Schema> subdirectory, which then 
-has a subdirectory called "Result".  This "Result" subdirectory then 
-has files named according to each of the tables in our simple database 
-(C<Author.pm>, C<BookAuthor.pm>, and C<Book.pm>).  These three 
-files are called "Result Classes" in DBIx::Class nomenclature. Although the 
-Result Class files are named after tables in our database, the classes 
-correspond to the I<row-level data> that is returned by DBIC (more on 
-this later, especially in 
+
+If you look in the C<lib/MyApp/Schema.pm> file, you will find that it
+only contains a call to the C<load_namespaces> method.  You will also
+find that C<lib/MyApp> contains a C<Schema> subdirectory, which then has
+a subdirectory called "Result".  This "Result" subdirectory then has
+files named according to each of the tables in our simple database
+(C<Author.pm>, C<BookAuthor.pm>, and C<Book.pm>).  These three files are
+called "Result Classes" (or
+"L<ResultSource Classes|DBIx::Class::ResultSource>") in DBIx::Class
+nomenclature. Although the Result Class files are named after tables in
+our database, the classes correspond to the I<row-level data> that is
+returned by DBIC (more on this later, especially in
 L<Catalyst::Manual::Tutorial::04_BasicCRUD/EXPLORING THE POWER OF DBIC>).
 
-The idea with the Result Source files created under 
-C<lib/MyApp/Schema/Result> by the C<create=static> option is to only 
-edit the files below the C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!> 
-warning. If you place all of your changes below that point in the 
-file, you can regenerate the automatically created information at the 
-top of each file should your database structure get updated.
-
-Also note the "flow" of the model information across the various files 
-and directories.  Catalyst will initially load the model from 
-C<lib/MyApp/Model/DB.pm>.  This file contains a reference to 
-C<lib/MyApp/Schema.pm>, so that file is loaded next.  Finally, the 
-call to C<load_namespaces> in C<Schema.pm> will load each of the 
-"Result Class" files from the C<lib/MyApp/Schema/Result> subdirectory. 
-The final outcome is that Catalyst will dynamically create three 
-table-specific Catalyst models every time the application starts (you 
-can see these three model files listed in the debug output generated 
+The idea with the Result Source files created under
+C<lib/MyApp/Schema/Result> by the C<create=static> option is to only
+edit the files below the C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!>
+warning. If you place all of your changes below that point in the file,
+you can regenerate the automatically created information at the top of
+each file should your database structure get updated.
+
+Also note the "flow" of the model information across the various files
+and directories.  Catalyst will initially load the model from
+C<lib/MyApp/Model/DB.pm>.  This file contains a reference to
+C<lib/MyApp/Schema.pm>, so that file is loaded next.  Finally, the call
+to C<load_namespaces> in C<Schema.pm> will load each of the "Result
+Class" files from the C<lib/MyApp/Schema/Result> subdirectory.  The
+final outcome is that Catalyst will dynamically create three
+table-specific Catalyst models every time the application starts (you
+can see these three model files listed in the debug output generated
 when you launch the application).
 
-B<NOTE:> Older versions of 
-L<Catalyst::Model::DBIC::Schema|Catalyst::Model::DBIC::Schema> use the 
-deprecated DBIx::Class C<load_classes> technique instead of the newer 
-C<load_namespaces>.  For new applications, please try to use 
-C<load_namespaces> since it more easily supports a very useful DBIC 
-technique called "ResultSet Classes."  If you need to convert an 
-existing application from "load_classes" to "load_namespaces," you can 
+Additionally, the C<lib/MyApp/Schema.pm> model can easily be loaded
+outside of Catalyst, for example, in command-line utilities and/or cron
+jobs. C<lib/MyApp/Model/DB.pm> provides a very thin "bridge" between
+Catalyst this external database model.  Once you see how we can add some
+powerful features to our DBIC model in
+L<Chapter 4|Catalyst::Manual::Tutorial::04_BasicCRUD>, the elegance
+of this approach will start to become more obvious.
+
+B<NOTE:> Older versions of
+L<Catalyst::Model::DBIC::Schema> use the
+deprecated DBIx::Class C<load_classes> technique instead of the newer
+C<load_namespaces>.  For new applications, please try to use
+C<load_namespaces> since it more easily supports a very useful DBIC
+technique called "ResultSet Classes."  If you need to convert an
+existing application from "load_classes" to "load_namespaces," you can
 use this process to automate the migration, but first make sure you have
 version C<0.39> of L<Catalyst::Model::DBIC::Schema> and
 L<DBIx::Class::Schema::Loader> version C<0.05000> or later.
@@ -870,10 +880,10 @@ L<DBIx::Class::Schema::Loader> version C<0.05000> or later.
 
 =head1 ENABLE THE MODEL IN THE CONTROLLER
 
-Open C<lib/MyApp/Controller/Books.pm> and un-comment the model code we 
+Open C<lib/MyApp/Controller/Books.pm> and un-comment the model code we
 left disabled earlier so that your version matches the following (un-
-comment the line containing C<[$c-E<gt>model('DB::Book')-E<gt>all]> 
-and delete the next 2 lines):
+comment the line containing C<[$c-E<gt>model('DB::Book')-E<gt>all]> and
+delete the next 2 lines):
 
     =head2 list
     
@@ -897,38 +907,36 @@ and delete the next 2 lines):
         $c->stash(template => 'books/list.tt2');
     }
 
-B<TIP>: You may see the C<$c-E<gt>model('DB::Book')> un-commented 
-above written as C<$c-E<gt>model('DB')-E<gt>resultset('Book')>.  The 
-two are equivalent.  Either way, C<$c-E<gt>model> returns a 
-L<DBIx::Class::ResultSet|DBIx::Class::ResultSet> which handles queries 
-against the database and iterating over the set of results that is 
+B<TIP>: You may see the C<$c-E<gt>model('DB::Book')> un-commented above
+written as C<$c-E<gt>model('DB')-E<gt>resultset('Book')>.  The two are
+equivalent.  Either way, C<$c-E<gt>model> returns a
+L<DBIx::Class::ResultSet> which handles queries
+against the database and iterating over the set of results that is
 returned.
 
-We are using the C<-E<gt>all> to fetch all of the books.  DBIC 
-supports a wide variety of more advanced operations to easily do 
-things like filtering and sorting the results.  For example, the 
-following could be used to sort the results by descending title:
+We are using the C<-E<gt>all> to fetch all of the books.  DBIC supports
+a wide variety of more advanced operations to easily do things like
+filtering and sorting the results.  For example, the following could be
+used to sort the results by descending title:
 
     $c->model('DB::Book')->search({}, {order_by => 'title DESC'});
 
-Some other examples are provided in 
-L<DBIx::Class::Manual::Cookbook/Complex WHERE clauses>, with 
-additional information found at L<DBIx::Class::ResultSet/search>, 
-L<DBIx::Class::Manual::FAQ/Searching>, 
-L<DBIx::Class::Manual::Intro|DBIx::Class::Manual::Intro> 
-and L<Catalyst::Model::DBIC::Schema|Catalyst::Model::DBIC::Schema>.
+Some other examples are provided in
+L<DBIx::Class::Manual::Cookbook/Complex WHERE clauses>, with additional
+information found at L<DBIx::Class::ResultSet/search>,
+L<DBIx::Class::Manual::FAQ/Searching>, L<DBIx::Class::Manual::Intro> and
+L<Catalyst::Model::DBIC::Schema>.
 
 
 =head2 Test Run The Application
 
-First, let's enable an environment variable that causes DBIx::Class to 
-dump the SQL statements used to access the database.  This is a 
-helpful trick when you are trying to debug your database-oriented 
-code.  Press C<Ctrl-C> to break out of the development server and
-enter:
+First, let's enable an environment variable that causes L<DBIx::Class>
+to dump the SQL statements used to access the database.  This is a
+helpful trick when you are trying to debug your database-oriented code.
+Press C<Ctrl-C> to break out of the development server and enter:
 
     $ export DBIC_TRACE=1
-    $ script/myapp_server.pl -r 
+    $ script/myapp_server.pl -r
 
 This assumes you are using bash as your shell -- adjust accordingly if
 you are using a different shell (for example, under tcsh, use
@@ -937,8 +945,8 @@ C<setenv DBIC_TRACE 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> for details (including options
-to log to a file instead of displaying to the Catalyst development server
-log).
+to log to a file instead of displaying to the Catalyst development
+server log).
 
 Then launch the Catalyst development server.  The log output should
 display something like:
@@ -948,14 +956,14 @@ display something like:
     [debug] Statistics enabled
     [debug] Loaded plugins:
     .----------------------------------------------------------------------------.
-    | Catalyst::Plugin::ConfigLoader  0.27                                       |
+    | Catalyst::Plugin::ConfigLoader  0.30                                       |
     | Catalyst::Plugin::StackTrace  0.11                                         |
     '----------------------------------------------------------------------------'
     
     [debug] Loaded dispatcher "Catalyst::Dispatcher"
-    [debug] Loaded engine "Catalyst::Engine::HTTP"
-    [debug] Found home "/home/me/MyApp"
-    [debug] Loaded Config "/home/me/MyApp/myapp.conf"
+    [debug] Loaded engine "Catalyst::Engine"
+    [debug] Found home "/root/MyApp"
+    [debug] Loaded Config "/root/MyApp/myapp.conf"
     [debug] Loaded components:
     .-----------------------------------------------------------------+----------.
     | Class                                                           | Type     |
@@ -991,14 +999,13 @@ display something like:
     '-------------------------------------+--------------------------------------'
     
     [info] MyApp powered by Catalyst 5.80020
-    You can connect to your server at http://debian:3000
+    HTTP::Server::PSGI: Accepting connections at http://0:3000
 
-B<NOTE:> Be sure you run the C<script/myapp_server.pl> command from
-the 'base' directory of your application, not inside the C<script>
-directory itself or it will not be able to locate the C<myapp.db>
-database file.  You can use a fully qualified or a relative path to
-locate the database file, but we did not specify that when we ran the
-model helper earlier.
+B<NOTE:> Be sure you run the C<script/myapp_server.pl> command from the
+'base' directory of your application, not inside the C<script> directory
+itself or it will not be able to locate the C<myapp.db> database file.
+You can use a fully qualified or a relative path to locate the database
+file, but we did not specify that when we ran the model helper earlier.
 
 Some things you should note in the output above:
 
@@ -1006,10 +1013,10 @@ Some things you should note in the output above:
 
 =item *
 
-Catalyst::Model::DBIC::Schema dynamically created three model classes,
-one to represent each of the three tables in our database
-(C<MyApp::Model::DB::Author>, C<MyApp::Model::DB::BookAuthor>,
-and C<MyApp::Model::DB::Book>).
+L<Catalyst::Model::DBIC::Schema> dynamically created three model
+classes, one to represent each of the three tables in our database
+(C<MyApp::Model::DB::Author>, C<MyApp::Model::DB::BookAuthor>, and
+C<MyApp::Model::DB::Book>).
 
 =item *
 
@@ -1027,34 +1034,34 @@ books loaded by the C<myapp01.sql> script above without any formatting.
 The rating for each book should appear on each row, but the "Author(s)"
 column will still be blank (we will fill that in later).
 
-Also notice in the output of the C<script/myapp_server.pl> that 
-DBIx::Class used the following SQL to retrieve the data:
+Also notice in the output of the C<script/myapp_server.pl> that
+L<DBIx::Class> used the following SQL to retrieve the data:
 
     SELECT me.id, me.title, me.rating FROM book me
 
 because we enabled DBIC_TRACE.
 
 You now have the beginnings of a simple but workable web application.
-Continue on to future sections and we will develop the application
-more fully.
+Continue on to future sections and we will develop the application more
+fully.
 
 
 =head1 CREATE A WRAPPER FOR THE VIEW
 
-When using TT, you can (and should) create a wrapper that will
-literally wrap content around each of your templates.  This is
-certainly useful as you have one main source for changing things that
-will appear across your entire site/application instead of having to
-edit many individual files.
+When using TT, you can (and should) create a wrapper that will literally
+wrap content around each of your templates.  This is certainly useful as
+you have one main source for changing things that will appear across
+your entire site/application instead of having to edit many individual
+files.
 
 
 =head2 Configure HTML.pm For The Wrapper
 
-In order to create a wrapper, you must first edit your TT view and
-tell it where to find your wrapper file. 
+In order to create a wrapper, you must first edit your TT view and tell
+it where to find your wrapper file.
 
-Edit your TT view in C<lib/MyApp/View/HTML.pm> and change it to match the 
-following:
+Edit your TT view in C<lib/MyApp/View/HTML.pm> and change it to match
+the following:
 
     __PACKAGE__->config(
         # Change default TT extension
@@ -1073,11 +1080,12 @@ following:
 =head2 Create the Wrapper Template File and Stylesheet
 
 Next you need to set up your wrapper template.  Basically, you'll want
-to take the overall layout of your site and put it into this file.
-For the tutorial, open C<root/src/wrapper.tt2> and input the following:
+to take the overall layout of your site and put it into this file.  For
+the tutorial, open C<root/src/wrapper.tt2> and input the following:
 
     <?xml version="1.0" encoding="UTF-8"?>
-    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" [%#
+        %]"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
     <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
     <head>
     <title>[% template.title or "My Catalyst App!" %]</title>
@@ -1098,7 +1106,8 @@ For the tutorial, open C<root/src/wrapper.tt2> and input the following:
         Navigation:
         <ul>
             <li><a href="[% c.uri_for('/books/list') %]">Home</a></li>
-            <li><a href="[% c.uri_for('/') %]" title="Catalyst Welcome Page">Welcome</a></li>
+            <li><a href="[% c.uri_for('/')
+                %]" title="Catalyst Welcome Page">Welcome</a></li>
         </ul>
     </div><!-- end menu -->
     
@@ -1123,26 +1132,26 @@ Notice the status and error message sections in the code above:
     <span class="error">[% error_msg %]</span>
 
 If we set either message in the Catalyst stash (e.g.,
-C<$c-E<gt>stash-E<gt>{status_msg} = 'Request was successful!'>) it
-will be displayed whenever any view used by that request is rendered.
-The C<message> and C<error> CSS styles can be customized to suit your
-needs in the C<root/static/css/main.css> file we create below.
+C<$c-E<gt>stash-E<gt>{status_msg} = 'Request was successful!'>) it will
+be displayed whenever any view used by that request is rendered.  The
+C<message> and C<error> CSS styles can be customized to suit your needs
+in the C<root/static/css/main.css> file we create below.
 
-B<Notes:> 
+B<Notes:>
 
 =over 4
 
 =item *
 
-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 chapter of the tutorial).
+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> (we will use Catalyst sessions in the
+Authentication chapter of the tutorial).
 
 =item *
 
-Although it is beyond the scope of this tutorial, you may wish to use
-a JavaScript or AJAX tool such as jQuery (L<http://www.jquery.com>) or
+Although it is beyond the scope of this tutorial, you may wish to use a
+JavaScript or AJAX tool such as jQuery (L<http://www.jquery.com>) or
 Dojo (L<http://www.dojotoolkit.org>).
 
 =back
@@ -1197,84 +1206,84 @@ content:
     }
 
 You may wish to check out a "CSS Framework" like Emastic
-(L<http://code.google.com/p/emastic/>) as a way to quickly
-provide lots of high-quality CSS functionality.
+(L<http://code.google.com/p/emastic/>) as a way to quickly provide lots
+of high-quality CSS functionality.
 
 
 =head2 Test Run The Application
 
-Hit "Reload" in your web browser and you should now see a formatted 
-version of our basic book list. (Again, the development server should 
-have automatically restarted when you made changes to 
-C<lib/MyApp/View/HTML.pm>. If you are not using the "-r" option, you will 
-need to hit C<Ctrl-C> and manually restart it. Also note that the 
-development server does I<NOT> need to restart for changes to the TT and 
-static files we created and edited in the C<root> directory -- those 
-updates are handled on a per-request basis.) 
+Hit "Reload" in your web browser and you should now see a formatted
+version of our basic book list. (Again, the development server should
+have automatically restarted when you made changes to
+C<lib/MyApp/View/HTML.pm>. If you are not using the "-r" option, you
+will need to hit C<Ctrl-C> and manually restart it. Also note that the
+development server does I<NOT> need to restart for changes to the TT and
+static files we created and edited in the C<root> directory -- those
+updates are handled on a per-request basis.)
 
-Although our wrapper and stylesheet are obviously very simple, you 
-should see how it allows us to control the overall look of an entire 
-website from two central files. To add new pages to the site, just 
-provide a template that fills in the C<content> section of our wrapper 
-template -- the wrapper will provide the overall feel of the page. 
+Although our wrapper and stylesheet are obviously very simple, you
+should see how it allows us to control the overall look of an entire
+website from two central files. To add new pages to the site, just
+provide a template that fills in the C<content> section of our wrapper
+template -- the wrapper will provide the overall feel of the page.
 
 
 =head2 Updating the Generated DBIx::Class Result Class Files
 
-If you take a look at the Schema files automatically generated by 
-L<DBIx::Class::Schema::Loader>, you will see that it has already defined 
-C<has_many> and C<belongs_to> relationships on each side of our foreign 
-keys. For example, take a look at C<lib/MyApp/Schema/Result/Book.pm> and 
-notice the following code: 
+If you take a look at the Schema files automatically generated by
+L<DBIx::Class::Schema::Loader>, you will see that it has already defined
+C<has_many> and C<belongs_to> relationships on each side of our foreign
+keys. For example, take a look at C<lib/MyApp/Schema/Result/Book.pm> and
+notice the following code:
 
     =head1 RELATIONS
-
+    
     =head2 book_authors
-
+    
     Type: has_many
-
+    
     Related object: L<MyApp::Schema::Result::BookAuthor>
-
+    
     =cut
-
+    
     __PACKAGE__->has_many(
       "book_authors",
       "MyApp::Schema::Result::BookAuthor",
       { "foreign.book_id" => "self.id" },
+      { cascade_copy => 0, cascade_delete => 0 },
     );
 
-Each C<Book> "has_many" C<book_authors>, where C<BookAuthor> is
-the many-to-many table that allows each Book to have multiple
-Authors, and each Author to have mulitple books.  The arguments
-to C<has_many> are:
+Each C<Book> "has_many" C<book_authors>, where C<BookAuthor> is the
+many-to-many table that allows each Book to have multiple Authors, and
+each Author to have multiple books.  The arguments to C<has_many> are:
 
 =over 4
 
 =item *
 
-C<book_authors> - The name for this relationship.  DBIC will create
-an accessor on the C<Books> DBIC Row object with this name.
+C<book_authors> - The name for this relationship.  DBIC will create an
+accessor on the C<Books> DBIC Row object with this name.
 
 =item *
 
-C<MyApp::Schema::Result::BookAuthor> - The name of the DBIC model
-class referenced by this C<has_many> relationship.
+C<MyApp::Schema::Result::BookAuthor> - The name of the DBIC model class
+referenced by this C<has_many> relationship.
 
 =item *
 
-C<foreign.book_id> - C<book_id> is the name of the foreign key 
-column in the I<foreign> table that points back to this table.
+C<foreign.book_id> - C<book_id> is the name of the foreign key column in
+the I<foreign> table that points back to this table.
 
 =item *
 
-C<self.id> - C<id> is the name of the column in I<this> table
-that is referenced by the foreign key.
+C<self.id> - C<id> is the name of the column in I<this> table that is
+referenced by the foreign key.
 
 =back
 
-See L<DBIx::Class::Relationship/has_many> for
-additional information.  Note that you might see a "hand coded"
-version of the C<has_many> relationship above expressed as:
+See L<DBIx::Class::Relationship/has_many> for additional information.
+Note that you might see a "hand coded" version of the C<has_many>
+relationship above expressed as:
 
     __PACKAGE__->has_many(
       "book_authors",
@@ -1282,48 +1291,47 @@ version of the C<has_many> relationship above expressed as:
       "book_id",
     );
 
-Where the third argument is simply the name of the column in
-the foreign table.  However, the hashref syntax used by 
-L<DBIx::Class::Schema::Loader> is more flexible (for example,
-it can handle "multi-column" foreign keys).
+Where the third argument is simply the name of the column in the foreign
+table.  However, the hashref syntax used by
+L<DBIx::Class::Schema::Loader> is more flexible (for example, it can
+handle "multi-column foreign keys").
 
-B<Note:> If you are using older versions of SQLite and related DBIC 
-tools, you will need to manually define your C<has_many> and 
-C<belongs_to> relationships. We recommend upgrading to the versions 
-specified above. :-) 
+B<Note:> If you are using older versions of SQLite and related DBIC
+tools, you will need to manually define your C<has_many> and
+C<belongs_to> relationships. We recommend upgrading to the versions
+specified above. :-)
 
-Have a look at C<lib/MyApp/Schema/Result/BookAuthor.pm> and notice
-that there is a C<belongs_to> relationship defined that acts as the
-"mirror image" to the C<has_many> relationship we just looked at
-above:
+Have a look at C<lib/MyApp/Schema/Result/BookAuthor.pm> and notice that
+there is a C<belongs_to> relationship defined that acts as the "mirror
+image" to the C<has_many> relationship we just looked at above:
 
     =head1 RELATIONS
-
+    
     =head2 book
-
+    
     Type: belongs_to
-
+    
     Related object: L<MyApp::Schema::Result::Book>
-
+    
     =cut
-
+    
     __PACKAGE__->belongs_to(
       "book",
       "MyApp::Schema::Result::Book",
       { id => "book_id" },
-      { join_type => "LEFT" },
+      { join_type => "LEFT", on_delete => "CASCADE", on_update => "CASCADE" },
     );
 
-The arguments are similar, but see 
+The arguments are similar, but see
 L<DBIx::Class::Relationship/belongs_to> for the details.
 
-Although recent versions of SQLite and L<DBIx::Class::Schema::Loader> 
-automatically handle the C<has_many> and C<belongs_to> relationships, 
+Although recent versions of SQLite and L<DBIx::Class::Schema::Loader>
+automatically handle the C<has_many> and C<belongs_to> relationships,
 C<many_to_many> relationship bridges (not technically a relationship)
-currently need to be manually inserted. 
-To add a C<many_to_many> relationship bridge, first edit 
-C<lib/MyApp/Schema/Result/Book.pm> and add the following text below 
-the C<# You can replace this text...> comment:
+currently need to be manually inserted.  To add a C<many_to_many>
+relationship bridge, first edit C<lib/MyApp/Schema/Result/Book.pm> and
+add the following text below the C<# You can replace this text...>
+comment:
 
     # many_to_many():
     #   args:
@@ -1333,27 +1341,27 @@ the C<# You can replace this text...> comment:
     #   You must already have the has_many() defined to use a many_to_many().
     __PACKAGE__->many_to_many(authors => 'book_authors', 'author');
 
-B<Note:> Be careful to put this code I<above> the C<1;> at the end of the
-file.  As with any Perl package, we need to end the last line with
-a statement that evaluates to C<true>.  This is customarily done with
+B<Note:> Be careful to put this code I<above> the C<1;> at the end of
+the file.  As with any Perl package, we need to end the last line with a
+statement that evaluates to C<true>.  This is customarily done with
 C<1;> on a line by itself.
 
 The C<many_to_many> relationship bridge is optional, but it makes it
-easier to map a book to its collection of authors.  Without 
-it, we would have to "walk" through the C<book_author> table as in 
-C<$book-E<gt>book_author-E<gt>first-E<gt>author-E<gt>last_name> (we 
-will see examples on how to use DBIx::Class objects in your code soon, 
-but note that because C<$book-E<gt>book_author> 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>author-E<gt>first-E<gt>last_name>. Note that you cannot 
-define a C<many_to_many> relationship bridge without also having the 
+easier to map a book to its collection of authors.  Without it, we would
+have to "walk" through the C<book_author> table as in
+C<$book-E<gt>book_author-E<gt>first-E<gt>author-E<gt>last_name> (we will
+see examples on how to use DBIx::Class objects in your code soon, but
+note that because C<$book-E<gt>book_author> 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>author-E<gt>first-E<gt>last_name>. Note that you cannot
+define a C<many_to_many> relationship bridge without also having the
 C<has_many> relationship in place.
 
-Then edit C<lib/MyApp/Schema/Result/Author.pm> and add the reverse 
-C<many_to_many> relationship bridge for C<Author> as follows (again, be careful 
-to put in above the C<1;> but below the C<# DO NOT MODIFY THIS OR 
-ANYTHING ABOVE!> comment): 
+Then edit C<lib/MyApp/Schema/Result/Author.pm> and add the reverse
+C<many_to_many> relationship bridge for C<Author> as follows (again, be
+careful to put in above the C<1;> but below the C<# DO NOT MODIFY THIS
+OR ANYTHING ABOVE!> comment):
 
     # many_to_many():
     #   args:
@@ -1373,23 +1381,23 @@ alternate way to specify the trace option just in case):
     $ DBIC_TRACE=1 script/myapp_server.pl -r
 
 Make sure that the application loads correctly and that you see the
-three dynamically created model class (one for each of the
-Result Classes we created).
+three dynamically created model class (one for each of the Result
+Classes we created).
 
-Then hit the URL L<http://localhost:3000/books/list> with your browser 
+Then hit the URL L<http://localhost:3000/books/list> with your browser
 and be sure that the book list still displays correctly.
 
-B<Note:> You will not see the authors yet because the view does not yet 
-use the new relations. Read on to the next section where we update the 
-template to do that.
+B<Note:> You will not see the authors yet because the view isn't taking
+advantage of these relationships. Read on to the next section where we
+update the template to do that.
 
 
 =head1 UPDATING THE VIEW
 
-Let's add a new column to our book list page that takes advantage of 
-the relationship information we manually added to our schema files in 
-the previous section.  Edit C<root/src/books/list.tt2> and replace
-the "empty" table cell "<td></td>" with the following:
+Let's add a new column to our book list page that takes advantage of the
+relationship information we manually added to our schema files in the
+previous section.  Edit C<root/src/books/list.tt2> and replace the
+"empty" table cell "<td></td>" with the following:
 
     ...
     <td>
@@ -1411,22 +1419,22 @@ the "empty" table cell "<td></td>" with the following:
     </td>
     ...
 
-B<IMPORTANT NOTE:> Again, you should keep as much "logic code" as 
-possible out of your views.  This kind of logic belongs in your model 
-(the same goes for controllers -- keep them as "thin" as possible and 
-push all of the "complicated code" out to your model objects).  Avoid 
-code like you see in the previous example -- we are only using it here 
-to show some extra features in TT until we get to the more advanced 
-model features we will see in Chapter 4 (see 
+B<IMPORTANT NOTE:> Again, you should keep as much "logic code" as
+possible out of your views.  This kind of logic belongs in your model
+(the same goes for controllers -- keep them as "thin" as possible and
+push all of the "complicated code" out to your model objects).  Avoid
+code like you see in the previous example -- we are only using it here
+to show some extra features in TT until we get to the more advanced
+model features we will see in Chapter 4 (see
 L<Catalyst::Manual::Tutorial::04_BasicCRUD/EXPLORING THE POWER OF DBIC>).
 
-Then hit "Reload" in your browser (note that you don't need to reload 
-the development server or use the C<-r> option when updating TT 
-templates) and you should now see the number of authors each book has 
-along with a comma-separated list of the authors' last names.  (If you 
-didn't leave the development server running from the previous step, 
-you will obviously need to start it before you can refresh your 
-browser window.)
+Then hit "Reload" in your browser (note that you don't need to reload
+the development server or use the C<-r> option when updating TT
+templates) and you should now see the number of authors each book has
+along with a comma-separated list of the authors' last names.  (If you
+didn't leave the development server running from the previous step, you
+will obviously need to start it before you can refresh your browser
+window.)
 
 If you are still running the development server with C<DBIC_TRACE>
 enabled, you should also now see five more C<SELECT> statements in the
@@ -1445,33 +1453,47 @@ DBIx::Class):
     SELECT author.id, author.first_name, author.last_name FROM book_author me  
     JOIN author author ON author.id = me.author_id WHERE ( me.book_id = ? ): '5'
 
-Also note in C<root/src/books/list.tt2> that we are using "| html", a 
-type of TT filter, to escape characters such as E<lt> and E<gt> to &lt; 
-and &gt; and avoid various types of dangerous hacks against your 
-application.  In a real application, you would probably want to put 
-"| html" at the end of every field where a user has control over the 
-information that can appear in that field (and can therefore inject 
-markup or code if you don't "neutralize" those fields).  In addition to 
-"| html", Template Toolkit has a variety of other useful filters that 
-can found in the documentation for 
-L<Template::Filters|Template::Filters>.
+Also note in C<root/src/books/list.tt2> that we are using "| html", a
+type of TT filter, to escape characters such as E<lt> and E<gt> to &lt;
+and &gt; and avoid various types of dangerous hacks against your
+application.  In a real application, you would probably want to put "|
+html" at the end of every field where a user has control over the
+information that can appear in that field (and can therefore inject
+markup or code if you don't "neutralize" those fields).  In addition to
+"| html", Template Toolkit has a variety of other useful filters that
+can found in the documentation for L<Template::Filters>.  (While we are
+on the topic of security and escaping of dangerous values, one of the
+advantages of using tools like DBIC for database access or
+L<HTML::FormFu> for form management [see
+L<Chapter 9|Catalyst::Manual::Tutorial::09_AdvancedCRUD::09_FormFu>
+is that they automatically handle most escaping for you and therefore
+dramatically increase the security of your app.)
 
 
 =head1 RUNNING THE APPLICATION FROM THE COMMAND LINE
 
-In some situations, it can be useful to run your application and
-display a page without using a browser.  Catalyst lets you do this
-using the C<scripts/myapp_test.pl> script.  Just supply the URL you
-wish to display and it will run that request through the normal
-controller dispatch logic and use the appropriate view to render the
-output (obviously, complex pages may dump a lot of text to your
-terminal window).  For example, if you type:
+In some situations, it can be useful to run your application and display
+a page without using a browser.  Catalyst lets you do this using the
+C<scripts/myapp_test.pl> script.  Just supply the URL you wish to
+display and it will run that request through the normal controller
+dispatch logic and use the appropriate view to render the output
+(obviously, complex pages may dump a lot of text to your terminal
+window).  For example, if C<Ctrl+C> out of the development server
+and then type:
 
     $ script/myapp_test.pl "/books/list"
 
 You should get the same text as if you visited
 L<http://localhost:3000/books/list> with the normal development server
-and asked your browser to view the page source.
+and asked your browser to view the page source.  You can even pipe this
+HTML text output to a text-based browser using a command like:
+
+    $ script/myapp_test.pl "/books/list" | lynx -stdin
+
+And you should see a fully rendered text-based view of your page.  (If
+you are following along in Debian 6, type
+C<sudo aptitude -y install lynx> to install lynx.)  If you do start
+lynx, you can use the "Q" key to quit.
 
 
 =head1 OPTIONAL INFORMATION
@@ -1486,10 +1508,10 @@ if you wish.>
 Once your controller logic has processed the request from a user, it
 forwards processing to your view in order to generate the appropriate
 response output.  Catalyst uses
-L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> by
-default to automatically perform this operation.  If you look in
-C<lib/MyApp/Controller/Root.pm>, you should see the empty
-definition for the C<sub end> method:
+L<Catalyst::Action::RenderView> by default
+to automatically perform this operation.  If you look in
+C<lib/MyApp/Controller/Root.pm>, you should see the empty definition for
+the C<sub end> method:
 
     sub end : ActionClass('RenderView') {}
 
@@ -1513,10 +1535,9 @@ the controller does not define a controller-specific C<end> method, the
 =item *
 
 Because the definition includes an C<ActionClass> attribute, the
-L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> logic
-will be executed B<after> any code inside the definition of C<sub end>
-is run.  See L<Catalyst::Manual::Actions|Catalyst::Manual::Actions>
-for more information on C<ActionClass>.
+L<Catalyst::Action::RenderView> logic will be executed B<after> any code
+inside the definition of C<sub end> is run.  See
+L<Catalyst::Manual::Actions> for more information on C<ActionClass>.
 
 =item *
 
@@ -1525,7 +1546,7 @@ logic in C<RenderView>.  However, you can easily extend the
 C<RenderView> logic by adding your own code inside the empty method body
 (C<{}>) created by the Catalyst Helpers when we first ran the
 C<catalyst.pl> to initialize our application.  See
-L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> for more
+L<Catalyst::Action::RenderView> for more
 detailed information on how to extend C<RenderView> in C<sub end>.
 
 =back
@@ -1533,11 +1554,11 @@ detailed information on how to extend C<RenderView> in C<sub end>.
 
 =head2 RenderView's "dump_info" Feature
 
-One of the nice features of C<RenderView> is that it automatically 
-allows you to add C<dump_info=1> to the end of any URL for your 
-application and it will force the display of the "exception dump" 
-screen to the client browser.  You can try this out by pointing 
-your browser to this URL: 
+One of the nice features of C<RenderView> is that it automatically
+allows you to add C<dump_info=1> to the end of any URL for your
+application and it will force the display of the "exception dump" screen
+to the client browser.  You can try this out by pointing your browser to
+this URL:
 
     http://localhost:3000/books/list?dump_info=1
 
@@ -1546,18 +1567,18 @@ You should get a page with the following message at the top:
     Caught exception in MyApp::Controller::Root->end "Forced debug - 
     Scrubbed output at /usr/share/perl5/Catalyst/Action/RenderView.pm line 46."
 
-Along with a summary of your application's state at the end of the 
-processing for that request.  The "Stash" section should show a 
-summarized version of the DBIC book model objects.  If desired, you 
-can adjust the summarization logic (called "scrubbing" logic) -- see 
-L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> for 
+Along with a summary of your application's state at the end of the
+processing for that request.  The "Stash" section should show a
+summarized version of the DBIC book model objects.  If desired, you can
+adjust the summarization logic (called "scrubbing" logic) -- see
+L<Catalyst::Action::RenderView> for
 details.
 
-Note that you shouldn't need to worry about "normal clients" using 
-this technique to "reverse engineer" your application -- C<RenderView> 
-only supports the C<dump_info=1> feature when your application is 
-running in C<-Debug> mode (something you won't do once you have your 
-application deployed in production).
+Note that you shouldn't need to worry about "normal clients" using this
+technique to "reverse engineer" your application -- C<RenderView> only
+supports the C<dump_info=1> feature when your application is running in
+C<-Debug> mode (something you won't do once you have your application
+deployed in production).
 
 
 =head2 Using The Default Template Name
@@ -1566,11 +1587,11 @@ By default, C<Catalyst::View::TT> will look for a template that uses the
 same name as your controller action, allowing you to save the step of
 manually specifying the template name in each action.  For example, this
 would allow us to remove the
-C<$c-E<gt>stash-E<gt>{template} = 'books/list.tt2';> line of our
-C<list> action in the Books controller.  Open
-C<lib/MyApp/Controller/Books.pm> in your editor and comment out this line
-to match the following (only the C<$c-E<gt>stash-E<gt>{template}> line
-has changed):
+C<$c-E<gt>stash-E<gt>{template} = 'books/list.tt2';>
+line of our C<list> action in the Books controller.
+Open C<lib/MyApp/Controller/Books.pm> in your editor and comment out
+this line to match the following (only the
+C<$c-E<gt>stash-E<gt>{template}> line has changed):
 
     =head2 list
     
@@ -1598,9 +1619,9 @@ has changed):
 You should now be able to access the L<http://localhost:3000/books/list>
 URL as before.
 
-B<NOTE:> Please note that if you use the default template technique,
-you will B<not> be able to use either the C<$c-E<gt>forward> or
-the C<$c-E<gt>detach> mechanisms (these are discussed in Chapter 2 and
+B<NOTE:> Please note that if you use the default template technique, you
+will B<not> be able to use either the C<$c-E<gt>forward> or the
+C<$c-E<gt>detach> mechanisms (these are discussed in Chapter 2 and
 Chapter 9 of the Tutorial).
 
 B<IMPORTANT:> Make sure that you do NOT skip the following section
@@ -1610,26 +1631,29 @@ before continuing to the next chapter 4 Basic CRUD.
 =head2 Return To A Manually Specified Template
 
 In order to be able to use C<$c-E<gt>forward> and C<$c-E<gt>detach>
-later in the tutorial, you should remove the comment from the
-statement in C<sub list> in C<lib/MyApp/Controller/Books.pm>:
+later in the tutorial, you should remove the comment from the statement
+in C<sub list> in C<lib/MyApp/Controller/Books.pm>:
 
     $c->stash(template => 'books/list.tt2');
 
-Then delete the C<TEMPLATE_EXTENSION> line in
-C<lib/MyApp/View/HTML.pm>.
+Then delete the C<TEMPLATE_EXTENSION> line in C<lib/MyApp/View/HTML.pm>.
 
-Check the L<http://localhost:3000/books/list> URL in your browser.
-It should look the same manner as with earlier sections.
+Check the L<http://localhost:3000/books/list> URL in your browser.  It
+should look the same manner as with earlier sections.
+
+
+You can jump to the next chapter of the tutorial here:
+L<Basic CRUD|Catalyst::Manual::Tutorial::04_BasicCRUD>
 
 
 =head1 AUTHOR
 
 Kennedy Clark, C<hkclark@gmail.com>
 
-Please report any errors, issues or suggestions to the author.  The
-most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
+Feel free to contact the author for any errors or suggestions, but the
+best way to report issues is via the CPAN RT Bug system at
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
 
-Copyright 2006-2010, Kennedy Clark, under the
+Copyright 2006-2011, Kennedy Clark, under the
 Creative Commons Attribution Share-Alike License Version 3.0
 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).
index eabac9c..f609941 100644 (file)
@@ -56,34 +56,36 @@ L<Appendices|Catalyst::Manual::Tutorial::10_Appendices>
 
 =head1 DESCRIPTION
 
-This chapter of the tutorial builds on the fairly primitive 
-application created in Chapter 3 to add basic support for Create, 
-Read, Update, and Delete (CRUD) of C<Book> objects.  Note that the 
-'list' function in Chapter 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 Chapter 9.
-
-Although this chapter of the tutorial will show you how to build CRUD 
-functionality yourself, another option is to use a "CRUD builder" type 
-of tool to automate the process.  You get less control, but it can be 
-quick and easy.  For example, see 
-L<Catalyst::Plugin::AutoCRUD|Catalyst::Plugin::AutoCRUD>, 
-L<CatalystX::CRUD|CatalystX::CRUD>, and 
-L<CatalystX::CRUD::YUI|CatalystX::CRUD::YUI>.
-
-You can check out the source code for this example from the Catalyst
-Subversion repository as per the instructions in
-L<Catalyst::Manual::Tutorial::01_Intro|Catalyst::Manual::Tutorial::01_Intro>.
+This chapter of the tutorial builds on the fairly primitive application
+created in
+L<Chapter 3|Catalyst::Manual::Tutorial::03_MoreCatalystBasics> to add
+basic support for Create, Read, Update, and Delete (CRUD) of C<Book>
+objects.  Note that the 'list' function in
+L<Chapter 3|Catalyst::Manual::Tutorial::03_MoreCatalystBasics> 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
+L<Chapter 9|Catalyst::Manual::Tutorial::09_AdvancedCRUD>.
+
+Although this chapter of the tutorial will show you how to build CRUD
+functionality yourself, another option is to use a "CRUD builder" type
+of tool to automate the process.  You get less control, but it can be
+quick and easy.  For example, see L<Catalyst::Plugin::AutoCRUD>,
+L<CatalystX::CRUD>, and L<CatalystX::CRUD::YUI>.
+
+Source code for the tutorial in included in the F</root/Final> directory
+of the Tutorial Virtual machine (one subdirectory per chapter).  There
+are also instructions for downloading the code in
+L<Catalyst::Manual::Tutorial::01_Intro>.
 
 
 =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).
+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
@@ -119,15 +121,27 @@ Edit C<lib/MyApp/Controller/Books.pm> and enter the following method:
         # Assign the Book object to the stash for display and set template
         $c->stash(book     => $book,
                   template => 'books/create_done.tt2');
+    
+        # Disable caching for this page
+        $c->response->header('Cache-Control' => 'no-cache');
     }
 
 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.
+URL and passes it as arguments in C<@_> (as long as the number of
+arguments is not "fixed" using an attribute like C<:Args(0)>).  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.
+
+Also note that we are explicitly setting a C<no-cache> "Cache-Control"
+header to force browsers using the page to get a fresh copy every time.
+You could even move this to a C<auto> method in
+C<lib/MyApp/Controller/Root.pm> and it would automatically get applied
+to every page in the whole application via a single line of code
+(remember from Chapter 3, that every C<auto> method gets run in the
+Controller hierarchy).
 
 
 =head2 Include a Template for the 'url_create' Action:
@@ -140,23 +154,24 @@ Edit C<root/src/books/create_done.tt2> and then enter:
     [% USE Dumper(Indent=1) -%]
     
     [% # Set the page title.  META can 'go back' and set values in templates -%]
-    [% # that have been processed 'before' this template (here it's for      -%]
-    [% # root/lib/site/html and root/lib/site/header).  Note that META only  -%]
-    [% # works on simple/static strings (i.e. there is no variable           -%]
-    [% # interpolation).                                                     -%]
+    [% # that have been processed 'before' this template (here it's updating -%]
+    [% # the title in the root/src/wrapper.tt2 wrapper template).  Note that -%]
+    [% # META only works on simple/static strings (i.e. there is no variable -%]
+    [% # interpolation -- if you need dynamic/interpolated content in your   -%]
+    [% # title, set "$c->stash(title => $something)" in the controller).     -%]
     [% META title = 'Book Created' %]
     
     [% # Output information about the record that was added.  First title.   -%]
     <p>Added book '[% book.title %]'
     
-    [% # Output the last name of the first author.                           -%]
+    [% # Then, output the last name of the first author -%]
     by '[% book.authors.first.last_name %]'
     
-    [% # Output the rating for the book that was added -%]
+    [% # Then, output the rating for the book that was added -%]
     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' -%]
+    [% # Provide a link back to the list page.  'c.uri_for' builds -%]
+    [% # a full URI; e.g., 'http://localhost:3000/books/list'      -%]
     <p><a href="[% c.uri_for('/books/list') %]">Return to list</a></p>
     
     [% # Try out the TT Dumper (for development only!) -%]
@@ -165,27 +180,11 @@ Edit C<root/src/books/create_done.tt2> and then enter:
     [% Dumper.dump(book) %]
     </pre>
 
-The TT C<USE> directive allows access to a variety of plugin modules
-(TT plugins, that is, 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 Chapter 3.
-
-Note: If you are using TT v2.15 you will need to change the code that 
-outputs the "last name for the first author" above to match this:
-
-    [% authors = book.authors %]
-    by '[% authors.first.last_name IF authors.first;
-           authors.list.first.value.last_name IF ! authors.first %]'
-
-to get around an issue in TT v2.15 where blessed hash objects were not 
-handled correctly.  But, if you are still using v2.15, it's probably 
-time to upgrade  (v2.15 is almost 4 years old).  If you are following 
-along in Debian, then you should be on at least v2.20.  You can test 
-your version of Template Toolkit with the following:
-
-    perl -MTemplate -e 'print "$Template::VERSION\n"'
+The TT C<USE> directive allows access to a variety of plugin modules (TT
+plugins, that is, not Catalyst plugins) to add extra functionality to
+the base TT capabilities.  Here, the plugin allows L<Data::Dumper>
+"pretty printing" of objects and variables.  Other than that, the rest
+of the code should be familiar from the examples in Chapter 3.
 
 
 =head2 Try the 'url_create' Feature
@@ -205,21 +204,21 @@ Next, use your browser to enter the following URL:
 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 as it was returned by DBIC.  You should also see the following
-DBIC debug messages displayed in the development server log messages
-if you have DBIC_TRACE set:
+DBIC debug messages displayed in the development server log messages if
+you have DBIC_TRACE set:
 
     INSERT INTO book (rating, title) VALUES (?, ?): `5', `TCPIP_Illustrated_Vol-2'
     INSERT INTO book_author (author_id, book_id) VALUES (?, ?): `4', `6'
 
 The C<INSERT> statements are obviously adding the book and linking it to
-the existing record for Richard Stevens.  The C<SELECT> statement results
-from DBIC automatically fetching the book for the C<Dumper.dump(book)>.
+the existing record for Richard Stevens.  The C<SELECT> statement
+results from DBIC automatically fetching the book for the
+C<Dumper.dump(book)>.
 
-If you then click the "Return to list" link, you should find that 
-there are now six books shown (if necessary, Shift+Reload or 
-Ctrl+Reload your browser at the C</books/list> page).  You should now 
-see the six DBIC debug messages similar to the following (where 
-N=1-6):
+If you then click the "Return to list" link, you should find that there
+are now six books shown (if necessary, Shift+Reload or Ctrl+Reload your
+browser at the C</books/list> page).  You should now see the six DBIC
+debug messages similar to the following (where N=1-6):
 
     SELECT author.id, author.first_name, author.last_name 
         FROM book_author me  JOIN author author 
@@ -230,10 +229,10 @@ N=1-6):
 
 Although the example above uses the same C<Local> action type for the
 method that we saw in the previous chapter of the tutorial, there is an
-alternate approach that allows us to be more specific while also
-paving the way for more advanced capabilities.  Change the method
-declaration for C<url_create> in C<lib/MyApp/Controller/Books.pm> you
-entered above to match the following:
+alternate approach that allows us to be more specific while also paving
+the way for more advanced capabilities.  Change the method declaration
+for C<url_create> in C<lib/MyApp/Controller/Books.pm> you entered above
+to match the following:
 
     sub url_create :Chained('/') :PathPart('books/url_create') :Args(3) {
         # In addition to self & context, get the title, rating, &
@@ -246,12 +245,12 @@ entered above to match the following:
         ...
 
 This converts the method to take advantage of the Chained
-action/dispatch type. Chaining lets you have a single URL
-automatically dispatch to several controller methods, each of which
-can have precise control over the number of arguments that it will
-receive.  A chain can essentially be thought of having three parts --
-a beginning, a middle, and an end.  The bullets below summarize the key
-points behind each of these parts of a chain:
+action/dispatch type. Chaining lets you have a single URL automatically
+dispatch to several controller methods, each of which can have precise
+control over the number of arguments that it will receive.  A chain can
+essentially be thought of having three parts -- a beginning, a middle,
+and an end.  The bullets below summarize the key points behind each of
+these parts of a chain:
 
 
 =over 4
@@ -328,23 +327,22 @@ C<:PathPart('books/url_create')> to specify the base URL to match, and
 C<:Args(3)> to capture exactly three arguments and to end the chain.
 
 As we will see shortly, a chain can consist of as many "links" as you
-wish, with each part capturing some arguments and doing some work
-along the way.  We will continue to use the Chained action type in this
+wish, with each part capturing some arguments and doing some work along
+the way.  We will continue to use the Chained action type in this
 chapter of the tutorial and explore slightly more advanced capabilities
-with the base method and delete feature below.  But Chained dispatch
-is capable of far more.  For additional information, see
+with the base method and delete feature below.  But Chained dispatch is
+capable of far more.  For additional information, see
 L<Catalyst::Manual::Intro/Action types>,
-L<Catalyst::DispatchType::Chained|Catalyst::DispatchType::Chained>,
-and the 2006 Advent calendar entry on the subject:
-L<http://www.catalystframework.org/calendar/2006/10>.
+L<Catalyst::DispatchType::Chained>, and the 2006 Advent calendar entry
+on the subject: L<http://www.catalystframework.org/calendar/2006/10>.
 
 
 =head2 Try the Chained Action
 
 If you look back at the development server startup logs from your
-initial version of the C<url_create> method (the one using the
-C<:Local> attribute), you will notice that it produced output similar
-to the following:
+initial version of the C<url_create> method (the one using the C<:Local>
+attribute), you will notice that it produced output similar to the
+following:
 
     [debug] Loaded Path actions:
     .-------------------------------------+--------------------------------------.
@@ -357,8 +355,9 @@ to the following:
     | /books/url_create                   | /books/url_create                    |
     '-------------------------------------+--------------------------------------'
 
-When the development server restarts, the debug output should change 
-to something along the lines of the following:
+When the development server restarts after our conversion to Chained
+dispatch, the debug output should change to something along the lines of
+the following:
 
     [debug] Loaded Path actions:
     .-------------------------------------+--------------------------------------.
@@ -377,8 +376,8 @@ to something along the lines of the following:
     | /books/url_create/*/*/*             | /books/url_create                    |
     '-------------------------------------+--------------------------------------'
 
-C<url_create> has disappeared from the "Loaded Path actions" section
-but it now shows up under the newly created "Loaded Chained actions"
+C<url_create> has disappeared from the "Loaded Path actions" section but
+it now shows up under the newly created "Loaded Chained actions"
 section.  And the "/*/*/*" portion clearly shows our requirement for
 three arguments.
 
@@ -395,8 +394,8 @@ are now seven books shown (two copies of I<TCPIP_Illustrated_Vol-2>).
 
 =head2 Refactor to Use a 'base' Method to Start the Chains
 
-Let's make a quick update to our initial Chained action to show a
-little more of the power of chaining.  First, open
+Let's make a quick update to our initial Chained action to show a little
+more of the power of chaining.  First, open
 C<lib/MyApp/Controller/Books.pm> in your editor and add the following
 method:
 
@@ -421,11 +420,11 @@ C<$c-E<gt>stash-E<gt>{resultset}> so that it's automatically available
 for other actions that chain off C<base>.  If your controller always
 needs a book ID as its first argument, you could have the base method
 capture that argument (with C<:CaptureArgs(1)>) and use it to pull the
-book object with C<-E<gt>find($id)> and leave it in the stash for
-later parts of your chains to then act upon. Because we have several
-actions that don't need to retrieve a book (such as the C<url_create>
-we are working with now), we will instead add that functionality
-to a common C<object> action shortly.
+book object with C<-E<gt>find($id)> and leave it in the stash for later
+parts of your chains to then act upon. Because we have several actions
+that don't need to retrieve a book (such as the C<url_create> we are
+working with now), we will instead add that functionality to a common
+C<object> action shortly.
 
 As for C<url_create>, let's modify it to first dispatch to C<base>.
 Open up C<lib/MyApp/Controller/Books.pm> and edit the declaration for
@@ -433,8 +432,8 @@ C<url_create> to match the following:
 
     sub url_create :Chained('base') :PathPart('url_create') :Args(3) {
 
-Once you save C<lib/MyApp/Controller/Books.pm>, notice that the 
-development server will restart and our "Loaded Chained actions" section 
+Once you save C<lib/MyApp/Controller/Books.pm>, notice that the
+development server will restart and our "Loaded Chained actions" section
 will changed slightly:
 
     [debug] Loaded Chained actions:
@@ -445,29 +444,29 @@ will changed slightly:
     |                                     | => /books/url_create                 |
     '-------------------------------------+--------------------------------------'
 
-The "Path Spec" is the same, but now it maps to two Private actions as 
-we would expect.  The C<base> method is being triggered by the 
-C</books> part of the URL.  However, the processing then continues to 
-the C<url_create> method because this method "chained" off C<base> and 
-specified C<:PathPart('url_create')> (note that we could have omitted 
-the "PathPart" here because it matches the name of the method, but we 
+The "Path Spec" is the same, but now it maps to two Private actions as
+we would expect.  The C<base> method is being triggered by the C</books>
+part of the URL.  However, the processing then continues to the
+C<url_create> method because this method "chained" off C<base> and
+specified C<:PathPart('url_create')> (note that we could have omitted
+the "PathPart" here because it matches the name of the method, but we
 will include it to make the logic as explicit as possible).
 
 Once again, enter the following URL into your browser:
 
     http://localhost:3000/books/url_create/TCPIP_Illustrated_Vol-2/5/4
 
-The same "Added book 'TCPIP_Illustrated_Vol-2' by 'Stevens' with a 
-rating of 5." message and a dump of the new book object should appear. 
-Also notice the extra "INSIDE BASE METHOD" debug message in the 
-development server output from the C<base> method.  Click the "Return 
-to list" link, and you should find that there are now eight books 
-shown.  (You may have a larger number of books if you repeated any of 
-the "create" actions more than once.  Don't worry about it as long as 
-the number of books is appropriate for the number of times you added 
-new books... there should be the original five books added via
-C<myapp01.sql> plus one additional book for each time you ran one
-of the url_create variations above.)
+The same "Added book 'TCPIP_Illustrated_Vol-2' by 'Stevens' with a
+rating of 5." message and a dump of the new book object should appear.
+Also notice the extra "INSIDE BASE METHOD" debug message in the
+development server output from the C<base> method.  Click the "Return to
+list" link, and you should find that there are now eight books shown.
+(You may have a larger number of books if you repeated any of the
+"create" actions more than once.  Don't worry about it as long as the
+number of books is appropriate for the number of times you added new
+books... there should be the original five books added via
+C<myapp01.sql> plus one additional book for each time you ran one of the
+url_create variations above.)
 
 
 =head1 MANUALLY BUILDING A CREATE FORM
@@ -475,7 +474,9 @@ of the url_create variations above.)
 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.
+to enter data.  This section begins to address that concern (but just
+barely, see L<Chapter 9|Catalyst::Manual::Tutorial::09_AdvancedCRUD>
+for better options for handling web-based forms).
 
 
 =head2 Add Method to Display The Form
@@ -546,10 +547,6 @@ save the form information to the database:
         # Note: Above is a shortcut for this:
         # $book->create_related('book_authors', {author_id => $author_id});
     
-        # Avoid Data::Dumper issue mentioned earlier
-        # You can probably omit this
-        $Data::Dumper::Useperl = 1;
-    
         # Store new model object in stash and set template
         $c->stash(book     => $book,
                   template => 'books/create_done.tt2');
@@ -558,8 +555,8 @@ save the form information to the database:
 
 =head2 Test Out The Form
 
-Notice that the server startup log reflects the two new chained
-methods that we added:
+Notice that the server startup log reflects the two new chained methods
+that we added:
 
     [debug] Loaded Chained actions:
     .-------------------------------------+--------------------------------------.
@@ -581,7 +578,8 @@ C<create_done.tt2> template seen in earlier examples.  Finally, click
 
 B<Note:> Having the user enter the primary key ID for the author is
 obviously crude; we will address this concern with a drop-down list and
-add validation to our forms in Chapter 9.
+add validation to our forms in
+L<Chapter 9|Catalyst::Manual::Tutorial::09_AdvancedCRUD>.
 
 
 =head1 A SIMPLE DELETE FEATURE
@@ -593,18 +591,20 @@ from the database.
 
 =head2 Include a Delete Link in the List
 
-Edit C<root/src/books/list.tt2> and update it to match 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):
+Edit C<root/src/books/list.tt2> and update it to match the following
+(two sections have changed: 1) the additional '<th>Links</th>' table
+header, and 2) the five 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. -%]
+    [% # This is a TT comment. -%]
     
-    [% # Provide a title -%]
+    [%- # Provide a title -%]
     [% META title = 'Book List' -%]
     
+    [% # Note That the '-' at the beginning or end of TT code  -%]
+    [% # "chomps" the whitespace/newline at that end of the    -%]
+    [% # output (use View Source in browser to see the effect) -%]
+    
+    [% # Some basic HTML with a loop to display books -%]
     <table>
     <tr><th>Title</th><th>Rating</th><th>Author(s)</th><th>Links</th></tr>
     [% # Display each book in a table row %]
@@ -613,12 +613,12 @@ and 2) the four lines for the Delete link near the bottom):
         <td>[% book.title %]</td>
         <td>[% book.rating %]</td>
         <td>
-          [% # NOTE: See "Exploring The Power of DBIC" for a better way to do this!  -%]
+          [% # NOTE: See Chapter 4 for a better way to do this!                      -%]
           [% # First initialize a TT variable to hold a list.  Then use a TT FOREACH -%]
           [% # loop in 'side effect notation' to load just the last names of the     -%]
           [% # authors into the list. Note that the 'push' TT vmethod doesn't return -%]
           [% # a value, so nothing will be printed here.  But, if you have something -%]
-          [% # in TT that does return a value and you don't want it printed, you can -%]
+          [% # in TT that does return a value and you don't want it printed, you     -%]
           [% # 1) assign it to a bogus value, or                                     -%]
           [% # 2) use the CALL keyword to call it and discard the return value.      -%]
           [% tt_authors = [ ];
@@ -631,7 +631,8 @@ and 2) the four lines for the Delete link near the bottom):
         </td>
         <td>
           [% # Add a link to delete a book %]
-          <a href="[% c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
+          <a href="[%
+            c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
         </td>
       </tr>
     [% END -%]
@@ -639,32 +640,32 @@ and 2) the four lines for the Delete link near the bottom):
 
 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; in practice, anything that
-modifies data should be handled with a form sending a POST request).
-
-Also notice that we are using a more advanced form of C<uri_for> than
-we have seen before.  Here we use
-C<$c-E<gt>controller-E<gt>action_for> to automatically generate a URI
-appropriate for that action based on the method we want to link to
-while inserting the C<book.id> value into the appropriate place.  Now,
-if you ever change C<:PathPart('delete')> in your controller method to
-C<:PathPart('kill')>, then your links will automatically update
-without any changes to your .tt2 template file.  As long as the name
-of your method does not change (here, "delete"), then your links will
-still be correct.  There are a few shortcuts and options when using
-C<action_for()>:
+will be used instead of full HTML buttons; but, in practice, anything
+that modifies data should be handled with a form sending a POST
+request).
+
+Also notice that we are using a more advanced form of C<uri_for> than we
+have seen before.  Here we use C<$c-E<gt>controller-E<gt>action_for> to
+automatically generate a URI appropriate for that action based on the
+method we want to link to while inserting the C<book.id> value into the
+appropriate place.  Now, if you ever change C<:PathPart('delete')> in
+your controller method to something like C<:PathPart('kill')>, then your
+links will automatically update without any changes to your .tt2
+template file.  As long as the name of your method does not change
+(here, "delete"), then your links will still be correct.  There are a
+few shortcuts and options when using C<action_for()>:
 
 =over 4
 
 =item *
 
-If you are referring to a method in the current controller, you can
-use C<$self-E<gt>action_for('_method_name_')>.
+If you are referring to a method in the current controller, you can use
+C<$self-E<gt>action_for('_method_name_')>.
 
 =item *
 
-If you are referring to a method in a different controller, you need
-to include that controller's name as an argument to C<controller()>, as in
+If you are referring to a method in a different controller, you need to
+include that controller's name as an argument to C<controller()>, as in
 C<$c-E<gt>controller('_controller_name_')-E<gt>action_for('_method_name_')>.
 
 =back
@@ -676,20 +677,20 @@ doing it here for illustrative and simplicity purposes only.
 
 =head2 Add a Common Method to Retrieve a Book for the Chain
 
-As mentioned earlier, since we have a mixture of actions that operate
-on a single book ID and others that do not, we should not have C<base>
+As mentioned earlier, since we have a mixture of actions that operate on
+a single book ID and others that do not, we should not have C<base>
 capture the book ID, find the corresponding book in the database and
 save it in the stash for later links in the chain.  However, just
-because that logic does not belong in C<base> doesn't mean that we
-can't create another location to centralize the book lookup code.  In
-our case, we will create a method called C<object> that will store the
+because that logic does not belong in C<base> doesn't mean that we can't
+create another location to centralize the book lookup code.  In our
+case, we will create a method called C<object> that will store the
 specific book in the stash.  Chains that always operate on a single
 existing book can chain off this method, but methods such as
-C<url_create> that don't operate on an existing book can chain
-directly off base.
+C<url_create> that don't operate on an existing book can chain directly
+off base.
 
-To add the C<object> method, edit C<lib/MyApp/Controller/Books.pm>
-and add the following code:
+To add the C<object> method, edit C<lib/MyApp/Controller/Books.pm> and
+add the following code:
 
     =head2 object
     
@@ -714,9 +715,8 @@ and add the following code:
         $c->log->debug("*** INSIDE OBJECT METHOD for obj id=$id ***");
     }
 
-Now, any other method that chains off C<object> will automatically
-have the appropriate book waiting for it in
-C<$c-E<gt>stash-E<gt>{object}>.
+Now, any other method that chains off C<object> will automatically have
+the appropriate book waiting for it in C<$c-E<gt>stash-E<gt>{object}>.
 
 
 =head2 Add a Delete Action to the Controller
@@ -745,8 +745,8 @@ following method:
     }
 
 This method first deletes the book object saved by the C<object> method.
-However, it also removes the corresponding entry from the
-C<book_author> table with a cascading delete.
+However, it also removes the corresponding entry from the C<book_author>
+table with a cascading delete.
 
 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
@@ -761,9 +761,9 @@ equivalent.
 
 =head2 Try the Delete Feature
 
-Once you save the Books controller, the server should automatically restart.
-The C<delete> method should now appear in the "Loaded Chained actions" section
-of the startup debug output:
+Once you save the Books controller, the server should automatically
+restart.  The C<delete> method should now appear in the "Loaded Chained
+actions" section of the startup debug output:
 
     [debug] Loaded Chained actions:
     .-------------------------------------+--------------------------------------.
@@ -788,8 +788,6 @@ cascading delete operation via the DBIC_TRACE output:
 
     SELECT me.id, me.title, me.rating FROM book me WHERE ( ( me.id = ? ) ): '6'
     DELETE FROM book WHERE ( id = ? ): '6'
-    SELECT me.book_id, me.author_id FROM book_author me WHERE ( me.book_id = ? ): '6'
-    DELETE FROM book_author WHERE ( author_id = ? AND book_id = ? ): '4', '6'
 
 
 =head2 Fixing a Dangerous URL
@@ -800,21 +798,21 @@ prior step -- it is still referencing the delete action:
     http://localhost:3000/books/id/6/delete
 
 What if the user were to press reload with this URL still active?  In
-this case the redundant delete is harmless (although it does generate
-an exception screen, it doesn't perform any undesirable actions on the
-application or database), but in other cases this could clearly be
-extremely dangerous.
+this case the redundant delete is harmless (although it does generate an
+exception screen, it doesn't perform any undesirable actions on the
+application or database), but in other cases this could clearly lead to
+trouble.
 
 We can improve the logic by converting to a redirect.  Unlike
-C<$c-E<gt>forward('list'))> or C<$c-E<gt>detach('list'))> that perform
-a server-side alteration in the flow of processing, a redirect is a
-client-side mechanism that causes the browser to issue an entirely
-new request.  As a result, the URL in the browser is updated to match
-the destination of the redirection URL.
+C<$c-E<gt>forward('list'))> or C<$c-E<gt>detach('list'))> that perform a
+server-side alteration in the flow of processing, a redirect is a
+client-side mechanism that causes the browser to issue an entirely new
+request.  As a result, the URL in the browser is updated to match the
+destination of the redirection URL.
 
-To convert the forward used in the previous section to a redirect,
-open C<lib/MyApp/Controller/Books.pm> and edit the existing
-C<sub delete> method to match:
+To convert the forward used in the previous section to a redirect, open
+C<lib/MyApp/Controller/Books.pm> and edit the existing C<sub delete>
+method to match:
 
     =head2 delete
     
@@ -840,24 +838,25 @@ C<sub delete> method to match:
 
 =head2 Try the Delete and Redirect Logic
 
-Point your browser to L<http://localhost:3000/books/list> (don't just 
-hit "Refresh" in your browser since we left the URL in an invalid state 
-in the previous section!) and delete the first copy of the remaining two 
-"TCPIP_Illustrated_Vol-2" books. The URL in your browser should return 
-to the L<http://localhost:3000/books/list> URL, so that is an 
-improvement, but notice that I<no green "Book deleted" status message is 
-displayed>. Because the stash is reset on every request (and a redirect 
-involves a second request), the C<status_msg> is cleared before it can 
-be displayed. 
+Point your browser to L<http://localhost:3000/books/list> (don't just
+hit "Refresh" in your browser since we left the URL in an invalid state
+in the previous section!) and delete the first copy of the remaining two
+"TCPIP_Illustrated_Vol-2" books. The URL in your browser should return
+to the L<http://localhost:3000/books/list> URL, so that is an
+improvement, but notice that I<no green "Book deleted" status message is
+displayed>. Because the stash is reset on every request (and a redirect
+involves a second request), the C<status_msg> is cleared before it can
+be displayed.
 
 
 =head2 Using 'uri_for' to Pass Query Parameters
 
-There are several ways to pass information across a redirect. One 
-option is to use the C<flash> technique that we will see in Chapter 5 
-of this tutorial; however, here we will pass the information via query 
-parameters on the redirect itself.  Open 
-C<lib/MyApp/Controller/Books.pm> and update the existing C<sub delete> 
+There are several ways to pass information across a redirect. One option
+is to use the C<flash> technique that we will see in
+L<Chapter 5|Catalyst::Manual::Tutorial::05_Authentication> of this
+tutorial; however, here we will pass the information via query
+parameters on the redirect itself.  Open
+C<lib/MyApp/Controller/Books.pm> and update the existing C<sub delete>
 method to match the following:
 
     =head2 delete
@@ -886,56 +885,51 @@ query parameter:
     ...
     <div id="content">
         [%# Status and error messages %]
-        <span class="message">[% status_msg || c.request.params.status_msg | html %]</span>
+        <span class="message">[%
+            status_msg || c.request.params.status_msg | html %]</span>
         <span class="error">[% error_msg %]</span>
         [%# This is where TT will stick all of your template's contents. -%]
         [% content %]
     </div><!-- end content -->
     ...
 
-Although the sample above only shows the C<content> div, leave the
-rest of the file intact -- the only change we made to the C<wrapper.tt2>
-was to add "C<|| c.request.params.status_msg>" to the
-C<E<lt>span class="message"E<gt>> line.
+Although the sample above only shows the C<content> div, leave the rest
+of the file intact -- the only change we made to the C<wrapper.tt2> was
+to add "C<|| c.request.params.status_msg>" to the
+C<E<lt>span class="message"E<gt>> line.  Note that we definitely want
+the "C<| html>" TT filter here since it would be easy for users to
+modify the message on the URL and possibly inject harmful code into the
+application if we left that off.
 
 
 =head2 Try the Delete and Redirect With Query Param Logic
 
-Point your browser to L<http://localhost:3000/books/list> (you should 
-now be able to safely hit "refresh" in your browser). Then delete the 
-remaining copy of "TCPIP_Illustrated_Vol-2". The green "Book deleted" 
+Point your browser to L<http://localhost:3000/books/list> (you should
+now be able to safely hit "refresh" in your browser). Then delete the
+remaining copy of "TCPIP_Illustrated_Vol-2". The green "Book deleted"
 status message should return.  But notice that you can now hit the
-"Reload" button in your browser and it just redisplays the book
-list (and it correctly shows it without the "Book deleted" message
-on redisplay).
-
-B<NOTE:> Another popular method for maintaining server-side
-information across a redirect is to use the C<flash> technique we
-discuss in the next chapter of the tutorial,
-L<Authentication|Catalyst::Manual::Tutorial::05_Authentication>. While
-C<flash> is a "slicker" mechanism in that it's all handled by the
-server and doesn't "pollute" your URLs, B<it is important to note that
-C<flash> can lead to situations where the wrong information shows up
-in the wrong browser window if the user has multiple windows or
-browser tabs open>.  For example, Window A causes something to be
-placed in the stash, but before that window performs a redirect,
-Window B makes a request to the server and gets the status information
-that should really go to Window A.  For this reason, you may wish
-to use the "query param" technique shown here in your applications.
+"Reload" button in your browser and it just redisplays the book list
+(and it correctly shows it without the "Book deleted" message on
+redisplay).
+
+B<NOTE:> Be sure to check out
+L<Authentication|Catalyst::Manual::Tutorial::05_Authentication> where we
+use an improved technique that is better suited to your real world
+applications.
 
 
 =head1 EXPLORING THE POWER OF DBIC
 
-In this section we will explore some additional capabilities offered
-by DBIx::Class.  Although these features have relatively little to do
+In this section we will explore some additional capabilities offered by
+L<DBIx::Class>.  Although these features have relatively little to do
 with Catalyst per se, you will almost certainly want to take advantage
 of them in your applications.
 
 
 =head2 Add Datetime Columns to Our Existing Books Table
 
-Let's add two columns to our existing C<books> table to track when
-each book was added and when each book is updated:
+Let's add two columns to our existing C<books> table to track when each
+book was added and when each book is updated:
 
     $ sqlite3 myapp.db
     sqlite> ALTER TABLE book ADD created TIMESTAMP;
@@ -951,14 +945,23 @@ each book was added and when each book is updated:
     sqlite> .quit
     $
 
-This will modify the C<books> table to include the two new fields
-and populate those fields with the current time.
+Here are the commands without the surrounding sqlite3 prompt and output
+in case you want to cut and paste them as a single block (but still
+start sqlite3 before you paste these in):
+
+    ALTER TABLE book ADD created TIMESTAMP;
+    ALTER TABLE book ADD updated TIMESTAMP;
+    UPDATE book SET created = DATETIME('NOW'), updated = DATETIME('NOW');
+    SELECT * FROM book;
+
+This will modify the C<books> table to include the two new fields and
+populate those fields with the current time.
 
 
 =head2 Update DBIx::Class to Automatically Handle the Datetime Columns
 
-Next, we should re-run the DBIC helper to update the Result Classes
-with the new fields:
+Next, we should re-run the DBIC helper to update the Result Classes with
+the new fields:
 
     $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \
         create=static components=TimeStamp dbi:SQLite:myapp.db \
@@ -969,21 +972,21 @@ with the new fields:
     Schema dump completed.
      exists "/root/dev/MyApp/script/../lib/MyApp/Model/DB.pm"
 
-Notice that we modified our use of the helper slightly: we told
-it to include the L<DBIx::Class::TimeStamp|DBIx::Class::TimeStamp>
-in the C<load_components> line of the Result Classes.
+Notice that we modified our use of the helper slightly: we told it to
+include the L<DBIx::Class::TimeStamp> in the C<load_components> line of
+the Result Classes.
 
-If you open C<lib/MyApp/Schema/Result/Book.pm> in your editor you should 
-see that the C<created> and C<updated> fields are now included in the 
-call to C<add_columns()>. However, also notice that the C<many_to_many> 
-relationships we manually added below the "C<# DO NOT MODIFY...>" line 
-were automatically preserved. 
+If you open C<lib/MyApp/Schema/Result/Book.pm> in your editor you should
+see that the C<created> and C<updated> fields are now included in the
+call to C<add_columns()>. However, also notice that the C<many_to_many>
+relationships we manually added below the "C<# DO NOT MODIFY...>" line
+were automatically preserved.
 
-While we have this file open, let's update it with some additional
-information to have DBIC automatically handle the updating of these
-two fields for us.  Insert the following code at the bottom of the
-file (it B<must> be B<below> the "C<# DO NOT MODIFY...>" line and
-B<above> the C<1;> on the last line):
+While we C<lib/MyApp/Schema/Result/Book.pm> open, let's update it with
+some additional information to have DBIC automatically handle the
+updating of these two fields for us.  Insert the following code at the
+bottom of the file (it B<must> be B<below> the "C<# DO NOT MODIFY...>"
+line and B<above> the C<1;> on the last line):
 
     #
     # Enable automatic date handling
@@ -995,20 +998,28 @@ B<above> the C<1;> on the last line):
         { data_type => 'timestamp', set_on_create => 1, set_on_update => 1 },
     );
 
-This will override the definition for these fields that Schema::Loader 
-placed at the top of the file.  The C<set_on_create> and 
-C<set_on_update> options will cause DBIx::Class to automatically 
-update the timestamps in these columns whenever a row is created or 
-modified.
+This will override the definition for these fields that Schema::Loader
+placed at the top of the file.  The C<set_on_create> and
+C<set_on_update> options will cause DBIx::Class to automatically update
+the timestamps in these columns whenever a row is created or modified.
+
+B<Note> that adding the lines above will cause the development server to
+automatically restart if you are running it with the "-r" option.  In
+other words, the development server is smart enough to restart not only
+for code under the C<MyApp/Controller/>, C<MyApp/Model/>, and
+C<MyApp/View/> directories, but also under other directions such as our
+"external DBIC model" in C<MyApp/Schema/>.  However, also note that it's
+smart enough to B<not> restart when you edit your C<.tt2> files under
+C<root/>.
 
 Then enter the following URL into your web browser:
 
     http://localhost:3000/books/url_create/TCPIP_Illustrated_Vol-2/5/4
 
-You should get the same "Book Created" screen we saw above.  However,
-if you now use the sqlite3 command-line tool to dump the C<books> table,
-you will see that the new book we added has an appropriate date and
-time entered for it (see the last line in the listing below):
+You should get the same "Book Created" screen we saw earlier.  However, if
+you now use the sqlite3 command-line tool to dump the C<books> table,
+you will see that the new book we added has an appropriate date and time
+entered for it (see the last line in the listing below):
 
     $ sqlite3 myapp.db "select * from book"
     1|CCSP SNRS Exam Certification Guide|5|2010-02-16 04:15:45|2010-02-16 04:15:45
@@ -1018,7 +1029,6 @@ time entered for it (see the last line in the listing below):
     5|Designing with Web Standards|5|2010-02-16 04:15:45|2010-02-16 04:15:45
     9|TCP/IP Illustrated, Vol 3|5|2010-02-16 04:15:45|2010-02-16 04:15:45
     10|TCPIP_Illustrated_Vol-2|5|2010-02-16 04:18:42|2010-02-16 04:18:42
-    sqlite> .q
 
 Notice in the debug log that the SQL DBIC generated has changed to
 incorporate the datetime logic:
@@ -1032,15 +1042,15 @@ incorporate the datetime logic:
 
 An often overlooked but extremely powerful features of DBIC is that it
 allows you to supply your own subclasses of C<DBIx::Class::ResultSet>.
-It allows you to pull complex and unsightly "query code" out of your
+This can be used to pull complex and unsightly "query code" out of your
 controllers and encapsulate it in a method of your ResultSet Class.
-These "canned queries" in your ResultSet Class can then be invoked
-via a single call, resulting in much cleaner and easier to read
-controller code.
+These "canned queries" in your ResultSet Class can then be invoked via a
+single call, resulting in much cleaner and easier to read controller
+code (or View code, if that's where you want to call it).
 
 To illustrate the concept with a fairly simple example, let's create a
-method that returns books added in the last 10 minutes.  Start by
-making a directory where DBIx::Class will look for our ResultSet Class:
+method that returns books added in the last 10 minutes.  Start by making
+a directory where DBIx::Class will look for our ResultSet Class:
 
     $ mkdir lib/MyApp/Schema/ResultSet
 
@@ -1094,33 +1104,33 @@ Then add the following method to the C<lib/MyApp/Controller/Books.pm>:
         $c->stash(template => 'books/list.tt2');
     }
 
-Now try different values for the "minutes" argument (the final number 
-value) using the URL C<http://localhost:3000/books/list_recent/_#_> in 
-your browser. For example, this would list all books added in the last 
-fifteen minutes: 
+Now try different values for the "minutes" argument (the final number
+value) using the URL C<http://localhost:3000/books/list_recent/_#_> in
+your browser. For example, this would list all books added in the last
+fifteen minutes:
 
     http://localhost:3000/books/list_recent/15
 
-Depending on how recently you added books, you might want to
-try a higher or lower value for the minutes.
+Depending on how recently you added books, you might want to try a
+higher or lower value for the minutes.
 
 
 =head2 Chaining ResultSets
 
-One of the most helpful and powerful features in DBIx::Class is that 
-it allows you to "chain together" a series of queries (note that this 
-has nothing to do with the "Chained Dispatch" for Catalyst that we 
-were discussing above).  Because each ResultSet returns another 
-ResultSet, you can take an initial query and immediately feed that 
-into a second query (and so on for as many queries you need). Note 
-that no matter how many ResultSets you chain together, the database 
-itself will not be hit until you use a method that attempts to access 
-the data. And, because this technique carries over to the ResultSet 
-Class feature we implemented in the previous section for our "canned 
-search", we can combine the two capabilities.  For example, let's add 
-an action to our C<Books> controller that lists books that are both 
-recent I<and> have "TCP" in the title.  Open up 
-C<lib/MyApp/Controller/Books.pm> and add the following method:
+One of the most helpful and powerful features in C<DBIx::Class> is that
+it allows you to "chain together" a series of queries (note that this
+has nothing to do with the "Chained Dispatch" for Catalyst that we were
+discussing earlier).  Because each ResultSet method returns another
+ResultSet, you can take an initial query and immediately feed that into
+a second query (and so on for as many queries you need).  Note that no
+matter how many ResultSets you chain together, the database itself will
+not be hit until you use a method that attempts to access the data. And,
+because this technique carries over to the ResultSet Class feature we
+implemented in the previous section for our "canned search", we can
+combine the two capabilities.  For example, let's add an action to our
+C<Books> controller that lists books that are both recent I<and> have
+"TCP" in the title.  Open up C<lib/MyApp/Controller/Books.pm> and add
+the following method:
 
     =head2 list_recent_tcp
     
@@ -1135,10 +1145,11 @@ C<lib/MyApp/Controller/Books.pm> and add the following method:
         # stash where they can be accessed by the TT template, but only
         # retrieve books created within the last $min number of minutes
         # AND that have 'TCP' in the title
-        $c->stash(books => [$c->model('DB::Book')
-                                ->created_after(DateTime->now->subtract(minutes => $mins))
-                                ->search({title => {'like', '%TCP%'}})
-                            ]);
+        $c->stash(books => [
+                $c->model('DB::Book')
+                    ->created_after(DateTime->now->subtract(minutes => $mins))
+                    ->search({title => {'like', '%TCP%'}})
+            ]);
     
         # Set the TT template to use.  You will almost always want to do this
         # in your action methods (action methods respond to user input in
@@ -1152,9 +1163,9 @@ To try this out, enter the following URL into your browser:
 
 And you should get a list of books added in the last 100 minutes that
 contain the string "TCP" in the title.  However, if you look at all
-books within the last 100 minutes, you should get a longer list
-(again, you might have to adjust the number of minutes depending on
-how recently you added books to your database):
+books within the last 100 minutes, you should get a longer list (again,
+you might have to adjust the number of minutes depending on how recently
+you added books to your database):
 
     http://localhost:3000/books/list_recent/100
 
@@ -1164,10 +1175,10 @@ the first URL and you should see something similar to the following:
     SELECT me.id, me.title, me.rating, me.created, me.updated FROM book me 
     WHERE ( ( title LIKE ? AND created > ? ) ): '%TCP%', '2010-02-16 02:49:32'
 
-However, let's not pollute our controller code with this raw "TCP"
-query -- it would be cleaner to encapsulate that code in a method on
-our ResultSet Class.  To do this, open
-C<lib/MyApp/Schema/ResultSet/Book.pm> and add the following method:
+However, let's not pollute our controller code with this raw "TCP" query
+-- it would be cleaner to encapsulate that code in a method on our
+ResultSet Class.  To do this, open C<lib/MyApp/Schema/ResultSet/Book.pm>
+and add the following method:
 
     =head2 title_like
     
@@ -1202,10 +1213,11 @@ shown here -- the rest of the method should be the same):
         # stash where they can be accessed by the TT template, but only
         # retrieve books created within the last $min number of minutes
         # AND that have 'TCP' in the title
-        $c->stash(books => [$c->model('DB::Book')
-                                ->created_after(DateTime->now->subtract(minutes => $mins))
-                                ->title_like('TCP')
-                            ]);
+        $c->stash(books => [
+                $c->model('DB::Book')
+                    ->created_after(DateTime->now->subtract(minutes => $mins))
+                    ->title_like('TCP')
+            ]);
     
         # Set the TT template to use.  You will almost always want to do this
         # in your action methods (action methods respond to user input in
@@ -1213,22 +1225,22 @@ shown here -- the rest of the method should be the same):
         $c->stash(template => 'books/list.tt2');
     }
 
-Try out the C<list_recent_tcp> and C<list_recent> URLs as we did above. 
-They should work just the same, but our code is obviously cleaner and 
-more modular, while also being more flexible at the same time. 
+Try out the C<list_recent_tcp> and C<list_recent> URLs as we did above.
+They should work just the same, but our code is obviously cleaner and
+more modular, while also being more flexible at the same time.
 
 
 =head2 Adding Methods to Result Classes
 
-In the previous two sections we saw a good example of how we could use 
-DBIx::Class ResultSet Classes to clean up our code for an entire query 
-(for example, our "canned searches" that filtered the entire query). 
-We can do a similar improvement when working with individual rows as 
-well.  Whereas the ResultSet construct is used in DBIC to correspond 
-to an entire query, the Result Class construct is used to represent a 
-row. Therefore, we can add row-specific "helper methods" to our Result 
-Classes stored in C<lib/MyApp/Schema/Result/>. For example, open 
-C<lib/MyApp/Schema/Result/Author.pm> and add the following method (as 
+In the previous two sections we saw a good example of how we could use
+DBIx::Class ResultSet Classes to clean up our code for an entire query
+(for example, our "canned searches" that filtered the entire query).  We
+can do a similar improvement when working with individual rows as well.
+Whereas the ResultSet construct is used in DBIC to correspond to an
+entire query, the Result Class construct is used to represent a row.
+Therefore, we can add row-specific "helper methods" to our Result
+Classes stored in C<lib/MyApp/Schema/Result/>. For example, open
+C<lib/MyApp/Schema/Result/Author.pm> and add the following method (as
 always, it must be above the closing "C<1;>"):
 
     #
@@ -1240,9 +1252,9 @@ always, it must be above the closing "C<1;>"):
         return $self->first_name . ' ' . $self->last_name;
     }
 
-This will allow us to conveniently retrieve both the first and last
-name for an author in one shot.  Now open C<root/src/books/list.tt2>
-and change the definition of C<tt_authors> from this:
+This will allow us to conveniently retrieve both the first and last name
+for an author in one shot.  Now open C<root/src/books/list.tt2> and
+change the definition of C<tt_authors> from this:
 
     ...
       [% tt_authors = [ ];
@@ -1256,16 +1268,16 @@ to:
          tt_authors.push(author.full_name) FOREACH author = book.authors %]
     ...
 
-(Only C<author.last_name> was changed to C<author.full_name> -- the
-rest of the file should remain the same.)
+(Only C<author.last_name> was changed to C<author.full_name> -- the rest
+of the file should remain the same.)
 
 Now go to the standard book list URL:
 
     http://localhost:3000/books/list
 
 The "Author(s)" column will now contain both the first and last name.
-And, because the concatenation logic was encapsulated inside our
-Result Class, it keeps the code inside our TT template nice and clean
+And, because the concatenation logic was encapsulated inside our Result
+Class, it keeps the code inside our TT template nice and clean
 (remember, we want the templates to be as close to pure HTML markup as
 possible). Obviously, this capability becomes even more useful as you
 use it to remove even more complicated row-specific logic from your
@@ -1274,9 +1286,9 @@ templates!
 
 =head2 Moving Complicated View Code to the Model
 
-The previous section illustrated how we could use a Result Class 
-method to print the full names of the authors without adding any extra 
-code to our view, but it still left us with a fairly ugly mess (see
+The previous section illustrated how we could use a Result Class method
+to print the full names of the authors without adding any extra code to
+our view, but it still left us with a fairly ugly mess (see
 C<root/src/books/list.tt2>):
 
     ...
@@ -1299,9 +1311,9 @@ C<root/src/books/list.tt2>):
     </td>
     ...
 
-Let's combine some of the techniques used earlier in this section to 
-clean this up.  First, let's add a method to our Book Result Class to 
-return the number of authors for a book.  Open 
+Let's combine some of the techniques used earlier in this section to
+clean this up.  First, let's add a method to our Book Result Class to
+return the number of authors for a book.  Open
 C<lib/MyApp/Schema/Result/Book.pm> and add the following method:
 
     =head2 author_count
@@ -1340,8 +1352,8 @@ same C<lib/MyApp/Schema/Result/Book.pm> file:
         return join(', ', @names);
     }
 
-This method loops through each author, using the C<full_name> Result 
-Class method we added to C<lib/MyApp/Schema/Result/Author.pm> in the 
+This method loops through each author, using the C<full_name> Result
+Class method we added to C<lib/MyApp/Schema/Result/Author.pm> in the
 prior section.
 
 Using these two methods, we can simplify our TT code.  Open
@@ -1355,35 +1367,39 @@ match the following:
     </td>
     ...
 
-Although most of the code we removed comprised comments, the overall 
-effect is dramatic... because our view code is so simple, we don't need 
-huge comments to clue people in to the gist of our code. The view code 
-is now self-documenting and readable enough that you could probably get 
-by with no comments at all. All of the "complex" work is being done in 
-our Result Class methods (and, because we have broken the code into 
-nice, modular chunks, the Result Class code is hardly something you 
+Although most of the code we removed comprised comments, the overall
+effect is dramatic... because our view code is so simple, we don't need
+huge comments to clue people in to the gist of our code.  The view code
+is now self-documenting and readable enough that you could probably get
+by with no comments at all.  All of the "complex" work is being done in
+our Result Class methods (and, because we have broken the code into
+nice, modular chunks, the Result Class code is hardly something you
 would call complex).
 
-As we saw in this section, always strive to keep your view AND 
-controller code as simple as possible by pulling code out into your 
-model objects.  Because DBIx::Class can be easily extended in so many 
-ways, it's an excellent to way accomplish this objective.  It will 
-make your code cleaner, easier to write, less error-prone, and easier 
-to debug and maintain.
+As we saw in this section, always strive to keep your view AND
+controller code as simple as possible by pulling code out into your
+model objects.  Because L<DBIx::Class> can be easily extended in so many
+ways, it's an excellent to way accomplish this objective.  It will make
+your code cleaner, easier to write, less error-prone, and easier to
+debug and maintain.
+
+Before you conclude this section, hit Refresh in your browser... the
+output should be the same even though the backend code has been trimmed
+down.
 
-Before you conclude this section, hit Refresh in your browser... the 
-output should be the same even though the backend code has been trimmed 
-down. 
+
+You can jump to the next chapter of the tutorial here:
+L<Authentication|Catalyst::Manual::Tutorial::05_Authentication>
 
 
 =head1 AUTHOR
 
 Kennedy Clark, C<hkclark@gmail.com>
 
-Please report any errors, issues or suggestions to the author.  The
-most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
+Feel free to contact the author for any errors or suggestions, but the
+best way to report issues is via the CPAN RT Bug system at
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
 
-Copyright 2006-2010, Kennedy Clark, under the
+Copyright 2006-2011, Kennedy Clark, under the
 Creative Commons Attribution Share-Alike License Version 3.0
 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).
index fd066b1..8692eed 100644 (file)
@@ -58,14 +58,15 @@ L<Appendices|Catalyst::Manual::Tutorial::10_Appendices>
 
 Now that we finally have a simple yet functional application, we can
 focus on providing authentication (with authorization coming next in
-Chapter 6).
+L<Chapter 6|Catalyst::Manual::Tutorial::06_Authorization>).
 
-This chapter of the tutorial is divided into two main sections: 1) basic,
-cleartext authentication and 2) hash-based authentication.
+This chapter of the tutorial is divided into two main sections: 1)
+basic, cleartext authentication and 2) hash-based authentication.
 
-You can checkout the source code for this example from the catalyst
-subversion repository as per the instructions in
-L<Catalyst::Manual::Tutorial::01_Intro|Catalyst::Manual::Tutorial::01_Intro>.
+Source code for the tutorial in included in the F</root/Final> directory
+of the Tutorial Virtual machine (one subdirectory per chapter).  There
+are also instructions for downloading the code in
+L<Catalyst::Manual::Tutorial::01_Intro>.
 
 
 =head1 BASIC AUTHENTICATION
@@ -78,8 +79,8 @@ application.
 
 First, we add both user and role information to the database (we will
 add the role information here although it will not be used until the
-authorization section, Chapter 6).  Create a new SQL script file by opening
-C<myapp02.sql> in your editor and insert:
+authorization section, Chapter 6).  Create a new SQL script file by
+opening C<myapp02.sql> in your editor and insert:
 
     --
     -- Add users and role tables, along with a many-to-many join table
@@ -124,8 +125,9 @@ Then load this into the C<myapp.db> database with the following command:
 =head2 Add User and Role Information to DBIC Schema
 
 Although we could manually edit the DBIC schema information to include
-the new tables added in the previous step, let's use the C<create=static>
-option on the DBIC model helper to do most of the work for us:
+the new tables added in the previous step, let's use the
+C<create=static> option on the DBIC model helper to do most of the work
+for us:
 
     $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \
         create=static components=TimeStamp dbi:SQLite:myapp.db \
@@ -142,20 +144,19 @@ option on the DBIC model helper to do most of the work for us:
 Notice how the helper has added three new table-specific Result Source
 files to the C<lib/MyApp/Schema/Result> directory.  And, more
 importantly, even if there were changes to the existing result source
-files, those changes would have only been written above the C<# DO NOT
-MODIFY THIS OR ANYTHING ABOVE!> comment and your hand-edited
+files, those changes would have only been written above the
+C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!> comment and your hand-edited
 enhancements would have been preserved.
 
-Speaking of "hand-editted enhancements," we should now add the
+Speaking of "hand-edited enhancements," we should now add the
 C<many_to_many> relationship information to the User Result Source file.
 As with the Book, BookAuthor, and Author files in
 L<Chapter 3|Catalyst::Manual::Tutorial::03_MoreCatalystBasics>,
-L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> has
-automatically created the C<has_many> and C<belongs_to> relationships
-for the new User, UserRole, and Role tables. However, as a convenience
-for mapping Users to their assigned roles (see
-L<Chapter 6|Catalyst::Manual::Tutorial::06_Authorization>), we will
-also manually add a C<many_to_many> relationship. Edit
+L<DBIx::Class::Schema::Loader> has automatically created the C<has_many>
+and C<belongs_to> relationships for the new User, UserRole, and Role
+tables. However, as a convenience for mapping Users to their assigned
+roles (see L<Chapter 6|Catalyst::Manual::Tutorial::06_Authorization>),
+we will also manually add a C<many_to_many> relationship. Edit
 C<lib/MyApp/Schema/Result/User.pm> add the following information between
 the C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!> comment and the closing
 C<1;>:
@@ -166,20 +167,21 @@ C<1;>:
     #     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(roles => 'user_roles', 'role_id');
+    __PACKAGE__->many_to_many(roles => 'user_roles', 'role');
 
 The code for this update is obviously very similar to the edits we made
-to the C<Book> and C<Author> classes created in Chapter 3 with one
+to the C<Book> and C<Author> classes created in
+L<Chapter 3|Catalyst::Manual::Tutorial::03_MoreCatalystBasics> with one
 exception: we only defined the C<many_to_many> relationship in one
 direction. Whereas we felt that we would want to map Authors to Books
 B<AND> Books to Authors, here we are only adding the convenience
 C<many_to_many> in the Users to Roles direction.
 
 Note that we do not need to make any change to the
-C<lib/MyApp/Schema.pm> schema file.  It simply tells DBIC to load all
-of the Result Class and ResultSet Class files it finds in below the
-C<lib/MyApp/Schema> directory, so it will automatically pick up our
-new table information.
+C<lib/MyApp/Schema.pm> schema file.  It simply tells DBIC to load all of
+the Result Class and ResultSet Class files it finds below the
+C<lib/MyApp/Schema> directory, so it will automatically pick up our new
+table information.
 
 
 =head2 Sanity-Check of the Development Server Reload
@@ -209,8 +211,8 @@ objects in the startup debug output:
     '-------------------------------------------------------------------+----------'
     ...
 
-Again, notice that your "Result Class" classes have been "re-loaded"
-by Catalyst under C<MyApp::Model>.
+Again, notice that your "Result Class" classes have been "re-loaded" by
+Catalyst under C<MyApp::Model>.
 
 
 =head2 Include Authentication and Session Plugins
@@ -223,20 +225,21 @@ C<StackTrace> is new):
         -Debug
         ConfigLoader
         Static::Simple
-
+    
         StackTrace
-
+    
         Authentication
-
+    
         Session
         Session::Store::File
         Session::State::Cookie
     /;
 
-B<Note:> As discussed in MoreCatalystBasics, different versions of
-C<Catalyst::Devel> have used a variety of methods to load the plugins,
-but we are going to use the current Catalyst 5.8X practice of putting
-them on the C<use Catalyst> line.
+B<Note:> As discussed in
+L<Chapter 3|Catalyst::Manual::Tutorial::03_MoreCatalystBasics>,
+different versions of C<Catalyst::Devel> have used a variety of methods
+to load the plugins, but we are going to use the current Catalyst 5.9
+practice of putting them on the C<use Catalyst> line.
 
 The C<Authentication> plugin supports Authentication while the
 C<Session> plugins are required to maintain state across multiple HTTP
@@ -244,13 +247,15 @@ requests.
 
 Note that the only required Authentication class is the main one. This
 is a change that occurred in version 0.09999_01 of the
-C<Authentication> plugin. You B<do not need> to specify a particular
-Authentication::Store or Authentication::Credential plugin. Instead,
-indicate the Store and Credential you want to use in your application
-configuration (see below).
+L<Authentication|Catalyst::Plugin::Authentication> plugin. You
+B<do not need> to specify a particular
+L<Authentication::Store|Catalyst::Authentication::Store> or
+C<Authentication::Credential> you want to use.  Instead, indicate the
+Store and Credential you want to use in your application configuration
+(see below).
 
-Make sure you include the additional plugins as new dependencies in
-the Makefile.PL file something like this:
+Make sure you include the additional plugins as new dependencies in the
+Makefile.PL file something like this:
 
     requires 'Catalyst::Plugin::Authentication';
     requires 'Catalyst::Plugin::Session';
@@ -259,24 +264,24 @@ the Makefile.PL file something like this:
 
 Note that there are several options for
 L<Session::Store|Catalyst::Plugin::Session::Store>.
-L<Session::Store::Memcached|Catalyst::Plugin::Session::Store::Memcached> is
-generally a good choice if you are on Unix.  If you are running on
-Windows
-L<Session::Store::File|Catalyst::Plugin::Session::Store::File> is fine. Consult
-L<Session::Store|Catalyst::Plugin::Session::Store> and its subclasses
-for additional information and options (for example to use a database-
-backed session store).
+L<Session::Store::Memcached|Catalyst::Plugin::Session::Store::Memcached>
+is generally a good choice if you are on Unix.  If you are running on
+Windows L<Session::Store::File|Catalyst::Plugin::Session::Store::File>
+is fine. Consult L<Session::Store|Catalyst::Plugin::Session::Store> and
+its subclasses for additional information and options (for example to
+use a database-backed session store).
 
 
 =head2 Configure Authentication
 
 There are a variety of ways to provide configuration information to
-L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication>.
-Here we will use
-L<Catalyst::Authentication::Realm::SimpleDB|Catalyst::Authentication::Realm::SimpleDB>
-because it automatically sets a reasonable set of defaults for us. Open
-C<lib/MyApp.pm> and place the following text above the call to
-C<__PACKAGE__-E<gt>setup();>:
+L<Catalyst::Plugin::Authentication>.  Here we will use
+L<Catalyst::Authentication::Realm::SimpleDB> because it automatically
+sets a reasonable set of defaults for us.  (Note: the C<SimpleDB> here
+has nothing to do with the SimpleDB offered in Amazon's web services
+offerings -- here we are only talking about a "simple" way to use your
+DB as an authentication backend.)  Open C<lib/MyApp.pm> and place the
+following text above the call to C<__PACKAGE__-E<gt>setup();>:
 
     # Configure SimpleDB Authentication
     __PACKAGE__->config(
@@ -289,16 +294,16 @@ C<__PACKAGE__-E<gt>setup();>:
         },
     );
 
-We could have placed this configuration in C<myapp.conf>, but placing
-it in C<lib/MyApp.pm> is probably a better place since it's not likely
+We could have placed this configuration in C<myapp.conf>, but placing it
+in C<lib/MyApp.pm> is probably a better place since it's not likely
 something that users of your application will want to change during
-deployment (or you could use a mixture: leave C<class> and
-C<user_model> defined in C<lib/MyApp.pm> as we show above, but place
-C<password_type> in C<myapp.conf> to allow the type of password to be
-easily modified during deployment).  We will stick with putting
-all of the authentication-related configuration in C<lib/MyApp.pm>
-for the tutorial, but if you wish to use C<myapp.conf>, just convert
-to the following code:
+deployment (or you could use a mixture: leave C<class> and C<user_model>
+defined in C<lib/MyApp.pm> as we show above, but place C<password_type>
+in C<myapp.conf> to allow the type of password to be easily modified
+during deployment).  We will stick with putting all of the
+authentication-related configuration in C<lib/MyApp.pm> for the
+tutorial, but if you wish to use C<myapp.conf>, just convert to the
+following code:
 
     <Plugin::Authentication>
         <default>
@@ -309,8 +314,7 @@ to the following code:
     </Plugin::Authentication>
 
 B<TIP:> Here is a short script that will dump the contents of
-C<MyApp->config> to L<Config::General|Config::General> format in
-C<myapp.conf>:
+C<MyApp->config> to L<Config::General> format in C<myapp.conf>:
 
     $ CATALYST_DEBUG=0 perl -Ilib -e 'use MyApp; use Config::General;
         Config::General->new->save_file("myapp.conf", MyApp->config);'
@@ -319,13 +323,13 @@ B<HOWEVER>, if you try out the command above, be sure to delete the
 "myapp.conf" command.  Otherwise, you will wind up with duplicate
 configurations.
 
-B<NOTE:> Because we are using SimpleDB along with a database layout
-that complies with its default assumptions: we don't need to specify
-the names of the columns where our username and password information
-is stored (hence, the "Simple" part of "SimpleDB").  That being said,
-SimpleDB lets you specify that type of information if you need to.
-Take a look at
-C<Catalyst::Authentication::Realm::SimpleDB|Catalyst::Authentication::Realm::SimpleDB>
+B<NOTE:> Because we are using
+L<SimpleDB|Catalyst::Authentication::Realm::SimpleDB> along with a
+database layout that complies with its default assumptions: we don't
+need to specify the names of the columns where our username and password
+information is stored (hence, the "Simple" part of "SimpleDB").  That
+being said, SimpleDB lets you specify that type of information if you
+need to.  Take a look at C<Catalyst::Authentication::Realm::SimpleDB>
 for details.
 
 
@@ -341,25 +345,22 @@ 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>, locate the
-C<sub index :Path :Args(0)> method (or C<sub index : Private> if you
-are using an older version of Catalyst) that was automatically
-inserted by the helpers when we created the Login controller above,
-and update the definition of C<sub index> to match:
+Then open C<lib/MyApp/Controller/Login.pm>, and update the definition of
+C<sub index> to match:
 
     =head2 index
-
+    
     Login logic
-
+    
     =cut
-
+    
     sub index :Path :Args(0) {
         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
@@ -378,69 +379,60 @@ and update the definition of C<sub index> to match:
             $c->stash(error_msg => "Empty username or password.")
                 unless ($c->user_exists);
         }
-
+    
         # If either of above don't work out, send to the login page
         $c->stash(template => 'login.tt2');
     }
 
-Be sure to remove the
-C<$c-E<gt>response-E<gt>body('Matched MyApp::Controller::Login in Login.');>
-line of the C<sub index>.
-
 This controller fetches the C<username> and C<password> values from the
 login form and attempts to authenticate the user.  If successful, it
 redirects the user to the book list page.  If the login fails, the user
 will stay at the login page and 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.
+C<username> and C<password> values are not present in the form, the user
+will be taken to the empty login form.
 
 Note that we could have used something like "C<sub default :Path>",
-however, it is generally recommended (partly for historical reasons,
-and partly for code clarity) only to use C<default> in
+however, it is generally recommended (partly for historical reasons, and
+partly for code clarity) only to use C<default> in
 C<MyApp::Controller::Root>, and then mainly to generate the 404 not
 found page for the application.
 
 Instead, we are using "C<sub somename :Path :Args(0) {...}>" here to
 specifically match the URL C</login>. C<Path> actions (aka, "literal
-actions") create URI matches relative to the namespace of the
-controller where they are defined.  Although C<Path> supports
-arguments that allow relative and absolute paths to be defined, here
-we use an empty C<Path> definition to match on just the name of the
-controller itself.  The method name, C<index>, is arbitrary. We make
-the match even more specific with the C<:Args(0)> action modifier --
-this forces the match on I<only> C</login>, not
-C</login/somethingelse>.
+actions") create URI matches relative to the namespace of the controller
+where they are defined.  Although C<Path> supports arguments that allow
+relative and absolute paths to be defined, here we use an empty C<Path>
+definition to match on just the name of the controller itself.  The
+method name, C<index>, is arbitrary. We make the match even more
+specific with the C<:Args(0)> action modifier -- this forces the match
+on I<only> C</login>, not C</login/somethingelse>.
 
 Next, update the corresponding method in
 C<lib/MyApp/Controller/Logout.pm> to match:
 
     =head2 index
-
+    
     Logout logic
-
+    
     =cut
-
+    
     sub index :Path :Args(0) {
         my ($self, $c) = @_;
-
+    
         # Clear the user's state
         $c->logout;
-
+    
         # Send the user to the starting point
         $c->response->redirect($c->uri_for('/'));
     }
 
-As with the login controller, be sure to delete the
-C<$c-E<gt>response-E<gt>body('Matched MyApp::Controller::Logout in Logout.');>
-line of the C<sub index>.
-
 
 =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="[% c.uri_for('/login') %]">
       <table>
@@ -471,17 +463,17 @@ 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's "chain" (all from application path to most specific class are run)
     # See the 'Actions' section of 'Catalyst::Manual::Intro' for more info.
     sub auto :Private {
         my ($self, $c) = @_;
-
+    
         # Allow unauthenticated users to reach the login page.  This
         # allows unauthenticated users to reach any action in the Login
         # controller.  To lock it down to a single action, we could use:
@@ -491,7 +483,7 @@ the following method:
         if ($c->controller eq $c->controller('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
@@ -501,7 +493,7 @@ the following method:
             # 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;
     }
@@ -509,11 +501,10 @@ the following method:
 As discussed in
 L<Catalyst::Manual::Tutorial::03_MoreCatalystBasics/CREATE A CATALYST CONTROLLER>,
 every C<auto> method from the application/root controller down to the
-most specific controller will be called.  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.
+most specific controller will be called.  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
@@ -564,18 +555,15 @@ C<test01> and password C<mypass>, and you should be taken to the Book
 List page.
 
 B<IMPORTANT NOTE:> If you are having issues with authentication on
-Internet Explorer, be sure to check the system clocks on both your
-server and client machines.  Internet Explorer is very picky about
-timestamps for cookies.  You can quickly sync a Debian system by
-installing the "ntpdate" package:
-
-    sudo aptitude -y install ntpdate
-
-And then run the following command:
+Internet Explorer (or potentially other browsers), be sure to check the
+system clocks on both your server and client machines.  Internet
+Explorer is very picky about timestamps for cookies.  You can use the
+C<ntpq -p> command on the Tutorial Virtual Machine to check time sync
+and/or use the following command to force a sync:
 
     sudo ntpdate-debian
 
-Or, depending on your firewall configuration:
+Or, depending on your firewall configuration, try it with "-u":
 
     sudo ntpdate-debian -u
 
@@ -595,8 +583,8 @@ bottom (below the closing </table> tag):
 
 Reload your browser and you should now see a "Login" and "Create" links
 at the bottom of the page (as mentioned earlier, you can update template
-files without a development server reload).  Click the first link
-to return to the login page.  This time you I<should> see the "You are
+files without a development server reload).  Click the first 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.
@@ -607,36 +595,37 @@ 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 that include a
-random "salt" value to make them extremely difficult to crack with
+from cleartext passwords to SHA-1 password hashes that include a random
+"salt" value to make them extremely difficult to crack, even with
 dictionary and "rainbow table" attacks.
 
 B<Note:> This section is optional.  You can skip it and the rest of the
 tutorial will function normally.
 
-Be aware 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 salted SHA-1 hash. If you are concerned about cleartext passwords
-between the browser and your application, consider using SSL/TLS, made
-easy with the Catalyst plugin Catalyst::Plugin:RequireSSL.
+Be aware 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 salted SHA-1 hash. If you are concerned about
+cleartext passwords between the browser and your application, consider
+using SSL/TLS, made easy with modules such as
+L<Catalyst::Plugin:RequireSSL> and L<Catalyst::ActionRole::RequireSSL>.
 
 
 =head2 Re-Run the DBIC::Schema Model Helper to Include DBIx::Class::PassphraseColumn
 
-Next, we can re-run the model helper to have it include
-L<DBIx::Class::PassphraseColumn> in all of the Result Classes it generates for
-us.  Simply use the same command we saw in Chapters 3 and 4, but add
-C<,PassphraseColumn> to the C<components> argument:
+Let's re-run the model helper to have it include
+L<DBIx::Class::PassphraseColumn> in all of the Result Classes it
+generates for us.  Simply use the same command we saw in Chapters 3 and
+4, but add C<,PassphraseColumn> to the C<components> argument:
 
     $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \
         create=static components=TimeStamp,PassphraseColumn dbi:SQLite:myapp.db \
         on_connect_do="PRAGMA foreign_keys = ON"
 
 If you then open one of the Result Classes, you will see that it
-includes PassphraseColumn in the C<load_components> line.  Take a look at
-C<lib/MyApp/Schema/Result/User.pm> since that's the main class where we
-want to use hashed and salted passwords:
+includes PassphraseColumn in the C<load_components> line.  Take a look
+at C<lib/MyApp/Schema/Result/User.pm> since that's the main class where
+we want to use hashed and salted passwords:
 
     __PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
 
@@ -661,34 +650,37 @@ the closing "1;":
         },
     );
 
-This redefines the automatically generated definition for the password fields at
-the top of the Result Class file to now use PassphraseColumn logic, storing
-passwords in RFC 2307 format (C<passphrase> is set to C<rfc2307>).
-C<passphrase_class> can be set to the name of any C<Authen::Passphrase::*>
-class, such as C<SaltedDigest> to use L<Authen::Passphrase::SaltedDigest>, or
-C<BlowfishCrypt> to use L<Authen::Passphrase::BlowfishCrypt>.
-C<passphrase_args> is then used to customize the passphrase class you
-selected. Here we specified the digest algorithm to use as C<SHA-1> and the size
-of the salt to use, but we could have also specified any other option the
-selected passphrase class supports.
+This redefines the automatically generated definition for the password
+fields at the top of the Result Class file to now use PassphraseColumn
+logic, storing passwords in RFC 2307 format (C<passphrase> is set to
+C<rfc2307>).  C<passphrase_class> can be set to the name of any
+C<Authen::Passphrase::*> class, such as C<SaltedDigest> to use
+L<Authen::Passphrase::SaltedDigest>, or C<BlowfishCrypt> to use
+L<Authen::Passphrase::BlowfishCrypt>.  C<passphrase_args> is then used
+to customize the passphrase class you selected. Here we specified the
+digest algorithm to use as C<SHA-1> and the size of the salt to use, but
+we could have also specified any other option the selected passphrase
+class supports.
+
 
 =head2 Load Hashed Passwords in the Database
 
-Next, let's create a quick script to load some hashed and salted passwords
-into the C<password> column of our C<users> table.  Open the file
-C<set_hashed_passwords.pl> in your editor and enter the following text:
+Next, let's create a quick script to load some hashed and salted
+passwords into the C<password> column of our C<users> table.  Open the
+file C<set_hashed_passwords.pl> in your editor and enter the following
+text:
 
     #!/usr/bin/perl
-
+    
     use strict;
     use warnings;
-
+    
     use MyApp::Schema;
-
+    
     my $schema = MyApp::Schema->connect('dbi:SQLite:myapp.db');
-
+    
     my @users = $schema->resultset('User')->all;
-
+    
     foreach my $user (@users) {
         $user->password('mypass');
         $user->update;
@@ -703,7 +695,7 @@ Then run the following command:
 
     $ DBIC_TRACE=1 perl -Ilib set_hashed_passwords.pl
 
-We had to use the C<-Ilib> argument to tell perl to look under the
+We had to use the C<-Ilib> argument to tell Perl to look under the
 C<lib> directory for our C<MyApp::Schema> model.
 
 The DBIC_TRACE output should show that the update worked:
@@ -725,18 +717,18 @@ But we can further confirm our actions by dumping the users table:
     2|test02|{SSHA}FpGhpCJus+Ea9ne4ww8404HH+hJKW/fW+bAv1v6FuRUy2G7I2aoTRQ==|t02@na.com|Jane|Doe|1
     3|test03|{SSHA}ZyGlpiHls8qFBSbHr3r5t/iqcZE602XLMbkSVRRNl6rF8imv1abQVg==|t03@na.com|No|Go|0
 
-As you can see, the passwords are much harder to steal from the
-database (not only are the hashes stored, but every hash is different
-even though the passwords are the same because of the added "salt"
-value).  Also note that this demonstrates how to use a DBIx::Class
-model outside of your web application -- a very useful feature in many
-situations.
+As you can see, the passwords are much harder to steal from the database
+(not only are the hashes stored, but every hash is different even though
+the passwords are the same because of the added "salt" value).  Also
+note that this demonstrates how to use a DBIx::Class model outside of
+your web application -- a very useful feature in many situations.
 
 
 =head2 Enable Hashed and Salted Passwords
 
-Edit C<lib/MyApp.pm> and update it to match the following text (the
-only change is to the C<password_type> field):
+Edit C<lib/MyApp.pm> and update the config() section for
+C<Plugin::Authentication> it to match the following text (the only
+change is to the C<password_type> field):
 
     # Configure SimpleDB Authentication
     __PACKAGE__->config(
@@ -767,34 +759,34 @@ at L<http://localhost:3000/logout>).
 
 As discussed in the previous chapter of the tutorial, C<flash> allows
 you to set variables in a way that is very similar to C<stash>, but it
-will remain set across multiple requests.  Once the value is read, it
-is cleared (unless reset).  Although C<flash> has nothing to do with
+will remain set across multiple requests.  Once the value is read, it is
+cleared (unless reset).  Although C<flash> has nothing to do with
 authentication, it does leverage the same session plugins.  Now that
 those plugins are enabled, let's go back and update the "delete and
-redirect with query parameters" code seen at the end of the L<Basic
-CRUD|Catalyst::Manual::Tutorial::04_BasicCRUD> chapter of the tutorial to
-take advantage of C<flash>.
+redirect with query parameters" code seen at the end of the
+L<Basic CRUD|Catalyst::Manual::Tutorial::04_BasicCRUD> chapter of the
+tutorial to take advantage of C<flash>.
 
-First, open C<lib/MyApp/Controller/Books.pm> and modify C<sub delete>
-to match the following (everything after the model search line of code
-has changed):
+First, open C<lib/MyApp/Controller/Books.pm> and modify C<sub delete> to
+match the following (everything after the model search line of code has
+changed):
 
     =head2 delete
-
+    
     Delete a book
-
+    
     =cut
-
+    
     sub delete :Chained('object') :PathPart('delete') :Args(0) {
         my ($self, $c) = @_;
-
+    
         # Use the book object saved by 'object' and delete it along
         # with related 'book_authors' entries
         $c->stash->{object}->delete;
-
+    
         # Use 'flash' to save information across requests until it's read
         $c->flash->{status_msg} = "Book deleted";
-
+    
         # Redirect the user back to the list page
         $c->response->redirect($c->uri_for($self->action_for('list')));
     }
@@ -812,10 +804,10 @@ flash vs. the C<status_msg> query parameter:
     </div><!-- end content -->
     ...
 
-Although the sample above only shows the C<content> div, leave the
-rest of the file intact -- the only change we made to replace
-"|| c.request.params.status_msg" with "c.flash.status_msg" in the
-C<< <span class="message"> >> line.
+Although the sample above only shows the C<content> div, leave the rest
+of the file intact -- the only change we made to replace "||
+c.request.params.status_msg" with "c.flash.status_msg" in the
+C<E<lt>span class="message"E<gt>> line.
 
 
 =head2 Try Out Flash
@@ -828,60 +820,152 @@ several books.  Click the "Return to list" link and delete one of the
 
 B<NOTE:> While C<flash> will save information across multiple requests,
 I<it does get cleared the first time it is read>.  In general, this is
-exactly what you want -- the C<flash> message will get displayed on
-the next screen where it's appropriate, but it won't "keep showing up"
-after that first time (unless you reset it).  Please refer to
-L<Catalyst::Plugin::Session|Catalyst::Plugin::Session> for additional
+exactly what you want -- the C<flash> message will get displayed on the
+next screen where it's appropriate, but it won't "keep showing up" after
+that first time (unless you reset it).  Please refer to
+L<Catalyst::Plugin::Session> for additional information.
+
+B<Note:> There is also a C<flash-to-stash> feature that will
+automatically load the contents the contents of flash into stash,
+allowing us to use the more typical C<c.flash.status_msg> in our TT
+template in lieu of the more verbose C<status_msg || c.flash.status_msg>
+we used above.  Consult L<Catalyst::Plugin::Session> for additional
 information.
 
 
-=head2 Switch To Flash-To-Stash
+=head2 Switch To Catalyst::Plugin::StatusMessages 
+
+Although the query parameter technique we used in
+L<Chapter 4|Catalyst::Manual::Tutorial::04_BasicCRUD> and the C<flash>
+approach we used above will work in most cases, they both have their
+drawbacks.  The query parameters can leave the status message on the
+screen longer than it should (for example, if the user hits refresh).
+And C<flash> can display the wrong message on the wrong screen (flash
+just shows the message on the next page for that user... if the user
+has multiple windows or tabs open, then the wrong one can get the
+status message).
+
+L<Catalyst::Plugin::StatusMessage> is designed to address these
+shortcomings.  It stores the messages in the user's session (so they are
+available across multiple requests), but ties each status message to a
+random token.  By passing this token across the redirect, we are no
+longer relying on a potentially ambiguous "next request" like we do with
+flash.  And, because the message is deleted the first time it's
+displayed, the user can hit refresh and still only see the message a
+single time (even though the URL may continue to reference the token,
+it's only displayed the first time).  The use of C<StatusMessage>
+or a similar mechanism is recommended for all Catalyst applications.
+
+To enable C<StatusMessage>, first edit C<lib/MyApp.pm> and add
+C<StatusMessage> to the list of plugins:
 
-Although the a use of flash above works well, the
-C<status_msg || c.flash.status_msg> statement is a little ugly. A nice
-alternative is to use the C<flash_to_stash> feature that automatically
-copies the content of flash to stash.  This makes your controller
-and template code work regardless of where it was directly access, a
-forward, or a redirect.  To enable C<flash_to_stash>, you can either
-set the value in C<lib/MyApp.pm> by changing the default
-C<__PACKAGE__-E<gt>config> setting to something like:
+    use Catalyst qw/
+        -Debug
+        ConfigLoader
+        Static::Simple
+    
+        StackTrace
+    
+        Authentication
+    
+        Session
+        Session::Store::File
+        Session::State::Cookie
+    
+        StatusMessage
+    /;
 
-    __PACKAGE__->config(
-            name    => 'MyApp',
-            # Disable deprecated behavior needed by old applications
-            disable_component_resolution_regex_fallback => 1,
-            'Plugin::Session' => { flash_to_stash => 1 },
-        );
+Then edit C<lib/MyApp/Controller/Books.pm> and modify the C<delete>
+action to match the following:
 
-B<or> add the following to C<myapp.conf>:
+    sub delete :Chained('object') :PathPart('delete') :Args(0) {
+        my ($self, $c) = @_;
+    
+        # Saved the PK id for status_msg below
+        my $id = $c->stash->{object}->id;
+    
+        # Use the book object saved by 'object' and delete it along
+        # with related 'book_authors' entries
+        $c->stash->{object}->delete;
+    
+        # Redirect the user back to the list page
+        $c->response->redirect($c->uri_for($self->action_for('list'),
+            {mid => $c->set_status_msg("Deleted book $id")}));
+    }
 
-    <Plugin::Session>
-        flash_to_stash   1
-    </Plugin::Session>
+This uses the C<set_status_msg> that the plugin added to C<$c> to save
+the message under a random token.  (If we wanted to save an error
+message, we could have used C<set_error_msg>.)  Because
+C<set_status_msg> and C<set_error_msg> both return the random token, we
+can assign that value to the "C<mid>" query parameter via C<uri_for> as
+shown above.
+
+Next, we need to make sure that the list page will load display the
+message.  The easiest way to do this is to take advantage of the chained
+dispatch we implemented in
+L<Chapter 4|Catalyst::Manual::Tutorial::04_BasicCRUD>.  Edit
+C<lib/MyApp/Controller/Books.pm> again and update the C<base> action to
+match:
+
+    sub base :Chained('/') :PathPart('books') :CaptureArgs(0) {
+        my ($self, $c) = @_;
+    
+        # Store the ResultSet in stash so it's available for other methods
+        $c->stash(resultset => $c->model('DB::Book'));
+    
+        # Print a message to the debug log
+        $c->log->debug('*** INSIDE BASE METHOD ***');
+    
+        # Load status messages
+        $c->load_status_msgs;
+    }
 
-The C<__PACKAGE__-E<gt>config> option is probably preferable here
-since it's not something you will want to change at runtime without it
-possibly breaking some of your code.
+That way, anything that chains off C<base> will automatically get any
+status or error messages loaded into the stash.  Let's convert the
+C<list> action to take advantage of this.  Modify the method signature
+for C<list> from:
 
-Then edit C<root/src/wrapper.tt2> and change the C<status_msg> line
+    sub list :Local {
+
+to:
+
+    sub list :Chained('base') :PathParth('list') :Args(0) {
+
+Finally, let's clean up the status/error message code in our wrapper
+template.  Edit C<root/src/wrapper.tt2> and change the "content" div
 to match the following:
 
-    <span class="message">[% status_msg %]</span>
+    <div id="content">
+        [%# Status and error messages %]
+        <span class="message">[% status_msg %]</span>
+        <span class="error">[% error_msg %]</span>
+        [%# This is where TT will stick all of your template's contents. -%]
+        [% content %]
+    </div><!-- end content -->
 
 Now go to L<http://localhost:3000/books/list> in your browser. Delete
-another of the "Test" books you added in the previous step. Flash should
-still maintain the status message across the redirect even though you
-are no longer explicitly accessing C<c.flash>.
+another of the "Test" books you added in the previous step.  You should
+get redirection from the C<delete> action back to the C<list> action,
+but with a "mid=########" message ID query parameter.  The screen should
+say "Deleted book #" (where # is the PK id of the book you removed).
+However, if you hit refresh in your browser, the status message is no
+longer displayed  (even though the URL does still contain the message ID
+token, it is ignored -- thereby keeping the state of our status/error
+messages in sync with the users actions).
+
+
+You can jump to the next chapter of the tutorial here:
+L<Authorization|Catalyst::Manual::Tutorial::06_Authorization>
 
 
 =head1 AUTHOR
 
 Kennedy Clark, C<hkclark@gmail.com>
 
-Please report any errors, issues or suggestions to the author.  The
-most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
+Feel free to contact the author for any errors or suggestions, but the
+best way to report issues is via the CPAN RT Bug system at
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
 
-Copyright 2006-2010, Kennedy Clark, under the
+Copyright 2006-2011, Kennedy Clark, under the
 Creative Commons Attribution Share-Alike License Version 3.0
 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).
index cb4495c..930163c 100644 (file)
@@ -56,21 +56,23 @@ L<Appendices|Catalyst::Manual::Tutorial::10_Appendices>
 
 =head1 DESCRIPTION
 
-This chapter of the tutorial adds role-based authorization to the 
-existing authentication implemented in Chapter 5.  It provides simple 
-examples of how to use roles in both TT templates and controller 
-actions.  The first half looks at basic authorization concepts. The 
-second half looks at how moving your authorization code to your model 
+This chapter of the tutorial adds role-based authorization to the
+existing authentication implemented in
+L<Chapter 5|Catalyst::Manual::Tutorial::05_Authentication>.  It provides
+simple examples of how to use roles in both TT templates and controller
+actions.  The first half looks at basic authorization concepts. The
+second half looks at how moving your authorization code to your model
 can simplify your code and make things easier to maintain.
 
-You can checkout the source code for this example from the catalyst
-subversion repository as per the instructions in
-L<Catalyst::Manual::Tutorial::01_Intro|Catalyst::Manual::Tutorial::01_Intro>.
+Source code for the tutorial in included in the F</root/Final> directory
+of the Tutorial Virtual machine (one subdirectory per chapter).  There
+are also instructions for downloading the code in
+L<Catalyst::Manual::Tutorial::01_Intro>.
 
 
 =head1 BASIC AUTHORIZATION
 
-In this section you learn the basics of how authorization works under 
+In this section you learn the basics of how authorization works under
 Catalyst.
 
 
@@ -83,19 +85,21 @@ Edit C<lib/MyApp.pm> and add C<Authorization::Roles> to the list:
         -Debug
         ConfigLoader
         Static::Simple
-        
+    
         StackTrace
-        
+    
         Authentication
         Authorization::Roles
-        
+    
         Session
         Session::Store::File
         Session::State::Cookie
+
+        StatusMessage
     /;
 
-Once again, include this additional plugin as a new dependency in 
-the Makefile.PL file like this:
+Once again, include this additional plugin as a new dependency in the
+Makefile.PL file like this:
 
     requires 'Catalyst::Plugin::Authorization::Roles';
 
@@ -129,7 +133,7 @@ lines to the bottom of the file:
     </p>
 
 This code displays a different combination of links depending on the
-roles assigned to the user.  
+roles assigned to the user.
 
 
 =head2 Limit Books::add to 'admin' Users
@@ -189,12 +193,13 @@ 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
-Chapter 3 or C<detach> to an action that shows an "unauthorized" page).
+(for example, using L<Catalyst::Plugin::StateMessage> as shown in the
+L<last chapter|Catalyst::Manual::Tutorial::05_Authentication> to
+redirect to an "unauthorized" page).
 
 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 
+Pod comment.  For example, put something like C<=begin> before
 C<sub add : Local {> and C<=end> after the closing C<}>.
 
 
@@ -204,13 +209,13 @@ Make sure the development server is running:
 
     $ script/myapp_server.pl -r
 
-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> or 
-C<Ctrl+Reload> your browser and/or click the "User Logout" link on the book 
-list page).  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 "User Logout"
-link on the book list page.
+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> or
+C<Ctrl+Reload> your browser and/or click the "User Logout" link on the
+book list page).  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 "User
+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
@@ -218,24 +223,24 @@ 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 your browser directly) when you are 
+while logged in as each user.  Use one of the "logout" links (or go to
+L<http://localhost:3000/logout> in your browser directly) when you are
 done.
 
 
 =head1 ENABLE MODEL-BASED AUTHORIZATION
 
-Hopefully it's fairly obvious that adding detailed permission checking 
-logic to our controllers and view templates isn't a very clean or 
-scalable way to build role-based permissions into out application.  As 
-with many other aspects of MVC web development, the goal is to have 
-your controllers and views be an "thin" as possible, with all of the 
-"fancy business logic" built into your model.
+Hopefully it's fairly obvious that adding detailed permission checking
+logic to our controllers and view templates isn't a very clean or
+scalable way to build role-based permissions into out application.  As
+with many other aspects of MVC web development, the goal is to have your
+controllers and views be an "thin" as possible, with all of the "fancy
+business logic" built into your model.
 
-For example, let's add a method to our C<Books.pm> Result Class to 
-check if a user is allowed to delete a book.  Open 
-C<lib/MyApp/Schema/Result/Book.pm> and add the following method 
-(be sure to add it below the "C<DO NOT MODIFY ...>" line):
+For example, let's add a method to our C<Books.pm> Result Class to check
+if a user is allowed to delete a book.  Open
+C<lib/MyApp/Schema/Result/Book.pm> and add the following method (be sure
+to add it below the "C<DO NOT MODIFY ...>" line):
 
     =head2 delete_allowed_by
     
@@ -245,14 +250,14 @@ C<lib/MyApp/Schema/Result/Book.pm> and add the following method
     
     sub delete_allowed_by {
         my ($self, $user) = @_;
-        
+    
         # Only allow delete if user has 'admin' role
         return $user->has_role('admin');
     }
 
-Here we call a C<has_role> method on our user object, so we should add 
-this method to our Result Class.  Open 
-C<lib/MyApp/Schema/Result/User.pm> and add the following method below 
+Here we call a C<has_role> method on our user object, so we should add
+this method to our Result Class.  Open
+C<lib/MyApp/Schema/Result/User.pm> and add the following method below
 the "C<DO NOT MODIFY ...>" line:
 
     =head2 has_role
@@ -269,11 +274,18 @@ the "C<DO NOT MODIFY ...>" line:
         return any(map { $_->role } $self->roles) eq $role;
     }
 
-Let's also add Perl6::Junction to the requirements listed in 
+Let's also add C<Perl6::Junction> to the requirements listed in
 Makefile.PL:
 
     requires 'Perl6::Junction';
 
+B<Note:> Feel free to use C<grep> in lieu of C<Perl6::Junction::any> if
+you prefer.  Also, please don't let the use of the C<Perl6::Junction>
+module above lead you to believe that Catalyst is somehow dependent on
+Perl 6... we are simply using that module for its
+L<easy-to-read|http://blogs.perl.org/users/marc_sebastian_jakobs/2009/11/my-favorite-module-of-the-month-perl6junction.html>
+C<any> function.
+
 Now we need to add some enforcement inside our controller.  Open
 C<lib/MyApp/Controller/Books.pm> and update the C<delete> method to
 match the following code:
@@ -295,16 +307,14 @@ match the following code:
         # with related 'book_authors' entries
         $c->stash->{object}->delete;
     
-        # Use 'flash' to save information across requests until it's read
-        $c->flash->{status_msg} = "Book deleted";
-    
         # Redirect the user back to the list page
-        $c->response->redirect($c->uri_for($self->action_for('list')));
+        $c->response->redirect($c->uri_for($self->action_for('list'),
+            {mid => $c->set_status_msg("Deleted book $id")}));
     }
 
-Here, we C<detach> to an error page if the user is lacking the 
-appropriate permissions.  For this to work, we need to make 
-arrangements for the '/error_noperms' action to work.  Open 
+Here, we C<detach> to an error page if the user is lacking the
+appropriate permissions.  For this to work, we need to make arrangements
+for the '/error_noperms' action to work.  Open
 C<lib/MyApp/Controller/Root.pm> and add this method:
 
     =head2 error_noperms
@@ -312,7 +322,7 @@ C<lib/MyApp/Controller/Root.pm> and add this method:
     Permissions error screen
     
     =cut
-        
+    
     sub error_noperms :Chained('/') :PathPart('error_noperms') :Args(0) {
         my ($self, $c) = @_;
     
@@ -329,25 +339,29 @@ feature:
 
     http://localhost:3000/books/url_create/Test/1/4
 
-Then, while still logged in as C<test01>, click the "Delete" link next 
-to one of these books.  The book should be removed and you should see 
-the usual green "Book deleted" message.  Next, click the "User Logout" 
-link and log back in as C<test02>.  Now try deleting one of the books. 
-You should be taken to the red "Permission Denied" message on our 
-error page.
+Then, while still logged in as C<test01>, click the "Delete" link next
+to one of these books.  The book should be removed and you should see
+the usual green "Book deleted" message.  Next, click the "User Logout"
+link and log back in as C<test02>.  Now try deleting one of the books.
+You should be taken to the red "Permission Denied" message on our error
+page.
 
 Use one of the 'Logout' links (or go to the
 L<http://localhost:3000/logout> URL directly) when you are done.
 
 
+You can jump to the next chapter of the tutorial here:
+L<Debugging|Catalyst::Manual::Tutorial::07_Debugging>
+
+
 =head1 AUTHOR
 
 Kennedy Clark, C<hkclark@gmail.com>
 
-Please report any errors, issues or suggestions to the author.  The
-most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
+Feel free to contact the author for any errors or suggestions, but the
+best way to report issues is via the CPAN RT Bug system at
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
 
-Copyright 2006-2010, Kennedy Clark, under the
+Copyright 2006-2011, Kennedy Clark, under the
 Creative Commons Attribution Share-Alike License Version 3.0
 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).
index 5ba238f..25b56ab 100644 (file)
@@ -56,7 +56,7 @@ L<Appendices|Catalyst::Manual::Tutorial::10_Appendices>
 
 =head1 DESCRIPTION
 
-This chapter of the tutorial takes a brief look at the primary options 
+This chapter 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
@@ -79,12 +79,12 @@ 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. 
-(See L<Catalyst::Log|Catalyst::Log> for more detail.) For example, if 
-you add the following code to a controller action method:
+Folks in the former group can use Catalyst's C<$c-E<gt>log> facility.
+(See L<Catalyst::Log> for more detail.) For example, if you add the
+following code to a controller action method:
 
     $c->log->info("Starting the foreach loop here");
-
+    
     $c->log->debug("Value of \$id is: ".$id);
 
 Then the Catalyst development server will display your message along
@@ -93,8 +93,8 @@ template view use:
 
     [% c.log.debug("This is a test log message") %]
 
-As with many other logging facilities, a method is defined for
-each of the following "logging levels" (in increasing order of
+As with many other logging facilities, a method is defined for each of
+the following "logging levels" (in increasing order of
 severity/importance):
 
     $c->log->debug
@@ -103,10 +103,16 @@ severity/importance):
     $c->log->error
     $c->log->fatal
 
-You can also use L<Data::Dumper|Data::Dumper> in both Catalyst code 
-(C<use Data::Dumper; $c-E<gt>log-E<gt>debug("\$var is: ".Dumper($var));)>) 
+You can also use L<Data::Dumper> in both Catalyst code
+(C<use Data::Dumper; $c-E<gt>log-E<gt>debug("\$var is: ".Dumper($var));)>)
 and TT templates (C<[% Dumper.dump(book) %]>.
 
+B<NOTE:> Whether you are a logging fanatic or not, we strongly recommend
+that you take advantage of L<Log::Log4perl> or L<Log::Dispatch>.  It's
+easy to use L<Catalyst::Log> with either of these and they will provide
+a huge amount of extra functionality that you will want in virtually
+every production application you run or support.
+
 
 =head1 RUNNING CATALYST UNDER THE PERL DEBUGGER
 
@@ -129,14 +135,15 @@ you can obviously indent them if you prefer):
         # 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('DB::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).
+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).
 
 B<NOTE:> The C<DB> here is the Perl Debugger, not the DB model.
 
@@ -144,8 +151,8 @@ If you haven't done it already, enable SQL logging as before:
 
     $ export DBIC_TRACE=1
 
-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>:
+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
 
@@ -172,7 +179,7 @@ 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:48):
+    MyApp::Controller::Books::list(/root/MyApp/script/../lib/MyApp/Controller/Books.pm:48):
     48:         $c->stash->{books} = [$c->model('DB::Book')->all];
     
       DB<1>
@@ -184,14 +191,14 @@ C<single-step> into methods/subroutines):
 
       DB<1> n
     SELECT me.id, me.title, me.rating, me.created, me.updated FROM book me:
-    MyApp::Controller::Books::list(/home/me/MyApp/script/../lib/MyApp/Controller/Books.pm:53):
+    MyApp::Controller::Books::list(/root/MyApp/script/../lib/MyApp/Controller/Books.pm:53):
     53:         $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<DBIC_TRACE=1> earlier, SQL debug 
-output also shows up in the development server debug information.
+Notice that because we enabled C<DBIC_TRACE=1> earlier, SQL debug output
+also shows up in the development server debug information.
 
 Next, list the methods available on our C<Book> model:
 
@@ -241,15 +248,15 @@ breakpoint is hit (or the application exits):
 
 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.  
+debugger prompt.
 
     ^CCatalyst::Engine::HTTP::run(/usr/local/share/perl/5.10.0/Catalyst/Engine/HTTP.pm:260):
     260:            while ( accept( Remote, $daemon ) ) {
 
     DB<4>
 
-Finally, press C<q> to exit the debugger and return to your OS
-shell prompt:
+Finally, press C<q> to exit the debugger and return to your OS shell
+prompt:
 
       DB<4> q
     $
@@ -262,39 +269,37 @@ out C<perldebguts>.
 You can also type C<h> or C<h h> at the debugger prompt to view the
 built-in help screens.
 
-For an excellent book covering all aspects of the Perl debugger, we highly
-recommend reading 'Pro Perl Debugging' by Richard Foley.
+For an excellent book covering all aspects of the Perl debugger, we
+highly recommend reading 'Pro Perl Debugging' by Richard Foley.
 
-Oh yeah, before you forget, be sure to remove the C<DB::single=1> line you
-added above in C<lib/MyApp/Controller/Books.pm>.
+Oh yeah, before you forget, be sure to remove the C<DB::single=1> line
+you added above in C<lib/MyApp/Controller/Books.pm>.
 
 =head1 DEBUGGING MODULES FROM CPAN
 
-Although the techniques discussed above work well for code you are 
-writing, what if you want to use print/log/warn messages or set 
-breakpoints in code that you have installed from CPAN (or in module that 
-ship with Perl)?  One helpful approach is to place a copy of the module 
-inside the C<lib> directory of your Catalyst project.  When Catalyst 
-loads, it will load from inside your C<lib> directory first, only 
-turning to the global modules if a local copy cannot be found.  You can 
-then make modifications such as adding a C<$DB::single=1> to the local 
-copy of the module without risking the copy in the original location. 
-This can also be a great way to "locally override" bugs in modules while 
+Although the techniques discussed above work well for code you are
+writing, what if you want to use print/log/warn messages or set
+breakpoints in code that you have installed from CPAN (or in module that
+ship with Perl)?  One helpful approach is to place a copy of the module
+inside the C<lib> directory of your Catalyst project.  When Catalyst
+loads, it will load from inside your C<lib> directory first, only
+turning to the global modules if a local copy cannot be found.  You can
+then make modifications such as adding a C<$DB::single=1> to the local
+copy of the module without risking the copy in the original location.
+This can also be a great way to "locally override" bugs in modules while
 you wait for a fix on CPAN.
 
-
-Matt Trout has suggested the following shortcut to create a local
-copy of an installed module:
+Matt Trout has suggested the following shortcut to create a local copy
+of an installed module:
 
     mkdir -p lib/Module; cp `perldoc -l Module::Name` lib/Module/
 
-Note: If you are following along in Debian 5 or Ubuntu, you will
-need to install the C<perl-doc> package to use the C<perldoc> command.  
-Use C<sudo aptitude install perl-doc> to do that.
+Note: If you are following along in Debian 6 or Ubuntu, you will need to
+install the C<perl-doc> package to use the C<perldoc> command.  Use
+C<sudo aptitude install perl-doc> to do that.
 
-For example, you could make a copy of 
-L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication>
-with the following command:
+For example, you could make a copy of
+L<Catalyst::Plugin::Authentication> with the following command:
 
     mkdir -p lib/Catalyst/Plugin; cp \
         `perldoc -l Catalyst::Plugin::Authentication` lib/Catalyst/Plugin
@@ -302,8 +307,7 @@ with the following command:
 You can then use the local copy inside your project to place logging
 messages and/or breakpoints for further study of that module.
 
-B<Note:> Matt has also suggested the following tips for Perl 
-debugging:
+B<Note:> Matt has also suggested the following tips for Perl debugging:
 
 =over 4
 
@@ -345,9 +349,9 @@ Otherwise, it returns undef and nothing will be printed.
 
 =head1 TT DEBUGGING
 
-If you run into issues during the rendering of your template, it might 
-be helpful to enable TT C<DEBUG> options.  You can do this in a Catalyst 
-environment by adding a C<DEBUG> line to the C<__PACKAGE__->config> 
+If you run into issues during the rendering of your template, it might
+be helpful to enable TT C<DEBUG> options.  You can do this in a Catalyst
+environment by adding a C<DEBUG> line to the C<__PACKAGE__->config>
 declaration in C<lib/MyApp/View/HTML.pm>:
 
     __PACKAGE__->config({
@@ -355,27 +359,32 @@ declaration in C<lib/MyApp/View/HTML.pm>:
         DEBUG              => 'undef',
     });
 
-There are a variety of options you can use, such as 'undef', 'all', 
-'service', 'context', 'parser' and 'provider'.  See 
-L<Template::Constants|Template::Constants> for more information 
-(remove the C<DEBUG_> portion of the name shown in the TT docs and 
-convert to lower case for use inside Catalyst).
+There are a variety of options you can use, such as 'undef', 'all',
+'service', 'context', 'parser' and 'provider'.  See
+L<Template::Constants> for more information (remove the C<DEBUG_>
+portion of the name shown in the TT docs and convert to lower case for
+use inside Catalyst).
 
-B<NOTE:> B<Please be sure to disable TT debug options before continuing 
-with the tutorial> (especially the 'undef' option -- leaving this 
-enabled will conflict with several of the conventions used by this 
+B<NOTE:> B<Please be sure to disable TT debug options before continuing
+with the tutorial> (especially the 'undef' option -- leaving this
+enabled will conflict with several of the conventions used by this
 tutorial to leave some variables undefined on purpose).
 
 Happy debugging.
 
+
+You can jump to the next chapter of the tutorial here:
+L<Testing|Catalyst::Manual::Tutorial::08_Testing>
+
+
 =head1 AUTHOR
 
 Kennedy Clark, C<hkclark@gmail.com>
 
-Please report any errors, issues or suggestions to the author.  The
-most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
+Feel free to contact the author for any errors or suggestions, but the
+best way to report issues is via the CPAN RT Bug system at
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
 
-Copyright 2006-2010, Kennedy Clark, under the
+Copyright 2006-2011, Kennedy Clark, under the
 Creative Commons Attribution Share-Alike License Version 3.0
 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).
index 07ea4a0..b90b0e3 100644 (file)
@@ -56,41 +56,42 @@ L<Appendices|Catalyst::Manual::Tutorial::10_Appendices>
 
 =head1 DESCRIPTION
 
-You may have noticed that the Catalyst Helper scripts automatically 
-create basic C<.t> test scripts under the C<t> directory.  This 
-chapter of the tutorial briefly looks at how these tests can be used 
-not only to 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.
-
-You can check out the source code for this example from the Catalyst
-Subversion repository as per the instructions in
-L<Catalyst::Manual::Tutorial::01_Intro|Catalyst::Manual::Tutorial::01_Intro>.
+You may have noticed that the Catalyst Helper scripts automatically
+create basic C<.t> test scripts under the C<t> directory.  This chapter
+of the tutorial briefly looks at how these tests can be used not only to
+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.
+
+Source code for the tutorial in included in the F</root/Final> directory
+of the Tutorial Virtual machine (one subdirectory per chapter).  There
+are also instructions for downloading the code in
+L<Catalyst::Manual::Tutorial::01_Intro>.
 
 For an excellent introduction to learning the many benefits of testing
-your Perl applications and modules, you might want to read 'Perl Testing: 
-A Developer's Notebook' by Ian Langworth and chromatic.
+your Perl applications and modules, you might want to read 'Perl
+Testing: A Developer's Notebook' by Ian Langworth and chromatic.
 
 
 =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>
+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 -wl t
 
-There will be a lot of output because we have the C<-Debug> flag 
-enabled in C<lib/MyApp.pm> (see the C<CATALYST_DEBUG=0> tip below for 
-a quick and easy way to reduce the clutter).  Look for lines like this 
-for errors:
+There will be a lot of output because we have the C<-Debug> flag enabled
+in C<lib/MyApp.pm> (see the C<CATALYST_DEBUG=0> tip below for a quick
+and easy way to reduce the clutter).  Look for lines like this for
+errors:
 
     #   Failed test 'Request should succeed'
     #   at t/controller_Books.t line 8.
     # Looks like you failed 1 test of 3.
 
-The redirection used by the Authentication plugins will cause several 
+The redirection used by the Authentication plugins will cause several
 failures in the default tests.  You can fix this by making the following
 changes:
 
@@ -118,17 +119,17 @@ to:
 
     ok( request('/books')->is_redirect, 'Request should succeed' );
 
-4) Add the following statement to the top of C<t/view_TT.t>:
+4) Add the following statement to the top of C<t/view_HTML.t>:
 
     use MyApp;
 
-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:
+As you can see in the C<prove> command line above, the C<-l> option (or
+C<--lib> if you prefer) 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 -wl t
 
@@ -146,7 +147,7 @@ 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 -vwl t
+    $ CATALYST_DEBUG=0 prove -vwl t
 
 
 =head1 RUNNING A SINGLE TEST
@@ -156,8 +157,8 @@ command. For example:
 
     $ CATALYST_DEBUG=0 prove -wl t/01app.t
 
-Also note that you can also run tests directly from Perl without C<prove>.
-For example:
+Also note that you can also run tests directly from Perl without
+C<prove>.  For example:
 
     $ CATALYST_DEBUG=0 perl -w -Ilib t/01app.t
 
@@ -166,11 +167,10 @@ For example:
 
 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
+your own tests to exercise the various parts of your application.  The
+L<Test::WWW::Mechanize::Catalyst> module is very popular for writing
+these sorts of test cases.  This module extends L<Test::WWW::Mechanize>
+(and therefore L<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, and a real person to do the clicking.
@@ -178,7 +178,7 @@ use an actual web server, and a real person to do the clicking.
 To create a sample test case, open the C<t/live_app01.t> file in your
 editor and enter the following:
 
-    #!/usr/bin/perl
+    #!/usr/bin/env perl
     
     use strict;
     use warnings;
@@ -189,7 +189,7 @@ editor and enter the following:
     #   use Test::WWW::Mechanize::Catalyst "MyApp";
     
     BEGIN { 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;
@@ -228,8 +228,10 @@ editor and enter the following:
         "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'");
+    $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;
     
@@ -256,7 +258,8 @@ editor and enter the following:
     $ua1->content_contains("by 'Stevens'", "Check author added OK");
     $ua1->content_contains("with a rating of 2.", "Check rating added");
     # Try a regular expression to combine the previous 3 checks & account for whitespace
-    $ua1->content_like(qr/Added book 'TestTitle'\s+by 'Stevens'\s+with a rating of 2./, "Regex check");
+    $ua1->content_like(qr/Added book 'TestTitle'\s+by 'Stevens'\s+with a rating of 2./,
+        "Regex check");
     
     # Make sure the new book shows in the list
     $ua1->get_ok("http://localhost/books/list", "'test01' book list");
@@ -271,7 +274,7 @@ editor and enter the following:
     $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");
+    $ua1->content_like(qr/Deleted book \d+/, "Deleted book #");
     
     # User 'test02' should not be able to add a book
     $ua2->get_ok("http://localhost/books/url_create/TestTitle2/2/5", "'test02' add");
@@ -281,14 +284,13 @@ editor and enter the following:
 
 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.
+variety of other methods available in L<Test::WWW::Mechanize::Catalyst>
+(for example, regex-based matching). Consult
+L<Test::WWW::Mechanize::Catalyst>, L<Test::WWW::Mechanize>,
+L<WWW::Mechanize>, and L<Test::More> 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>.
+by L<Test::WWW::Mechanize::Catalyst>, see L<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
@@ -303,10 +305,10 @@ or
 
     $ DBIC_TRACE=0 CATALYST_DEBUG=0 prove -vwl t/live_app01.t
 
-Experiment with the C<DBIC_TRACE>, C<CATALYST_DEBUG> and C<-v> 
-settings.  If you find that there are errors, use the techniques 
-discussed in the "Catalyst Debugging" section (Chapter 7) to isolate 
-and fix any problems.
+Experiment with the C<DBIC_TRACE>, C<CATALYST_DEBUG> and C<-v> settings.
+If you find that there are errors, use the techniques discussed in the
+"Catalyst Debugging" section (Chapter 7) to isolate and fix any
+problems.
 
 If you want to run the test case under the Perl interactive debugger,
 try a command such as:
@@ -327,19 +329,19 @@ similar to the following:
 
 Unfortunately, this only shows us the first 50 characters of the HTML
 returned by the request -- not enough to determine where the problem
-lies.  A simple technique that can be used in such situations is to 
-temporarily insert a line similar to the following right after the 
+lies.  A simple technique that can be used in such situations is to
+temporarily insert a line similar to the following right after the
 failed test:
 
     diag $ua1->content;
 
 This will cause the full HTML returned by the request to be displayed.
 
-Another approach to see the full HTML content at the failure point in 
-a series of tests would be to insert a "C<$DB::single=1;> right above 
-the location of the failure and run the test under the perl debugger 
-(with C<-d>) as shown above.  Then you can use the debugger to explore 
-the state of the application right before or after the failure.
+Another approach to see the full HTML content at the failure point in a
+series of tests would be to insert a "C<$DB::single=1;> right above the
+location of the failure and run the test under the Perl debugger (with
+C<-d>) as shown above.  Then you can use the debugger to explore the
+state of the application right before or after the failure.
 
 
 =head1 SUPPORTING BOTH PRODUCTION AND TEST DATABASES
@@ -347,16 +349,16 @@ the state of the application right before or after the failure.
 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.
+L<Test::WWW::Mechanize::Catalyst> is that it runs your full application;
+however, this can complicate things when you want to support multiple
+databases.
 
 =head2 DATABASE CONFIG SWITCHING IN YOUR MODEL CLASS
 
-One solution is to allow the
-database specification to be overridden with an environment variable.
-For example, open C<lib/MyApp/Model/DB.pm> in your editor and
-change the C<__PACKAGE__-E<gt>config(...> declaration to resemble:
+One solution is to allow the database specification to be overridden
+with an environment variable.  For example, open
+C<lib/MyApp/Model/DB.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(
@@ -380,21 +382,25 @@ 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.
 
+
 =head2 DATABASE CONFIG SWITCHING USING MULTIPLE CONFIG FILES
 
-By utilizing L<Catalyst::Plugin::ConfigLoader>s functionality for loading
-multiple config files based on environment variables you can override your
-default (production) database connection settings.
+L<Catalyst::Plugin::ConfigLoader> has functionality to load loading
+multiple config files based on environment variablesi, allowing you to
+override your default (production) database connection settings during
+development (or vice versa).
 
-Setting C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> to 'testing' in your test script
-results in loading of an additional config file named myapp_testing.conf after
-myapp.conf which will override any parameters in myapp.conf.
+Setting C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> to 'testing' in your test
+script results in loading of an additional config file named
+C<myapp_testing.conf> after C<myapp.conf> which will override any
+parameters in C<myapp.conf>.
 
-You should set the environment variable in the BEGIN block of your test script
-to make sure it's set before your Catalyst application is started.
+You should set the environment variable in the BEGIN block of your test
+script to make sure it's set before your Catalyst application is
+started.
 
-The following is an example for a config and test script for a DBIx::Class
-model named MyDB and a controller named Foo:
+The following is an example for a config and test script for a
+DBIx::Class model named MyDB and a controller named Foo:
 
 myapp_testing.conf:
 
@@ -410,29 +416,33 @@ t/controller_Foo.t:
     use strict;
     use warnings;
     use Test::More;
-
+    
     BEGIN {
         $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX } = 'testing';
     }
-
+    
     eval "use Test::WWW::Mechanize::Catalyst 'MyApp'";
     plan $@
         ? ( skip_all => 'Test::WWW::Mechanize::Catalyst required' )
         : ( tests => 2 );
-
+    
     ok( my $mech = Test::WWW::Mechanize::Catalyst->new, 'Created mech object' );
-
+    
     $mech->get_ok( 'http://localhost/foo' );
 
 
+You can jump to the next chapter of the tutorial here:
+L<Advanced CRUD|Catalyst::Manual::Tutorial::09_AdvancedCRUD>
+
+
 =head1 AUTHOR
 
 Kennedy Clark, C<hkclark@gmail.com>
 
-Please report any errors, issues or suggestions to the author.  The
-most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
+Feel free to contact the author for any errors or suggestions, but the
+best way to report issues is via the CPAN RT Bug system at
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
 
-Copyright 2006-2010, Kennedy Clark, under the
+Copyright 2006-2011, Kennedy Clark, under the
 Creative Commons Attribution Share-Alike License Version 3.0
 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).
index fefa4fa..6c4c7f7 100644 (file)
@@ -57,16 +57,17 @@ L<Appendices|Catalyst::Manual::Tutorial::10_Appendices>
 =head1 DESCRIPTION
 
 This chapter of the tutorial explores more advanced functionality for
-Create, Read, Update, and Delete (CRUD) than we saw in Chapter 4.  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 to approach advanced CRUD operations in a Catalyst 
-environment.  Therefore, this section of the tutorial allows you to pick 
-from one of several modules that that cover different form management 
+Create, Read, Update, and Delete (CRUD) than we saw in
+L<Chapter 4|Catalyst::Manual::Tutorial::04_BasicCRUD>.  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 to approach advanced CRUD operations in a Catalyst
+environment.  Therefore, this section of the tutorial allows you to pick
+from one of several modules that that cover different form management
 tools.  Select one or more options from the list below.
 
 =head1 ADVANCED CRUD OPTIONS
@@ -87,7 +88,7 @@ L<FormBuilder|Catalyst::Manual::Tutorial::09_AdvancedCRUD::09_FormBuilder>
 
 =back
 
-B<NOTE:> Please contact the author if you would like to assist with 
+B<NOTE:> Please contact the author if you would like to assist with
 writing a new module.
 
 
@@ -95,10 +96,10 @@ writing a new module.
 
 Kennedy Clark, C<hkclark@gmail.com>
 
-Please report any errors, issues or suggestions to the author.  The
-most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
+Feel free to contact the author for any errors or suggestions, but the
+best way to report issues is via the CPAN RT Bug system at
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
 
-Copyright 2006-2010, Kennedy Clark, under the
+Copyright 2006-2011, Kennedy Clark, under the
 Creative Commons Attribution Share-Alike License Version 3.0
 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).
index 680feae..e7dfaa9 100644 (file)
@@ -56,28 +56,31 @@ L<Appendices|Catalyst::Manual::Tutorial::10_Appendices>
 
 =head1 DESCRIPTION
 
-This portion of the tutorial explores L<HTML::FormFu|HTML::FormFu> and 
-how it can be used to manage forms, perform validation of form input, 
-as well as save and restore data to/from the database.  This was written
-using HTML::FormFu version 0.05001.
+This portion of the tutorial explores L<HTML::FormFu> and how it can be
+used to manage forms, perform validation of form input, as well as save
+and restore data to/from the database.
 
-See 
-L<Catalyst::Manual::Tutorial::09_AdvancedCRUD|Catalyst::Manual::Tutorial::09_AdvancedCRUD>
-for additional form management options other than 
-L<HTML::FormFu|HTML::FormFu>.
+See L<Catalyst::Manual::Tutorial::09_AdvancedCRUD> for additional form
+management options other than L<HTML::FormFu>.
+
+Source code for the tutorial in included in the F</root/Final> directory
+of the Tutorial Virtual machine (one subdirectory per chapter).  There
+are also instructions for downloading the code in
+L<Catalyst::Manual::Tutorial::01_Intro>.
 
 
 =head1 HTML::FormFu FORM CREATION
 
-This section looks at how L<HTML::FormFu|HTML::FormFu> can be used to 
-add additional functionality to the manually created form from Chapter 4.
+This section looks at how L<HTML::FormFu> can be used to add additional
+functionality to the manually created form from
+L<Chapter 4|Catalyst::Manual::Tutorial::04_BasicCRUD>.
 
 
 =head2 Inherit From Catalyst::Controller::HTML::FormFu
 
 First, change your C<lib/MyApp/Controller/Books.pm> to inherit from
-L<Catalyst::Controller::HTML::FormFu|Catalyst::Controller::HTML::FormFu>
-by changing the C<extends> line from the default of:
+L<Catalyst::Controller::HTML::FormFu> by changing the C<extends> line
+from the default of:
 
     BEGIN {extends 'Catalyst::Controller'; }
 
@@ -87,10 +90,13 @@ to use the FormFu base controller class:
 
 Don't forget to add:
 
+    requires 'HTML::FormFu';
     requires 'Catalyst::Controller::HTML::FormFu';
+    requires 'requires 'HTML::FormFu::Model::DBIC';';
 
 to your C<Makefile.PL>.
 
+
 =head2 Add Action to Display and Save the Form
 
 Open C<lib/MyApp/Controller/Books.pm> in your editor and add the
@@ -107,7 +113,7 @@ following method:
     
         # Get the form that the :FormConfig attribute saved in the stash
         my $form = $c->stash->{form};
-  
+    
         # Check if the form has been submitted (vs. displaying the initial
         # form) and if the data passed validation.  "submitted_and_valid"
         # is shorthand for "$form->submitted && !$form->has_errors"
@@ -116,10 +122,9 @@ following method:
             my $book = $c->model('DB::Book')->new_result({});
             # Save the form data for the book
             $form->model->update($book);
-            # Set a status message for the user
-            $c->flash->{status_msg} = 'Book created';
-            # Return to the books list
-            $c->response->redirect($c->uri_for($self->action_for('list'))); 
+            # Set a status message for the user & return to books list
+            $c->response->redirect($c->uri_for($self->action_for('list'),
+                {mid => $c->set_status_msg("Book created")}));
             $c->detach;
         } else {
             # Get the authors from the DB
@@ -134,7 +139,7 @@ following method:
             # Add the authors to it
             $select->options(\@authors);
         }
-        
+    
         # Set the template
         $c->stash(template => 'books/formfu_create.tt2');
     }
@@ -143,12 +148,12 @@ following method:
 =head2 Create a Form Config File
 
 Although C<HTML::FormFu> supports any configuration file handled by
-L<Config::Any|Config::Any>, most people tend to use YAML.  First
-create a directory to hold your form configuration files:
+L<Config::Any>, most people tend to use YAML.  First create a directory
+to hold your form configuration files:
 
-    mkdir -p root/forms/books
+    $ mkdir -p root/forms/books
 
-Then create the file C<root/forms/books/formfu_create.yml> and enter the 
+Then create the file C<root/forms/books/formfu_create.yml> and enter the
 following text:
 
     ---
@@ -186,15 +191,15 @@ following text:
           name: submit
           value: Submit
 
-B<NOTE:> Copying and pasting YAML from perl documentation is sometimes
+B<NOTE:> Copying and pasting YAML from Perl documentation is sometimes
 tricky.  See the L<Config::General Config for this tutorial> section of
 this document for a more foolproof config format.
 
 
 =head2 Update the CSS
 
-Edit C<root/static/css/main.css> and add the following lines to the bottom of
-the file:
+Edit C<root/static/css/main.css> and add the following lines to the
+bottom of the file:
 
     ...
     input {
@@ -208,16 +213,13 @@ the file:
         display: block;
     }
 
-These changes will display form elements vertically.  Note that the 
-existing definition of the C<.error> class is pulling the color scheme 
-settings from the C<root/lib/config/col> file that was created by the 
-TTSite helper.  This allows control over the CSS color settings from a 
-single location.
+These changes will display form elements vertically.
 
 
 =head2 Create a Template Page To Display The Form
 
-Open C<root/src/books/formfu_create.tt2> in your editor and enter the following:
+Open C<root/src/books/formfu_create.tt2> in your editor and enter the
+following:
 
     [% META title = 'Create/Update Book' %]
     
@@ -239,8 +241,8 @@ the bottom of the existing file:
       <a href="[% c.uri_for(c.controller.action_for('formfu_create')) %]">Create</a>
     </p>
 
-This adds a new link to the bottom of the book list page that we can
-use to easily launch our HTML::FormFu-based form.
+This adds a new link to the bottom of the book list page that we can use
+to easily launch our HTML::FormFu-based form.
 
 
 =head2 Test The HTML::FormFu Create Form
@@ -257,16 +259,16 @@ form.  Fill in the following values:
     Rating: 4
     Author: Comer
 
-Click the Submit button, and you will be returned to the Book List page
+Click the "Submit" button, and you will be returned to the Book List page
 with a "Book created" status message displayed.
 
 Also note that this implementation allows you to create books with any
-bogus information.  Although we have constrained the authors with the 
-drop-down list (note that this isn't bulletproof because we still have 
-not prevented a user from "hacking" the form to specify other values), 
-there are no restrictions on items such as the length of the title (for 
-example, you can create a one-letter title) and the value of the rating 
-(you can use any number you want, and even non-numeric values with 
+bogus information.  Although we have constrained the authors with the
+drop-down list (note that this isn't bulletproof because we still have
+not prevented a user from "hacking" the form to specify other values),
+there are no restrictions on items such as the length of the title (for
+example, you can create a one-letter title) and the value of the rating
+(you can use any number you want, and even non-numeric values with
 SQLite).  The next section will address this concern.
 
 B<Note:> Depending on the database you are using and how you established
@@ -278,19 +280,18 @@ performing any validation.
 
 =head1 HTML::FormFu VALIDATION AND FILTERING
 
-Although the use of L<HTML::FormFu|HTML::FormFu> 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 also be used to 
-remove extraneous whitespace from fields or to escape meta-characters in 
-user input.
+Although the use of L<HTML::FormFu> 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 also be used to remove extraneous
+whitespace from fields or to escape meta-characters in user input.
 
 
 =head2 Add Constraints
 
-Open C<root/forms/books/formfu_create.yml> in your editor and update it 
+Open C<root/forms/books/formfu_create.yml> in your editor and update it
 to match:
 
     ---
@@ -366,17 +367,17 @@ to match:
     
     # Global filters and constraints.
     constraints:
-      # The user cannot leave any fields blank
-      - Required
-      # If not all fields are required, move the Required constraint to the 
-      # fields that are
+        # The user cannot leave any fields blank
+        - Required
+        # If not all fields are required, move the Required constraint to the
+        # fields that are
     filter:
-      # Remove whitespace at both ends
-      - TrimEdges
-      # Escape HTML characters for safety
-      - HTMLEscape
+        # Remove whitespace at both ends
+        - TrimEdges
+        # Escape HTML characters for safety
+        - HTMLEscape
 
-B<NOTE:> Copying and pasting YAML from perl documentation is sometimes
+B<NOTE:> Copying and pasting YAML from Perl documentation is sometimes
 tricky.  See the L<Config::General Config for this tutorial> section of
 this document for a more foolproof config format.
 
@@ -387,39 +388,41 @@ The main changes are:
 =item *
 
 The C<Select> element for C<authors> is changed from a single-select
-drop-down to a multi-select list by adding configuration for the 
+drop-down to a multi-select list by adding configuration for the
 C<multiple> and C<size> options in C<formfu_create.yml>.
 
 =item *
 
 Constraints are added to provide validation of the user input.  See
-L<HTML::FormFu::Constraint|HTML::FormFu::Constraint> for other
-constraints that are available.
+L<HTML::FormFu::Constraint> for other constraints that are available.
 
 =item *
 
-A variety of filters are run on every field to remove and escape 
-unwanted input.  See L<HTML::FormFu::Filter|HTML::FormFu::Filter>
-for more filter options.
+A variety of filters are run on every field to remove and escape
+unwanted input.  See L<HTML::FormFu::Filter> for more filter options.
 
 =back
 
 
 =head2 Try Out the Updated Form
 
-Make sure you are still logged in as C<test01> and 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::FormFu C<constraint> items will 
-validate the logic and insert feedback as appropriate.  Try adding blank 
-spaces at the front or the back of the title and note that it will be 
+Make sure you are still logged in as C<test01> and 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::FormFu C<constraint> items will
+validate the logic and insert feedback as appropriate.  Try adding blank
+spaces at the front or the back of the title and note that it will be
 removed.
 
+Note that you can update your FormFu YAML forms and the development
+server does not need to reload -- the form definition is read from
+the YAML file each time a controller action uses it.
+
 
 =head1 CREATE AND UPDATE/EDIT ACTION
 
-Let's expand the work done above to add an edit action.  First, open 
-C<lib/MyApp/Controller/Books.pm> and add the following method to the 
+Let's expand the work done above to add an edit action.  First, open
+C<lib/MyApp/Controller/Books.pm> and add the following method to the
 bottom:
 
     =head2 formfu_edit
@@ -437,8 +440,9 @@ bottom:
     
         # Make sure we were able to get a book
         unless ($book) {
-            $c->flash->{error_msg} = "Invalid book -- Cannot edit";
-            $c->response->redirect($c->uri_for($self->action_for('list')));
+            # Set an error message for the user & return to books list
+            $c->response->redirect($c->uri_for($self->action_for('list'),
+                {mid => $c->set_error_msg("Invalid book -- Cannot edit")}));
             $c->detach;
         }
     
@@ -452,9 +456,9 @@ bottom:
             # Save the form data for the book
             $form->model->update($book);
             # Set a status message for the user
-            $c->flash->{status_msg} = 'Book edited';
-            # Return to the books list
-            $c->response->redirect($c->uri_for($self->action_for('list')));
+            # Set a status message for the user & return to books list
+            $c->response->redirect($c->uri_for($self->action_for('list'),
+                {mid => $c->set_status_msg("Book edited")}));
             $c->detach;
         } else {
             # Get the authors from the DB
@@ -476,22 +480,22 @@ bottom:
         $c->stash(template => 'books/formfu_create.tt2');
     }
 
-Most of this code should look familiar to what we used in the 
-C<formfu_create> method (in fact, we should probably centralize some of 
+Most of this code should look familiar to what we used in the
+C<formfu_create> method (in fact, we should probably centralize some of
 the common code in separate methods).  The main differences are:
 
 =over 4
 
 =item *
 
-We have to manually specify the name of the FormFu .yml file as an 
-argument to C<:FormConfig> because the name can no longer be 
+We have to manually specify the name of the FormFu .yml file as an
+argument to C<:FormConfig> because the name can no longer be
 automatically deduced from the name of our action/method (by default,
 FormFu would look for a file named C<books/formfu_edit.yml>).
 
 =item *
 
-We load the book object from the stash (found using the $id passed to 
+We load the book object from the stash (found using the $id passed to
 the Chained object method)
 
 =item *
@@ -500,51 +504,53 @@ We use C<$id> to look up the existing book from the database.
 
 =item *
 
-We make sure the book lookup returned a valid book.  If not, we set 
-the error message and return to the book list.
+We make sure the book lookup returned a valid book.  If not, we set the
+error message and return to the book list.
 
 =item *
 
-If the form has been submitted and passes validation, we skip creating a 
-new book and just use C<$form-E<gt>model-E<gt>update> to update the existing 
-book.
+If the form has been submitted and passes validation, we skip creating a
+new book and just use C<$form-E<gt>model-E<gt>update> to update the
+existing book.
 
 =item *
 
-If the form is being displayed for the first time (or has failed 
+If the form is being displayed for the first time (or has failed
 validation and it being redisplayed), we use
- C<$form-E<gt>model-E<gt>default_values> to populate the form with data from the
-database.
+C<$form-E<gt>model-E<gt>default_values> to populate the form with data
+from the database.
 
 =back
 
-Then, edit C<root/src/books/list.tt2> and add a new link below the 
-existing "Delete" link that allows us to edit/update each existing book. 
-The last E<lt>tdE<gt> cell in the book list table should look like the 
+Then, edit C<root/src/books/list.tt2> and add a new link below the
+existing "Delete" link that allows us to edit/update each existing book.
+The last E<lt>tdE<gt> cell in the book list table should look like the
 following:
 
     ...
     <td>
       [% # Add a link to delete a book %]
-      <a href="[% c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
+      <a href="[%
+        c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
       [% # Add a link to edit a book %]
-      <a href="[% c.uri_for(c.controller.action_for('formfu_edit'), [book.id]) %]">Edit</a>
+      <a href="[%
+        c.uri_for(c.controller.action_for('formfu_edit'), [book.id]) %]">Edit</a>
     </td>
     ...
 
-B<Note:> Only add two lines (the "Add a link to edit a book" comment
-and the href for C<formfu_edit>).  Make sure you add it below the
-existing C<delete> link.
+B<Note:> Only add three lines (the "Add a link to edit a book" comment and
+the href for C<formfu_edit>).  Make sure you add it below the existing
+C<delete> link.
 
 
 =head2 Try Out the Edit/Update Feature
 
-Make sure you are still logged in as C<test01> and go to the 
-L<http://localhost:3000/books/list> URL in your browser.  Click the 
-"Edit" link next to "Internetworking with TCP/IP Vol. II", change the 
-rating to a 3, the "II" at end of the title to the number "2", add 
-Stevens as a co-author (control-click), and click Submit.  You will then 
-be returned to the book list with a "Book edited" message at the top in 
+Make sure you are still logged in as C<test01> and go to the
+L<http://localhost:3000/books/list> URL in your browser.  Click the
+"Edit" link next to "Internetworking with TCP/IP Vol. II", change the
+rating to a 3, the "II" at end of the title to the number "2", add
+Stevens as a co-author (control-click), and click Submit.  You will then
+be returned to the book list with a "Book edited" message at the top in
 green.  Experiment with other edits to various books.
 
 
@@ -584,9 +590,8 @@ real reason you worked through this Tutorial in the first place.
 
 If you are having difficulty with YAML config above, please save the
 below into the file C<formfu_create.conf> and delete the
-C<formfu_create.yml> file.  The below is in
-L<Config::General|Config::General> format which follows the syntax of
-Apache config files.
+C<formfu_create.yml> file.  The below is in L<Config::General> format
+which follows the syntax of Apache config files.
 
    constraints   Required
    <elements>
@@ -632,16 +637,16 @@ Apache config files.
        type   Submit
    </elements>
    indicator   submit
-   
+
 
 =head1 AUTHOR
 
 Kennedy Clark, C<hkclark@gmail.com>
 
-Please report any errors, issues or suggestions to the author.  The
-most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
+Feel free to contact the author for any errors or suggestions, but the
+best way to report issues is via the CPAN RT Bug system at
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
 
-Copyright 2006-2010, Kennedy Clark, under the
+Copyright 2006-2011, Kennedy Clark, under the
 Creative Commons Attribution Share-Alike License Version 3.0
 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).
index 13f3836..8db2e5f 100644 (file)
@@ -57,15 +57,15 @@ L<Appendices|Catalyst::Manual::Tutorial::10_Appendices>
 =head1 DESCRIPTION
 
 This portion of the tutorial explores
-L<HTML::FormHandler|HTML::FormHandler> and how it can be used to manage
+L<HTML::FormHandler> and how it can be used to manage
 forms, perform validation of form input, and save and restore data
 to or from the database. This was written using HTML::FormHandler version
 0.28001.
 
 See 
-L<Catalyst::Manual::Tutorial::09_AdvancedCRUD|Catalyst::Manual::Tutorial::09_AdvancedCRUD>
+L<Catalyst::Manual::Tutorial::09_AdvancedCRUD>
 for additional form management options other than 
-L<HTML::FormHandler|HTML::FormHandler>.
+L<HTML::FormHandler>.
 
 
 =head1 Install HTML::FormHandler
@@ -86,7 +86,7 @@ to your C<Makefile.PL>.
 
 =head1 HTML::FormHandler FORM CREATION
 
-This section looks at how L<HTML::FormHandler|HTML::FormHandler> can be used to 
+This section looks at how L<HTML::FormHandler> can be used to 
 add additional functionality to the manually created form from Chapter 4.
 
 
@@ -150,9 +150,9 @@ Add the following methods:
         $c->stash( template => 'books/form.tt2', form => $form );
         $form->process( item => $book, params => $c->req->params );
         return unless $form->validated;
-        $c->flash( message => 'Book created' );
-        # Redirect the user back to the list page
-        $c->response->redirect($c->uri_for($self->action_for('list')));
+        # Set a status message for the user & return to books list
+        $c->response->redirect($c->uri_for($self->action_for('list'),
+            {mid => $c->set_status_msg("Book created")}));
     }
 
 These two methods could be combined at this point, but we'll use the 'form'
index 3af8a6c..ac3bccf 100644 (file)
@@ -175,7 +175,7 @@ Chapter 3: More Catalyst Basics
 
 Install the PostgreSQL server and client and DBD::Pg:
 
-If you are following along in Debian 5, you can quickly install these 
+If you are following along in Debian 6, you can quickly install these
 items via this command:
 
     sudo aptitude install postgresql libdbd-pg-perl libdatetime-format-pg-perl
@@ -541,7 +541,7 @@ The Perl C<DBD::MySQL> module
 =back
 
 For CentOS users (see
-L<Catalyst::Manual::Installation::CentOS4|Catalyst::Manual::Installation::CentOS4>),
+L<Catalyst::Manual::Installation::CentOS4>),
 you can use the following commands to install the software and start the MySQL
 daemon:
 
@@ -561,10 +561,10 @@ in you MySQL. You can simply figure out that your install supports it or not:
     # mysql -u root -p
     Enter password:
     Welcome to the MySQL monitor.  Commands end with ; or \g.
-
+    
     Type 'help;' or '\h' for help. Type '\c' to clear the current input 
     statement.
-
+    
     mysql> SHOW VARIABLES LIKE 'have_innodb';
     +---------------+-------+
     | Variable_name | Value |
@@ -572,7 +572,7 @@ in you MySQL. You can simply figure out that your install supports it or not:
     | have_innodb   | YES   |
     +---------------+-------+
     1 row in set (0.01 sec)
-
+    
     mysql> exit
     Bye
 
@@ -586,18 +586,18 @@ Create the database and set the permissions:
     # mysql -u root -p
     Enter password:
     Welcome to the MySQL monitor.  Commands end with ; or \g.
-
+    
     Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
-
+    
     mysql> CREATE DATABASE `myapp`;
     Query OK, 1 row affected (0.01 sec)
-
+    
     mysql> GRANT ALL PRIVILEGES ON myapp.* TO 'tutorial'@'localhost' IDENTIFIED BY 'yourpassword';
     Query OK, 0 rows affected (0.00 sec)
-
+    
     mysql> FLUSH PRIVILEGES;
     Query OK, 0 rows affected (0.00 sec)
-
+    
     mysql> exit
     Bye
 
@@ -643,7 +643,7 @@ Open the C<myapp01_mysql.sql> in your editor and enter:
     (3, 'Internetworking with TCP/IP Vol.1', 4),
     (4, 'Perl Cookbook', 5),
     (5, 'Designing with Web Standards', 5);
-
+    
     INSERT INTO `book_authors` (`book_id`, `author_id`) VALUES
     (1, 1),
     (1, 2),
@@ -653,7 +653,7 @@ Open the C<myapp01_mysql.sql> in your editor and enter:
     (4, 6),
     (4, 7),
     (5, 8);
-
+    
     INSERT INTO `authors` (`id`, `first_name`, `last_name`) VALUES
     (1, 'Greg', 'Bastien'),
     (2, 'Sara', 'Nasseh'),
@@ -663,7 +663,7 @@ Open the C<myapp01_mysql.sql> in your editor and enter:
     (6, 'Tom', 'Christiansen'),
     (7, 'Nathan', 'Torkington'),
     (8, 'Jeffrey', 'Zeldman');
-
+    
     ALTER TABLE `book_authors`
     ADD CONSTRAINT `book_author_ibfk_2` FOREIGN KEY (`author_id`) REFERENCES `authors` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
     ADD CONSTRAINT `book_author_ibfk_1` FOREIGN KEY (`book_id`) REFERENCES `books` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
@@ -681,11 +681,11 @@ Make sure the data loaded correctly:
     $ mysql -u tutorial -p myapp
     Reading table information for completion of table and column names
     You can turn off this feature to get a quicker startup with -A
-
+    
     Welcome to the MySQL monitor.  Commands end with ; or \g.
-
+    
     Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
-
+    
     mysql> show tables;
     +-----------------+
     | Tables_in_myapp |
@@ -695,7 +695,7 @@ Make sure the data loaded correctly:
     | books           |
     +-----------------+
     3 rows in set (0.00 sec)
-
+    
     mysql> select * from books;
     +----+------------------------------------+--------+
     | id | title                              | rating |
@@ -707,7 +707,7 @@ Make sure the data loaded correctly:
     |  5 | Designing with Web Standards       |      5 |
     +----+------------------------------------+--------+
     5 rows in set (0.00 sec)
-
+    
     mysql>
 
 =back
@@ -777,18 +777,18 @@ Open C<myapp02_mysql.sql> in your editor and enter:
     INSERT INTO `roles` (`id`, `role`) VALUES
     (1, 'user'),
     (2, 'admin');
-
+    
     INSERT INTO `users` (`id`, `username`, `password`, `email_address`, `first_name`, `last_name`, `active`) VALUES
     (1, 'test01', 'mypass', 't01@na.com', 'Joe', 'Blow', 1),
     (2, 'test02', 'mypass', 't02@na.com', 'Jane', 'Doe', 1),
     (3, 'test03', 'mypass', 't03@na.com', 'No', 'Go', 0);
-
+    
     INSERT INTO `user_roles` (`user_id`, `role_id`) VALUES
     (1, 1),
     (2, 1),
     (3, 1),
     (1, 2);
-
+    
     ALTER TABLE `user_roles
     ADD CONSTRAINT `user_role_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
     ADD CONSTRAINT `user_role_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
@@ -842,10 +842,10 @@ Load the user/roles data:
 
 Kennedy Clark, C<hkclark@gmail.com>
 
-Please report any errors, issues or suggestions to the author.  The
-most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
+Feel free to contact the author for any errors or suggestions, but the
+best way to report issues is via the CPAN RT Bug system at
+L<https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual>.
 
-Copyright 2006-2010, Kennedy Clark, under the
+Copyright 2006-2011, Kennedy Clark, under the
 Creative Commons Attribution Share-Alike License Version 3.0
 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).