removing old tutorial; it's now in Task-Catalyst-Tutorial
Jonathan Rockway [Wed, 27 Sep 2006 06:36:56 +0000 (06:36 +0000)]
lib/Catalyst/Manual/Tutorial/AdvancedCRUD.pod [deleted file]
lib/Catalyst/Manual/Tutorial/Appendices.pod [deleted file]
lib/Catalyst/Manual/Tutorial/Authentication.pod [deleted file]
lib/Catalyst/Manual/Tutorial/Authorization.pod [deleted file]
lib/Catalyst/Manual/Tutorial/BasicCRUD.pod [deleted file]
lib/Catalyst/Manual/Tutorial/CatalystBasics.pod [deleted file]
lib/Catalyst/Manual/Tutorial/Debugging.pod [deleted file]
lib/Catalyst/Manual/Tutorial/Intro.pod [deleted file]
lib/Catalyst/Manual/Tutorial/Testing.pod [deleted file]

diff --git a/lib/Catalyst/Manual/Tutorial/AdvancedCRUD.pod b/lib/Catalyst/Manual/Tutorial/AdvancedCRUD.pod
deleted file mode 100644 (file)
index 77b81cd..0000000
+++ /dev/null
@@ -1,764 +0,0 @@
-=head1 NAME
-
-Catalyst::Manual::Tutorial::AdvancedCRUD - Catalyst Tutorial - Part 8: Advanced CRUD
-
-
-=head1 OVERVIEW
-
-This is B<Part 8 of 9> for the Catalyst tutorial.
-
-L<Tutorial Overview|Catalyst::Manual::Tutorial>
-
-=over 4
-
-=item 1
-
-L<Introduction|Catalyst::Manual::Tutorial::Intro>
-
-=item 2
-
-L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
-
-=item 3
-
-L<Basic CRUD|Catalyst::Manual::Tutorial_BasicCRUD>
-
-=item 4
-
-L<Authentication|Catalyst::Manual::Tutorial::Authentication>
-
-=item 5
-
-L<Authorization|Catalyst::Manual::Tutorial::Authorization>
-
-=item 6
-
-L<Debugging|Catalyst::Manual::Tutorial::Debugging>
-
-=item 7
-
-L<Testing|Catalyst::Manual::Tutorial::Testing>
-
-=item 8
-
-B<AdvancedCRUD>
-
-=item 9
-
-L<Appendices|Catalyst::Manual::Tutorial::Appendices>
-
-=back
-
-=head1 DESCRIPTION
-
-This part of the tutorial explores more advanced functionality for
-Create, Read, Update, and Delete (CRUD) than we saw in Part 3.  In
-particular, it looks at a number of techniques that can be useful for
-the Update portion of CRUD, such as automated form generation,
-validation of user-entered data, and automated transfer of data between
-forms and model objects.
-
-In keeping with the Catalyst (and Perl) spirit of flexibility, there are
-many different ways to approach advanced CRUD operations in a Catalyst
-environment.  One alternative is to use
-L<Catalyst::Helper::Controller::Scaffold|Catalyst::Helper::Controller::Scaffold> 
-to instantly construct a set of Controller methods and templates for 
-basic CRUD operations.  Although a popular subject in Quicktime 
-movies that serve as promotional material for various frameworks, 
-real-world applications generally require more control.  Other 
-options include L<Data::FormValidator|Data::FormValidator> and
-L<HTML::FillInForm|HTML::FillInForm>.
-
-Here, we will make use of the L<HTML::Widget|HTML::Widget> to not only 
-ease form creation, but to also provide validation of the submitted 
-data.  The approached used by this part of the tutorial is to slowly 
-incorporate additional L<HTML::Widget|HTML::Widget> functionality in a 
-step-wise fashion (we start with fairly simple form creation and then 
-move on to more complex and "magical" features such as validation and 
-auto-population/auto-saving).
-
-B<Note:> Part 8 of the tutorial is optional.  Users who do not wish to
-use L<HTML::Widget|HTML::Widget> may skip this part.
-
-You can checkout the source code for this example from the catalyst subversion repository as per the instructions in L<Catalyst::Manual::Tutorial::Intro>
-
-=head1 C<HTML::WIDGET> FORM CREATION
-
-This section looks at how L<HTML::Widget|HTML::Widget> can be used to
-add additional functionality to the manually created form from Part 3.
-
-=head2 Add the C<HTML::Widget> Plugin
-
-Open C<lib/MyApp.pm> in your editor and add the following to the list of
-plugins (be sure to leave the existing plugins enabled):
-
-    HTML::Widget
-
-=head2 Add a Form Creation Helper Method
-
-Open C<lib/MyApp/Controller/Books.pm> in your editor and add the
-following method:
-
-    =head2 make_book_widget
-    
-    Build an HTML::Widget form for book creation and updates
-    
-    =cut
-    
-    sub make_book_widget {
-        my ($self, $c) = @_;
-    
-        # Create an HTML::Widget to build the form
-        my $w = $c->widget('book_form')->method('post');
-    
-        # Get authors
-        my @authorObjs = $c->model("MyAppDB::Author")->all();
-        my @authors = map {$_->id => $_->last_name }
-                           sort {$a->last_name cmp $b->last_name} @authorObjs;
-    
-        # Create the form feilds
-        $w->element('Textfield', 'title'  )->label('Title')->size(60);
-        $w->element('Textfield', 'rating' )->label('Rating')->size(1);
-        $w->element('Select',    'authors')->label('Authors')
-            ->options(@authors);
-        $w->element('Submit',    'submit' )->value('submit');
-    
-        # Return the widget    
-        return $w;
-    }
-
-This method provides a central location that builds an 
-HTML::Widget-based form with the appropriate fields.  The "Get authors" 
-code uses DBIC to retrieve a list of model objects and then uses C<map> 
-to create a hash where the hash keys are the database primary keys from 
-the authors table and the associated values are the last names of the 
-authors.
-
-=head2 Add Actions to Display and Save the Form
-
-Open C<lib/MyApp/Controller/Books.pm> in your editor and add the
-following methods:
-
-    =head2 hw_create
-    
-    Build an HTML::Widget form for book creation and updates
-    
-    =cut
-    
-    sub hw_create : Local {
-        my ($self, $c) = @_;
-    
-        # Create the widget and set the action for the form
-        my $w = $self->make_book_widget($c);
-        $w->action($c->uri_for('hw_create_do'));
-    
-        # Write form to stash variable for use in template
-        $c->stash->{widget_result} = $w->result;
-    
-        # Set the template
-        $c->stash->{template} = 'books/hw_form.tt2';
-    }
-    
-    
-    =head2 hw_create_do
-    
-    Build an HTML::Widget form for book creation and updates
-    
-    =cut
-    
-    sub hw_create_do : Local {
-        my ($self, $c) = @_;
-    
-        # Retrieve the data from the form
-        my $title   = $c->request->params->{title};
-        my $rating  = $c->request->params->{rating};
-        my $authors = $c->request->params->{authors};
-    
-        # Call create() on the book model object. Pass the table 
-        # columns/field values we want to set as hash values
-        my $book = $c->model('MyAppDB::Book')->create({
-                title   => $title,
-                rating  => $rating
-            });
-        
-        # Add a record to the join table for this book, mapping to 
-        # appropriate author
-        $book->add_to_book_authors({author_id => $authors});
-        
-        # Set a status message for the user
-        $c->stash->{status_msg} = 'Book created';
-    
-        # Use 'hw_create' to redisplay the form.  As discussed in 
-        # Part 3, 'detach' is like 'forward', but it does not return
-        $c->detach('hw_create');
-    }
-
-Note how we use C<make_book_widget> to build the core parts of the form
-in one location, but we set the action (the URL the form is sent to when
-the user clicks the 'Submit' button) separately in C<hw_create>.  Doing
-so allows us to have the same form submit the data to different actions
-(e.g., C<hw_create_do> for a create operation but C<hw_update_do> to
-update an existing book object).
-
-B<NOTE:> If you receive an error about Catalyst not being able to find
-the template C<hw_create_do.tt2>, please verify that you followed the
-instructions in the final section of
-L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics> where
-you returned to a manually-specified template.  You can either use 
-C<forward>/C<detach> B<OR> default template names, but the two cannot
-be used together.
-
-
-=head2 Update the CSS
-
-Edit C<root/src/ttsite.css> and add the following lines to the bottom of
-the file:
-
-    label {
-        display: block;
-        width: 10em;
-        position: relative;
-        margin: .5em 0em;
-    }
-    label input {
-        position: absolute;
-        left: 100%;
-    }
-    label select {
-        position: absolute;
-        left: 100%;
-    }
-    .submit {
-        margin-top: 2em;;
-    }
-    .error_messages {
-        color: [% site.col.error %];
-    }
-
-These changes will display form elements vertically and also show error
-messages in red.  Note that we are pulling the color scheme settings
-from the C<root/lib/config/col> file that was created by the TTSite
-helper.  This allows us to change the color used by various error styles
-in the CSS from a single location.
-
-=head2 Create a Template Page To Display The Form
-
-Open C<root/src/books/hw_form.tt2> in your editor and enter the following:
-
-    [% META title = 'Create/Update Book' %]
-    
-    [% widget_result.as_xml %]
-    
-    <p><a href="[% Catalyst.uri_for('list') %]">Return to book list</a></p>
-
-=head2 Add Links for Create and Update via C<HTML::Widget>
-
-Open C<root/src/books/list.tt2> in your editor and add the following to
-the bottom of the existing file:
-
-    <p>
-      HTML::Widget:
-      <a href="[% Catalyst.uri_for('hw_create') %]">Create</a>
-    </p>
-
-
-=head2 Test The <HTML::Widget> Create Form
-
-Press C<Ctrl-C> to kill the previous server instance (if it's still
-running) and restart it:
-
-    $ script/myapp_server.pl
-
-Login as C<test01>.  Once at the Book List page, click the HTML::Widget
-"Create" link to display for form produced by C<make_book_widget>.  Fill
-out the form with the following values: Title = "Internetworking with
-TCP/IP Vol. II", Rating = "4", and Author = "Comer".  Click Submit, and
-you will be returned to the Create/Update Book page with a "Book
-created" status message displayed.  Click "Return to book list" to view
-the newly created book on the main list.
-
-Also note that this implementation allows you to can create books with
-bogus information.  Although we have constrained the authors with the
-drop-down list, there are no restrictions on items such as the length of
-the title (for example, you can create a one-letter title) and value for
-the rating (you can use any number you want, and even non-numeric values
-with SQLite).  The next section will address this concern.
-
-B<Note:> Depending on the database you are using and how you established
-the columns in your tables, the database could obviously provide various
-levels of "type enforcement" on your data.  The key point being made in
-the previous paragraph is that the I<web application> itself is not
-performing any validation.
-
-=head1 C<HTML::WIDGET> VALIDATION AND FILTERING
-
-Although the use of L<HTML::Widget|HTML::Widget> in the previous section
-did provide an automated mechanism to build the form, the real power of
-this module stems from functionality that can automatically validate and
-filter the user input.  Validation uses constraints to be sure that
-users input appropriate data (for example, that the email field of a
-form contains a valid email address).  Filtering can be used to remove
-extraneous whitespace from fields or to escape meta-characters in user
-input.
-
-=head2 Add Constraints and Filters to the Widget Creation Method
-
-Open C<lib/MyApp/Controller/Books.pm> in your editor and update the
-C<make_book_widget> method to match the following (new sections have
-been marked with a C<*** NEW:> comment):
-
-    sub make_book_widget {
-        my ($self, $c) = @_;
-    
-        # Create an HTML::Widget to build the form
-        my $w = $c->widget('book_form')->method('post');
-            
-        # Get authors
-        my @authorObjs = $c->model("MyAppDB::Author")->all();
-        my @authors = map {$_->id => $_->last_name }
-                           sort {$a->last_name cmp $b->last_name} @authorObjs;
-    
-        # Create the form feilds
-        $w->element('Textfield', 'title'  )->label('Title')->size(60);
-        $w->element('Textfield', 'rating' )->label('Rating')->size(1);
-        # ***NEW: Convert to multi-select list
-        $w->element('Select',    'authors')->label('Authors')
-            ->options(@authors)->multiple(1)->size(3);
-        $w->element('Submit',    'submit' )->value('submit');
-    
-        # ***NEW: Set constraints
-        $w->constraint(All     => qw/title rating authors/)
-            ->message('Required. ');
-        $w->constraint(Integer => qw/rating/)
-            ->message('Must be an integer. ');
-        $w->constraint(Range   => qw/rating/)->min(1)->max(5)
-            ->message('Must be a number between 1 and 5. ');
-        $w->constraint(Length  => qw/title/)->min(5)->max(50)
-            ->message('Must be between 5 and 50 characters. ');
-    
-        # ***NEW: Set filters
-        for my $column (qw/title rating authors/) {
-            $w->filter( HTMLEscape => $column );
-            $w->filter( TrimEdges  => $column );
-        }
-    
-        # Return the widget    
-        return $w;
-    }
-
-The main changes are:
-
-=over 4
-
-=item *
-
-The C<Select> element for C<authors> is changed from a single-select
-drop-down to a multi-select list by adding calls to C<multiple> (set to
-C<true>) and C<size> (set to the number of rows to display).
-
-=item *
-
-Four sets of constraints are added to provide validation of the user input.
-
-=item *
-
-Two filters are run on every field to remove and escape unwanted input.
-
-=back
-
-=head2 Rebuild the Form Submission Method to Include Validation
-
-Edit C<lib/MyApp/Controller/Books.pm> and change C<hw_create_do> to
-match the following code (enough of the code is different that you
-probably want to cut and paste this over code the existing method):
-
-    sub hw_create_do : Local {
-        my ($self, $c) = @_;
-    
-        # Retrieve the data from the form
-        my $title   = $c->request->params->{title};
-        my $rating  = $c->request->params->{rating};
-        my $authors = $c->request->params->{authors};
-        
-        # Create the widget and set the action for the form
-        my $w = $self->make_book_widget($c);
-        $w->action($c->uri_for('hw_create_do'));
-    
-        # Validate the form parameters
-        my $result = $w->process($c->req);
-    
-        # Write form (including validation error messages) to
-        # stash variable for use in template
-        $c->stash->{widget_result} = $result;
-    
-        # Were their validation errors?
-        if ($result->has_errors) {
-            # Warn the user at the top of the form that there were errors.
-            # Note that there will also be per-field feedback on
-            # validation errors because of '$w->process($c->req)' above.
-            $c->stash->{error_msg} = 'Validation errors!';
-        } else {
-            # Everything validated OK, so do the create
-            # Call create() on the book model object. Pass the table
-            # columns/field values we want to set as hash values
-            my $book = $c->model('MyAppDB::Book')->create({
-                    title   => $title,
-                    rating  => $rating
-                });
-    
-            # Add a record to the join table for this book, mapping to
-            # appropriate author.  Note that $authors will be 1 author as
-            # a scalar or ref to list of authors depending on how many the
-            # user selected; the 'ref $authors ?...' handles both cases
-            foreach my $author (ref $authors ? @$authors : $authors) {
-                $book->add_to_book_authors({author_id => $author});
-            }    
-            # Set a status message for the user
-            $c->stash->{status_msg} = 'Book created';
-        }
-    
-        # Set the template
-        $c->stash->{template} = 'books/hw_form.tt2';
-    }
-
-The key changes to C<hw_create_do> are:
-
-=over 4
-
-=item *
-
-C<hw_create_do> no longer does a C<detach> to C<hw_create> to redisplay
-the form.  Now that C<hw_create_do> has to process the form in order to
-perform the validation, we go ahead and build a complete set of form
-presentation logic into C<hw_create_do> (for example, C<hw_create_do>
-now has a C<$c-E<gt>stash-E<gt>{template}> line).  Note that if we
-process the form in C<hw_create_do> I<and> forward/detach back to
-<hw_create>, we would end up with C<make_book_widget> being called
-twice, resulting in a duplicate set of elements being added to the form.
-(There are other ways to address the "duplicate form rendering" issue --
-just be aware that it exists.)
-
-=item *
-
-C<$w-E<gt>process($c-E<gt>req)> is called to run the validation logic.
-Not only does this set the C<has_errors> flag if validation errors are
-encountered, it returns a string containing any field-specific warning
-messages.
-
-=item *
-
-An C<if> statement checks if any validation errors were encountered.  If
-so, C<$c-E<gt>stash-E<gt>{error_msg}> is set and the input form is
-redisplayed.  If no errors were found, the object is created in a manner
-similar to the prior version of the C<hw_create_do> method.
-
-=back
-
-=head2 Try Out the Form
-
-Press C<Ctrl-C> to kill the previous server instance (if it's still 
-running) and restart it:
-
-    $ script/myapp_server.pl
-
-Now try adding a book with various errors: title less than 5 characters,
-non-numeric rating, a rating of 0 or 6, etc.  Also try selecting one,
-two, and zero authors.  When you click Submit, the HTML::Widget
-C<constraint> items will validate the logic and insert feedback as
-appropriate.
-
-
-=head1 Enable C<DBIx::Class::HTMLWidget> Support
-
-In this section we will take advantage of some of the "auto-population"
-features of C<DBIx::Class::HTMLWidget>.  Enabling
-C<DBIx::Class::HTMLWidget> provides two additional methods to your DBIC
-model classes:
-
-=over 4
-
-=item *
-
-fill_widget()
-
-Takes data from the database and transfers it to your form widget.
-
-=item *
-
-populate_from_widget()
-
-Takes data from a form widget and uses it to update the corresponding
-records in the database.
-
-=back
-
-In other words, the two methods are a mirror image of each other: one
-reads from the database while the other writes to the database.
-
-=head2 Add C<DBIx::Class::HTMLWidget> to DBIC Model
-
-In order to use L<DBIx::Class::HTMLWidget|DBIx::Class::HTMLWidget>, we
-need to add C<HTMLWidget> to the C<load_components> line of DBIC result
-source files that need to use the C<fill_widget> and
-C<populate_from_widget> methods.  In this case, open
-C<lib/MyAppDB/Book.pm> and update the C<load_components> line to match:
-
-       __PACKAGE__->load_components(qw/PK::Auto Core HTMLWidget/);
-
-=head2 Use C<populate_from_widget> in C<hw_create_do>
-
-Edit C<lib/MyApp/Controller/Books.pm> and update C<hw_create_do> to
-match the following code:
-
-    =head2 hw_create_do
-    
-    Build an HTML::Widget form for book creation and updates
-    
-    =cut
-    
-    sub hw_create_do : Local {
-        my ($self, $c) = @_;
-    
-        # Create the widget and set the action for the form
-        my $w = $self->make_book_widget($c);
-        $w->action($c->uri_for('hw_create_do'));
-    
-        # Validate the form parameters
-        my $result = $w->process($c->req);
-    
-        # Write form (including validation error messages) to
-        # stash variable for use in template
-        $c->stash->{widget_result} = $result;
-    
-        # Were their validation errors?
-        if ($result->has_errors) {
-            # Warn the user at the top of the form that there were errors.
-            # Note that there will also be per-field feedback on
-            # validation errors because of '$w->process($c->req)' above.
-            $c->stash->{error_msg} = 'Validation errors!';
-        } else {
-            my $book = $c->model('MyAppDB::Book')->new({});
-            $book->populate_from_widget($result);
-    
-            # Add a record to the join table for this book, mapping to
-            # appropriate author.  Note that $authors will be 1 author as
-            # a scalar or ref to list of authors depending on how many the
-            # user selected; the 'ref $authors ?...' handles both cases
-            my $authors = $c->request->params->{authors};
-            foreach my $author (ref $authors ? @$authors : $authors) {
-                $book->add_to_book_authors({author_id => $author});
-            }
-    
-            # Set a status message for the user
-            $c->flash->{status_msg} = 'Book created';
-            
-            # Redisplay an empty form for another
-            $c->stash->{widget_result} = $w->result;
-        }
-    
-        # Set the template
-        $c->stash->{template} = 'books/hw_form.tt2';
-    }
-
-In this version of C<hw_create_do> we removed the logic that manually
-pulled the form variables and used them to call
-C<$c-E<gt>model('MyAppDB::Book')-E<gt>create> and replaced it with a
-single call to C<$book-E<gt>populate_from_widget>.  Note that we still
-have to call C<$book-E<gt>add_to_book_authors> once per author because
-C<populate_from_widget> does not currently handle the relationships
-between tables.  Also, we reset the form to an empty fields by adding
-another call to C<$w-E<gt>result> and storing the output in the stash 
-(if we don't override the output from C<$w-E<gt>process($c-E<gt>req)>,
-the form values already entered will be retained on redisplay --
-although this could be desirable for some applications, we avoid it
-here to help avoid the creation of duplicate records).
-
-
-=head2 Try Out the Form
-
-Press C<Ctrl-C> to kill the previous server instance (if it's still 
-running) and restart it:
-
-    $ script/myapp_server.pl
-
-Try adding a book that validates.  Return to the book list and the book 
-you added should be visible.
-
-
-
-=head1 Rendering C<HTMLWidget> Forms in a Table
-
-Some developers my wish to use the "old-fashioned" table style of 
-rendering a form in lieu of the default C<HTML::Widget> rendering that 
-assumes you will use CSS for formatting.  This section demonstrates
-some techniques that can override the default rendering with a 
-custom class.
-
-
-=head2 Add a New "Element Container"
-
-Open C<lib/FormElementContainer.pm> in your editor and enter:
-
-    package FormElementContainer;
-    
-    use base 'HTML::Widget::Container';
-    
-    sub _build_element {
-        my ($self, $element) = @_;
-    
-        return () unless $element;
-        if (ref $element eq 'ARRAY') {
-            return map { $self->_build_element($_) } @{$element};
-        }
-        my $e = $element->clone;
-        $e = new HTML::Element('span', class => 'fields_with_errors')->push_content($e)
-            if $self->error && $e->tag eq 'input';
-    
-        return $e ? ($e) : ();
-    }
-    
-    1;
-
-This simply dumps the HTML code for a given form element, followed by a 
-C<span> that can contain validation error message.
-
-
-=head2 Enable the New Element Container When Building the Form
-
-Open C<lib/MyApp/Controller/Books.pm> in your editor.  First add a
-C<use> for your element container class:
-
-    use FormElementContainer;
-
-B<Note:> If you forget to C<use> your container class in your 
-controller, then your form will not be displayed and no error messages 
-will be generated. Don't forget this important step!
-
-Then tell C<HTML::Widget> to use that class during rendering by updating
-C<make_book_widget> to match the following:
-
-    sub make_book_widget {
-        my ($self, $c) = @_;
-    
-        # Create an HTML::Widget to build the form
-        my $w = $c->widget('book_form')->method('post');
-    
-        # ***New: Use custom class to render each element in the form    
-        $w->element_container_class('FormElementContainer');
-        
-        # Get authors
-        my @authorObjs = $c->model("MyAppDB::Author")->all();
-        my @authors = map {$_->id => $_->last_name }
-                           sort {$a->last_name cmp $b->last_name} @authorObjs;
-    
-        # Create the form feilds
-        $w->element('Textfield', 'title'  )->label('Title')->size(60);
-        $w->element('Textfield', 'rating' )->label('Rating')->size(1);
-        # Convert to multi-select list
-        $w->element('Select',    'authors')->label('Authors')
-            ->options(@authors)->multiple(1)->size(3);
-        $w->element('Submit',    'submit' )->value('submit');
-    
-        # Set constraints
-        $w->constraint(All     => qw/title rating authors/)
-            ->message('Required. ');
-        $w->constraint(Integer => qw/rating/)
-            ->message('Must be an integer. ');
-        $w->constraint(Range   => qw/rating/)->min(1)->max(5)
-            ->message('Must be a number between 1 and 5. ');
-        $w->constraint(Length  => qw/title/)->min(5)->max(50)
-            ->message('Must be between 5 and 50 characters. ');
-    
-        # Set filters
-        for my $column (qw/title rating authors/) {
-            $w->filter( HTMLEscape => $column );
-            $w->filter( TrimEdges  => $column );
-        }
-    
-        # Return the widget    
-        return $w;
-    }
-
-The two new lines are marked with C<***New:>.
-
-
-=head2 Update the TT Template
-
-Open C<root/src/books/hw_form.tt2> and edit it to match:
-
-    [% META title = 'Create/Update Book' %]
-    
-    [%# Comment out the auto-rendered form %]
-    [%# widget_result.as_xml %]
-    
-    
-    [%# Iterate over the form elements and display each -%]
-    <form name="book_form" action="[% widget_result.action %]" method="post">
-    <table border="0">
-    [% FOREACH element = widget_result.elements %]
-      <tr>
-        <td class="form-label">
-          [% element.label.as_text %]
-        </td>
-        <td class="form-element">
-          [% element.element_xml %]
-          <span class="form-error">
-            [% element.error_xml %]
-          </span>
-        </td>
-      </tr>
-    [% END %]
-    </table>
-    </form>
-    
-    
-    <p><a href="[% Catalyst.uri_for('list') %]">Return to book list</a></p>
-    
-    
-    [%# A little JavaScript to move the cursor to the first field %]
-    <script LANGUAGE="JavaScript">
-    document.book_form.book_form_title.focus();
-    </script>
-
-This represents three changes:
-
-=over 4
-
-=item *
-
-The existing C<widget_result.as_xml> has been commented out.
-
-=item *
-
-It loops through each form element, displaying the field name in the 
-first table cell along with the form element and validation errors in 
-the second field.
-
-=item *
-
-JavaScript to position the user's cursor in the first field of the form.
-
-=back
-
-
-=head2 Try Out the Form
-
-Press C<Ctrl-C> to kill the previous server instance (if it's still 
-running) and restart it:
-
-    $ script/myapp_server.pl
-
-Try adding a book that validates.  Return to the book list and the book 
-you added should be visible.
-
-
-=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/trunk/Catalyst-Runtime/lib/Catalyst/Manual/Tutorial/>.
-
-Copyright 2006, Kennedy Clark, under Creative Commons License
-(L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
diff --git a/lib/Catalyst/Manual/Tutorial/Appendices.pod b/lib/Catalyst/Manual/Tutorial/Appendices.pod
deleted file mode 100644 (file)
index 1025640..0000000
+++ /dev/null
@@ -1,473 +0,0 @@
-=head1 NAME
-
-Catalyst::Manual::Tutorial::Appendices - Catalyst Tutorial - Part 9: Appendices
-
-
-=head1 OVERVIEW
-
-This is B<Part 9 of 9> of the Catalyst tutorial.
-
-L<Tutorial Overview|Catalyst::Manual::Tutorial>
-
-=over 4
-
-=item 1
-
-L<Introduction|Catalyst::Manual::Tutorial::Intro>
-
-=item 2
-
-L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
-
-=item 3
-
-L<Basic CRUD|Catalyst::Manual::Tutorial_BasicCRUD>
-
-=item 4
-
-L<Authentication|Catalyst::Manual::Tutorial::Authentication>
-
-=item 5
-
-L<Authorization|Catalyst::Manual::Tutorial::Authorization>
-
-=item 6
-
-L<Debugging|Catalyst::Manual::Tutorial::Debugging>
-
-=item 7
-
-L<Testing|Catalyst::Manual::Tutorial::Testing>
-
-=item 8
-
-L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
-
-=item 9
-
-B<Appendices>
-
-=back
-
-
-=head1 DESCRIPTION
-
-This part of the tutorial provides supporting information relevant to
-the Catalyst tutorial.
-
-
-=head1 APPENDIX 1: CUT AND PASTE FOR POD-BASED EXAMPLES
-
-You may notice that Pod indents example code with four spaces.  This
-section provides some quick advice to "un-indent" this text in common
-editors.
-
-=head2 "Un-indenting" with Vi/Vim
-
-When cutting and pasting multi-line text from Pod-based documents, the
-following vi/vim regexs can be helpful to "un-indent" the inserted text
-(do NOT type the quotes, they are only included to show spaces in the
-regex patterns).  I<Note that all 3 of the regexs end in 4 spaces>:
-
-=over 4
-
-=item * 
-
-":0,$s/^    "
-
-Removes four leading spaces from the entire file (from the first line,
-C<0>, to the last line, C<$>).
-
-=item * 
-
-"%s/^    "
-
-A shortcut for the previous item (C<%> specifies the entire file; so
-this removes four leading spaces from every line).
-
-=item * 
-
-":.,$s/^    "
-
-Removes the first four spaces from the line the cursor is on at the time
-the regex command is executed (".") to the last line of the file.
-
-=item * 
-
-":.,44s/^    "
-
-Removes four leading space from the current line through line 44
-(obviously adjust the C<44> to the appropriate value in your example).
-
-=back
-
-=head2 "Un-indenting" with Emacs
-
-Although there author has not used emacs for many years (apologies to 
-the emacs fans out there), here is a quick hint to get you started.  To
-replace the leading spaces of every line in a file, use:
-
-    M-x replace-regexp<RET>
-    Replace regexp: ^    <RET>
-    with: <RET>
-
-All of that will occur on the single line at the bottom of your screen.
-Note that "<RET>" represents the return key/enter.  Also, there are 
-four spaces after the "^" on the "Replace regexp:" line and no spaces
-entered on the last line.
-
-You can limit the replacement operation by selecting text first (depending
-on your version of emacs, you can either use the mouse or experiment with 
-commands such as C<C-SPC> to set the mark at the cursor location and 
-C<C-E<lt>> and C<C-E<gt>> to set the mark at the beginning and end of the
-file respectively.
-
-
-=head1 APPENDIX 2: USING MYSQL AND POSTGRESQL
-
-The main database used in this tutorial is the very simple yet powerful
-SQLite.  This section provides information that can be used to "convert"
-the tutorial to use MySQL and PostgreSQL.  However, note that part of
-the beauty of the MVC architecture is that very little database-specific
-code is spread throughout the system (at least when MVC is "done
-right").  Consequently, converting from one database to another is
-relatively painless with most Catalyst applications.  In general, you
-just need to adapt the schema definition C<.sql> file you use to
-initialize your database and adjust a few configuration parameters.
-
-Also note that the purpose of the data definition statements for this
-section are not designed to take maximum advantage of the various
-features in each database for issues such as referential integrity and
-field types/constraints.
-
-=head2 MySQL
-
-Use the following steps to adapt the tutorial to MySQL.  Thanks to Jim 
-Howard for the help.
-
-=over 4
-
-=item *
-
-Part 2: Catalyst Basics
-
-=over 4
-
-=item *
-
-Install the required software:
-
-=over 4
-
-=item *
-
-The MySQL database server and client utility.
-
-=item *
-
-The Perl C<DBD::MySQL> module
-
-=back
-
-For CentOS users (see 
-L<Catalyst::Manual::Installation::CentOS4|Catalyst::Manual::Installation::CentOS4>),
-you can use the following commands to install the software and start the MySQL
-daemon:
-
-    yum -y install mysql mysql-server
-    service mysqld start
-
-=item *
-
-Create the database and set the permissions:
-
-    $ mysql
-    Welcome to the MySQL monitor.  Commands end with ; or \g.
-    Your MySQL connection id is 2 to server version: 4.1.20
-    
-    Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
-    
-    mysql> create database myapp;
-    Query OK, 1 row affected (0.01 sec)
-    
-    mysql> grant all on myapp.* to tutorial@'localhost';
-    Query OK, 0 rows affected (0.00 sec)
-    
-    mysql> flush privileges;
-    Query OK, 0 rows affected (0.00 sec)
-    
-    mysql> quit
-    Bye
-
-=item *
-
-Create the C<.sql> file and load the data:
-
-=over 4
-
-=item *
-
-Open the C<myapp01_mysql.sql> in your editor and enter:
-
-    --
-    -- Create a very simple database to hold book and author information
-    --
-    DROP TABLE IF EXISTS books;
-    DROP TABLE IF EXISTS book_authors;
-    DROP TABLE IF EXISTS authors;
-    CREATE TABLE books (
-           id          INT(11) PRIMARY KEY AUTO_INCREMENT,
-           title       TEXT ,
-           rating      INT(11)
-    );
-    -- 'book_authors' is a many-to-many join table between books & authors
-    CREATE TABLE book_authors (
-           book_id     INT(11),
-           author_id   INT(11),
-           PRIMARY KEY (book_id, author_id)
-    );
-    CREATE TABLE authors (
-           id          INT(11) PRIMARY KEY AUTO_INCREMENT,
-           first_name  TEXT,
-           last_name   TEXT
-    );
-    ---
-    --- Load some sample data
-    ---
-    INSERT INTO books VALUES (1, 'CCSP SNRS Exam Certification Guide', 5);
-    INSERT INTO books VALUES (2, 'TCP/IP Illustrated, Volume 1', 5);
-    INSERT INTO books VALUES (3, 'Internetworking with TCP/IP Vol.1', 4);
-    INSERT INTO books VALUES (4, 'Perl Cookbook', 5);
-    INSERT INTO books VALUES (5, 'Designing with Web Standards', 5);
-    INSERT INTO authors VALUES (1, 'Greg', 'Bastien');
-    INSERT INTO authors VALUES (2, 'Sara', 'Nasseh');
-    INSERT INTO authors VALUES (3, 'Christian', 'Degu');
-    INSERT INTO authors VALUES (4, 'Richard', 'Stevens');
-    INSERT INTO authors VALUES (5, 'Douglas', 'Comer');
-    INSERT INTO authors VALUES (6, 'Tom', 'Christiansen');
-    INSERT INTO authors VALUES (7, ' Nathan', 'Torkington');
-    INSERT INTO authors VALUES (8, 'Jeffrey', 'Zeldman');
-    INSERT INTO book_authors VALUES (1, 1);
-    INSERT INTO book_authors VALUES (1, 2);
-    INSERT INTO book_authors VALUES (1, 3);
-    INSERT INTO book_authors VALUES (2, 4);
-    INSERT INTO book_authors VALUES (3, 5);
-    INSERT INTO book_authors VALUES (4, 6);
-    INSERT INTO book_authors VALUES (4, 7);
-    INSERT INTO book_authors VALUES (5, 8);
-
-=item *
-
-Load the data:
-
-    mysql -ututorial myapp < myapp01_mysql.sql
-
-=item *
-
-Make sure the data loaded correctly:
-
-    $ mysql -ututorial 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.
-    Your MySQL connection id is 4 to server version: 4.1.20
-    
-    Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
-    
-    mysql> show tables;
-    +-----------------+
-    | Tables_in_myapp |
-    +-----------------+
-    | authors         |
-    | book_authors    |
-    | books           |
-    +-----------------+
-    3 rows in set (0.00 sec)
-    
-    mysql> select * from books;
-    +----+------------------------------------+--------+
-    | id | title                              | rating |
-    +----+------------------------------------+--------+
-    |  1 | CCSP SNRS Exam Certification Guide |      5 |
-    |  2 | TCP/IP Illustrated, Volume 1       |      5 |
-    |  3 | Internetworking with TCP/IP Vol.1  |      4 |
-    |  4 | Perl Cookbook                      |      5 |
-    |  5 | Designing with Web Standards       |      5 |
-    +----+------------------------------------+--------+
-    5 rows in set (0.00 sec)
-    
-    mysql>
-
-=back
-
-=item *
-
-Update the model:
-
-=over 4
-
-=item *
-
-Delete the existing model:
-
-    rm lib/MyApp/Model/MyAppDB.pm
-
-=item *
-
-Regenerate the model using the Catalyst "_create.pl" script:
-
-    script/myapp_create.pl model MyAppDB DBIC::Schema MyAppDB dbi:mysql:myapp 'tutorial' '' '{ AutoCommit => 1 }'
-
-=back
-
-=back
-
-=item *
-
-Part 4: Authentication
-
-=over 4
-
-=item *
-
-Create the C<.sql> file for the user/roles data:
-
-Open C<myapp02_mysql.sql> in your editor and enter:
-
-    --
-    -- Add users and roles tables, along with a many-to-many join table
-    --
-    CREATE TABLE users (
-            id            INT(11) PRIMARY KEY,
-            username      TEXT,
-            password      TEXT,
-            email_address TEXT,
-            first_name    TEXT,
-            last_name     TEXT,
-            active        INT(11)
-    );
-    CREATE TABLE roles (
-            id   INTEGER PRIMARY KEY,
-            role TEXT
-    );
-    CREATE TABLE user_roles (
-            user_id INT(11),
-            role_id INT(11),
-            PRIMARY KEY (user_id, role_id)
-    );
-    --
-    -- Load up some initial test data
-    --
-    INSERT INTO users VALUES (1, 'test01', 'mypass', 't01@na.com', 'Joe',  'Blow', 1);
-    INSERT INTO users VALUES (2, 'test02', 'mypass', 't02@na.com', 'Jane', 'Doe',  1);
-    INSERT INTO users VALUES (3, 'test03', 'mypass', 't03@na.com', 'No',   'Go',   0);
-    INSERT INTO roles VALUES (1, 'user');
-    INSERT INTO roles VALUES (2, 'admin');
-    INSERT INTO user_roles VALUES (1, 1);
-    INSERT INTO user_roles VALUES (1, 2);
-    INSERT INTO user_roles VALUES (2, 1);
-    INSERT INTO user_roles VALUES (3, 1);
-
-=item *
-
-Load the user/roles data:
-
-    mysql -ututorial myapp < myapp02_mysql.sql
-
-=item *
-
-Create the C<.sql> file for the hashed password data:
-
-Open C<myapp03_mysql.sql> in your editor and enter:
-
-    --
-    -- Convert passwords to SHA-1 hashes
-    --
-    UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 1;
-    UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 2;
-    UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 3;
-
-=item *
-
-Load the user/roles data:
-
-    mysql -ututorial myapp < myapp03_mysql.sql
-
-=back
-
-=back
-
-=head2 PostgreSQL
-
-B<TODO> -- Please see the latest version of this document for possible updates:
-L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/Catalyst-Runtime/lib/Catalyst/Manual/Tutorial/Appendices.pod>
-
-
-=head1 APPENDIX 3: IMPROVED HASHING SCRIPT
-
-Here is an improved SHA-1 hashing script from Gavin Henry that does
-not expose the passwords to "capture" on the command line.
-
-    #!/usr/bin/perl -w
-    #===============================================================================
-    #
-    #         FILE:  enc_pass.pl
-    #
-    #        USAGE:  ./enc_pass.pl
-    #
-    #  DESCRIPTION:  Encrypt a Password using SHA-1
-    #
-    #      OPTIONS:  ---
-    # REQUIREMENTS:  ---
-    #         BUGS:  ---
-    #        NOTES:  ---
-    #       AUTHOR:  Gavin Henry (GH), <ghenry@suretecsystems.com>
-    #      COMPANY:  Suretec Systems Ltd.
-    #      VERSION:  1.0
-    #      CREATED:  26/06/2006
-    #     REVISION:  ---
-    #    COPYRIGHT:  http://search.cpan.org/dist/perl/pod/perlgpl.pod
-    #===============================================================================
-    
-    use strict;
-    use warnings;
-    use Digest::SHA1;
-    use Term::ReadKey;
-    
-    sub get_pass {
-        ReadMode 'noecho';
-        chomp( my $pw = ReadLine 0 );
-        ReadMode 'normal';
-        return $pw;
-    }
-    
-    print "Enter the password to be encrypted: ";
-    my $pass = get_pass();
-    
-    print "\nConfirm the password: ";
-    my $verify = get_pass();
-    
-    if ( $pass eq $verify ) {
-        my $sha1_enc = Digest::SHA1->new;
-        $sha1_enc->add($pass);
-    
-        print "\nYour encrypted password is: "
-          . $sha1_enc->hexdigest . "\n"
-          . "Paste this into your SQL INSERT/COPY Data.\n";
-    }
-    else {
-        print "\nPasswords do not match!\n";
-    }
-
-
-=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/trunk/Catalyst-Runtime/lib/Catalyst/Manual/Tutorial/>.
-
-Copyright 2006, Kennedy Clark, under Creative Commons License
-(L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
diff --git a/lib/Catalyst/Manual/Tutorial/Authentication.pod b/lib/Catalyst/Manual/Tutorial/Authentication.pod
deleted file mode 100644 (file)
index 46b061c..0000000
+++ /dev/null
@@ -1,875 +0,0 @@
-=head1 NAME
-
-Catalyst::Manual::Tutorial::Authentication - Catalyst Tutorial - Part 4: Authentication
-
-
-=head1 OVERVIEW
-
-This is B<Part 4 of 9> for the Catalyst tutorial.
-
-L<Tutorial Overview|Catalyst::Manual::Tutorial>
-
-=over 4
-
-=item 1
-
-L<Introduction|Catalyst::Manual::Tutorial::Intro>
-
-=item 2
-
-L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
-
-=item 3
-
-L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD>
-
-=item 4
-
-B<Authentication>
-
-=item 5
-
-L<Authorization|Catalyst::Manual::Tutorial::Authorization>
-
-=item 6
-
-L<Debugging|Catalyst::Manual::Tutorial::Debugging>
-
-=item 7
-
-L<Testing|Catalyst::Manual::Tutorial::Testing>
-
-=item 8
-
-L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
-
-=item 9
-
-L<Appendices|Catalyst::Manual::Tutorial::Appendices>
-
-=back
-
-
-=head1 DESCRIPTION
-
-Now that we finally have a simple yet functional application, we can
-focus on providing authentication (with authorization coming next in
-Part 5).
-
-This part 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::Intro>
-
-=head1 BASIC AUTHENTICATION
-
-This section explores how to add authentication logic to a Catalyst
-application.
-
-
-=head2 Add Users and Roles to the Database
-
-First, we add both user and role information to the database (we will
-add the role information here although it will not be used until the
-authorization section, Part 5).  Create a new SQL script file by opening
-C<myapp02.sql> in your editor and insert:
-
-    --
-    -- Add users and roles tables, along with a many-to-many join table
-    --
-    CREATE TABLE users (
-            id            INTEGER PRIMARY KEY,
-            username      TEXT,
-            password      TEXT,
-            email_address TEXT,
-            first_name    TEXT,
-            last_name     TEXT,
-            active        INTEGER
-    );
-    CREATE TABLE roles (
-            id   INTEGER PRIMARY KEY,
-            role TEXT
-    );
-    CREATE TABLE user_roles (
-            user_id INTEGER,
-            role_id INTEGER,
-            PRIMARY KEY (user_id, role_id)
-    );
-    --
-    -- Load up some initial test data
-    --
-    INSERT INTO users VALUES (1, 'test01', 'mypass', 't01@na.com', 'Joe',  'Blow', 1);
-    INSERT INTO users VALUES (2, 'test02', 'mypass', 't02@na.com', 'Jane', 'Doe',  1);
-    INSERT INTO users VALUES (3, 'test03', 'mypass', 't03@na.com', 'No',   'Go',   0);
-    INSERT INTO roles VALUES (1, 'user');
-    INSERT INTO roles VALUES (2, 'admin');
-    INSERT INTO user_roles VALUES (1, 1);
-    INSERT INTO user_roles VALUES (1, 2);
-    INSERT INTO user_roles VALUES (2, 1);
-    INSERT INTO user_roles VALUES (3, 1);
-
-Then load this into the C<myapp.db> database with the following command:
-
-    $ sqlite3 myapp.db < myapp02.sql
-
-
-=head2 Add User and Role Information to DBIC Schema
-
-This step adds DBIC-based classes for the user-related database tables
-(the role information will not be used until Part 5):
-
-Edit C<lib/MyAppDB.pm> and update the contents to match (only the
-C<MyAppDB =E<gt> [qw/Book BookAuthor Author User UserRole Role/]> line
-has changed):
-
-    package MyAppDB;
-    
-    =head1 NAME 
-    
-    MyAppDB -- DBIC Schema Class
-    
-    =cut
-    
-    # Our schema needs to inherit from 'DBIx::Class::Schema'
-    use base qw/DBIx::Class::Schema/;
-    
-    # Need to load the DB Model classes here.
-    # You can use this syntax if you want:
-    #    __PACKAGE__->load_classes(qw/Book BookAuthor Author User UserRole Role/);
-    # Also, if you simply want to load all of the classes in a directory
-    # of the same name as your schema class (as we do here) you can use:
-    #    __PACKAGE__->load_classes(qw//);
-    # But the variation below is more flexible in that it can be used to 
-    # load from multiple namespaces.
-    __PACKAGE__->load_classes({
-        MyAppDB => [qw/Book BookAuthor Author User UserRole Role/]
-    });
-    
-    1;
-
-
-=head2 Create New "Result Source Objects"
-
-Create the following three files with the content shown below.
-
-C<lib/MyAppDB/User.pm>:
-
-    package MyAppDB::User;
-    
-    use base qw/DBIx::Class/;
-    
-    # Load required DBIC stuff
-    __PACKAGE__->load_components(qw/PK::Auto Core/);
-    # Set the table name
-    __PACKAGE__->table('users');
-    # Set columns in table
-    __PACKAGE__->add_columns(qw/id username password email_address first_name last_name/);
-    # Set the primary key for the table
-    __PACKAGE__->set_primary_key('id');
-    
-    #
-    # Set relationships:
-    #
-    
-    # has_many():
-    #   args:
-    #     1) Name of relationship, DBIC will create accessor with this name
-    #     2) Name of the model class referenced by this relationship
-    #     3) Column name in *foreign* table
-    __PACKAGE__->has_many(map_user_role => 'MyAppDB::UserRole', 'user_id');
-    
-    
-    =head1 NAME
-    
-    MyAppDB::User - A model object representing a person with access to the system.
-    
-    =head1 DESCRIPTION
-    
-    This is an object that represents a row in the 'users' table of your application
-    database.  It uses DBIx::Class (aka, DBIC) to do ORM.
-    
-    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
-    Offline utilities may wish to use this class directly.
-    
-    =cut
-    
-    1;
-
-
-C<lib/MyAppDB/Role.pm>:
-
-    package MyAppDB::Role;
-    
-    use base qw/DBIx::Class/;
-    
-    # Load required DBIC stuff
-    __PACKAGE__->load_components(qw/PK::Auto Core/);
-    # Set the table name
-    __PACKAGE__->table('roles');
-    # Set columns in table
-    __PACKAGE__->add_columns(qw/id role/);
-    # Set the primary key for the table
-    __PACKAGE__->set_primary_key('id');
-    
-    #
-    # Set relationships:
-    #
-    
-    # has_many():
-    #   args:
-    #     1) Name of relationship, DBIC will create accessor with this name
-    #     2) Name of the model class referenced by this relationship
-    #     3) Column name in *foreign* table
-    __PACKAGE__->has_many(map_user_role => 'MyAppDB::UserRole', 'role_id');
-    
-    
-    =head1 NAME
-    
-    MyAppDB::Role - A model object representing a class of access permissions to 
-    the system.
-    
-    =head1 DESCRIPTION
-    
-    This is an object that represents a row in the 'roles' table of your 
-    application database.  It uses DBIx::Class (aka, DBIC) to do ORM.
-    
-    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
-    "Offline" utilities may wish to use this class directly.
-    
-    =cut
-    
-    1;
-
-
-C<lib/MyAppDB/UserRole.pm>:
-
-    package MyAppDB::UserRole;
-    
-    use base qw/DBIx::Class/;
-    
-    # Load required DBIC stuff
-    __PACKAGE__->load_components(qw/PK::Auto Core/);
-    # Set the table name
-    __PACKAGE__->table('user_roles');
-    # Set columns in table
-    __PACKAGE__->add_columns(qw/user_id role_id/);
-    # Set the primary key for the table
-    __PACKAGE__->set_primary_key(qw/user_id role_id/);
-    
-    #
-    # Set relationships:
-    #
-    
-    # belongs_to():
-    #   args:
-    #     1) Name of relationship, DBIC will create accessor with this name
-    #     2) Name of the model class referenced by this relationship
-    #     3) Column name in *this* table
-    __PACKAGE__->belongs_to(user => 'MyAppDB::User', 'user_id');
-    
-    # belongs_to():
-    #   args:
-    #     1) Name of relationship, DBIC will create accessor with this name
-    #     2) Name of the model class referenced by this relationship
-    #     3) Column name in *this* table
-    __PACKAGE__->belongs_to(role => 'MyAppDB::Role', 'role_id');
-    
-    
-    =head1 NAME
-    
-    MyAppDB::UserRole - A model object representing the JOIN between Users and Roles.
-    
-    =head1 DESCRIPTION
-    
-    This is an object that represents a row in the 'user_roles' table of your application
-    database.  It uses DBIx::Class (aka, DBIC) to do ORM.
-    
-    You probably won't need to use this class directly -- it will be automatically
-    used by DBIC where joins are needed.
-    
-    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
-    Offline utilities may wish to use this class directly.
-    
-    =cut
-    
-    1;
-
-The code for these three result source classes is obviously very familiar to the C<Book>, C<Author>, and C<BookAuthor> classes created in Part 2.
-
-
-=head2 Sanity-Check Reload of Development Server
-
-We aren't ready to try out the authentication just yet; we only want to do a quick check to be sure our model loads correctly.  Press C<Ctrl-C> to kill the previous server instance (if it's still running) and restart it:
-
-    $ script/myapp_server.pl
-
-Look for the three new model objects in the startup debug output:
-
-    ...
-     .-------------------------------------------------------------------+----------.
-    | Class                                                             | Type     |
-    +-------------------------------------------------------------------+----------+
-    | MyApp::Controller::Books                                          | instance |
-    | MyApp::Controller::Root                                           | instance |
-    | MyApp::Model::MyAppDB                                             | instance |
-    | MyApp::Model::MyAppDB::Author                                     | class    |
-    | MyApp::Model::MyAppDB::Book                                       | class    |
-    | MyApp::Model::MyAppDB::BookAuthor                                 | class    |
-    | MyApp::Model::MyAppDB::Role                                       | class    |
-    | MyApp::Model::MyAppDB::User                                       | class    |
-    | MyApp::Model::MyAppDB::UserRole                                   | class    |
-    | MyApp::View::TT                                                   | instance |
-    '-------------------------------------------------------------------+----------'
-    ...
-
-Again, notice that your "result source" classes have been "re-loaded" by Catalyst under C<MyApp::Model>.
-
-
-=head2 Include Authentication and Session Plugins
-
-Edit C<lib/MyApp.pm> and update it as follows (everything below C<StackTrace> is new):
-
-    use Catalyst qw/
-            -Debug
-            ConfigLoader
-            Static::Simple
-            
-            StackTrace
-            
-            Authentication
-            Authentication::Store::DBIC
-            Authentication::Credential::Password
-            
-            Session
-            Session::Store::FastMmap
-            Session::State::Cookie
-            /;
-
-The three C<Authentication> plugins work together to support
-Authentication while the C<Session> plugins are required to maintain
-state across multiple HTTP requests.  Note that there are several
-options for L<Session::Store|Catalyst::Plugin::Session::Store> 
-(L<Session::Store::FastMmap|Catalyst::Plugin::Session::Store::FastMmap>
-is generally a good choice if you are on Unix; try
-L<Cache::FileCache|Catalyst::Plugin::Cache::FileCache> if you are on
-Win32) -- consult L<Session::Store|Catalyst::Plugin::Session::Store> and
-its subclasses for additional information.
-
-
-=head2 Configure Authentication
-
-Although C<__PACKAGE__-E<gt>config(name =E<gt> 'value');> is still
-supported, newer Catalyst applications tend to place all configuration
-information in C<myapp.yml> and automatically load this information into
-C<MyApp-E<gt>config> using the 
-L<ConfigLoader|Catalyst::Plugin::ConfigLoader> plugin.  Here, we need
-to load several parameters that tell 
-L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication>
-where to locate information in your database.  To do this, edit the 
-C<myapp.yml> YAML and update it to match:
-
-    ---
-    name: MyApp
-    authentication:
-        dbic:
-            # Note this first definition would be the same as setting
-            # __PACKAGE__->config->{authentication}->{dbic}->{user_class} = 'MyAppDB::User'
-            # in lib/MyApp.pm (IOW, each hash key becomes a "name:" in the YAML file).
-            #
-            # This is the model object created by Catalyst::Model::DBIC from your
-            # schema (you created 'MyAppDB::User' but as the Catalyst startup
-            # debug messages show, it was loaded as 'MyApp::Model::MyAppDB::User').
-            # NOTE: Omit 'MyApp::Model' to avoid a component lookup issue in Catalyst 5.66
-            user_class: MyAppDB::User
-            # This is the name of the field in your 'users' table that contains the user's name
-            user_field: username
-            # This is the name of the field in your 'users' table that contains the password
-            password_field: password
-            # Other options can go here for hashed passwords
-
-Inline comments in the code above explain how each field is being used.
-
-B<TIP>: Although YAML uses a very simple and easy-to-ready format, it
-does require the use of a consistent level of indenting.  Be sure you
-line up everything on a given 'level' with the same number of indents.
-Also, be sure not to use C<tab> characters (YAML does not support them
-because they are handled inconsistently across editors).
-
-
-=head2 Add Login and Logout Controllers
-
-Use the Catalyst create script to create two stub controller files:
-
-    $ script/myapp_create.pl controller Login
-    $ script/myapp_create.pl controller Logout
-
-B<NOTE>: You could easily use a single controller here.  For example,
-you could have a C<User> controller with both C<login> and C<logout>
-actions.  Remember, Catalyst is designed to be very flexible, and leaves
-such matters up to you, the designer and programmer.
-
-Then open C<lib/MyApp/Controller/Login.pm>, locate the C<sub index : 
-Private> method (this was automatically inserted by the helpers when we 
-created the Login controller above), and delete this line:
-
-    $c->response->body('Matched MyApp::Controller::Login in Login.');
-
-Then update it to match:
-
-    =head2 index
-    
-    Login logic
-    
-    =cut
-    
-    sub index : Private {
-        my ($self, $c) = @_;
-    
-        # Get the username and password from form
-        my $username = $c->request->params->{username} || "";
-        my $password = $c->request->params->{password} || "";
-    
-        # If the username and password values were found in form
-        if ($username && $password) {
-            # Attempt to log the user in
-            if ($c->login($username, $password)) {
-                # If successful, then let them use the application
-                $c->response->redirect($c->uri_for('/books/list'));
-                return;
-            } else {
-                # Set an error message
-                $c->stash->{error_msg} = "Bad username or password.";
-            }
-        }
-    
-        # If either of above don't work out, send to the login page
-        $c->stash->{template} = 'login.tt2';
-    }
-
-This controller fetches the C<username> and C<password> values from the
-login form and attempts to perform a login.  If successful, it redirects
-the user to the book list page.  If the login fails, the user will stay
-at the login page but receive an error message.  If the C<username> and
-C<password> values are not present in the form, the user will be taken
-to the empty login form.
-
-Note that we could have used something like C<sub default :Private>; 
-however, the use of C<default> actions is discouraged because it does
-not receive path args as with other actions.  The recommended practice 
-is to only use C<default> in C<MyApp::Controller::Root>.
-
-Another option would be to use something like 
-C<sub base :Path :Args(0) {...}> (where the C<...> refers to the login 
-code shown in C<sub index : Private> above). We are using C<sub base 
-: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<base>, 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 : Private {
-        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->response->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=" [% Catalyst.uri_for('/login') %] ">
-      <table>
-        <tr>
-          <td>Username:</td>
-          <td><input type="text" name="username" size="40" /></td>
-        </tr>
-        <tr>
-          <td>Password:</td>
-          <td><input type="password" name="password" size="40" /></td>
-        </tr>
-        <tr>
-          <td colspan="2"><input type="submit" name="submit" value="Submit" /></td>
-        </tr>
-      </table>
-    </form>
-
-
-=head2 Add Valid User Check
-
-We need something that provides enforcement for the authentication
-mechanism -- a I<global> mechanism that prevents users who have not
-passed authentication from reaching any pages except the login page.
-This is generally done via an C<auto> action/method (prior to Catalyst
-v5.66, this sort of thing would go in C<MyApp.pm>, but starting in
-v5.66, the preferred location is C<lib/MyApp/Controller/Root.pm>).
-
-Edit the existing C<lib/MyApp/Controller/Root.pm> class file and insert
-the following method:
-
-    =head2 auto
-    
-    Check if there is a user and, if not, forward to login page
-    
-    =cut
-    
-    # Note that 'auto' runs after 'begin' but before your actions and that
-    # 'auto' "chain" (all from application path to most specific class are run)
-    # 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 anauthenticated users to reach any action in the Login
-        # controller.  To lock it down to a single action, we could use:
-        #   if ($c->action eq $c->controller('Login')->action_for('index'))
-        # to only allow unauthenticated access to the C<index> action we
-        # added above.
-        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
-            $c->log->debug('***Root::auto User not found, forwarding to /login');
-            # Redirect the user to the login page
-            $c->response->redirect($c->uri_for('/login'));
-            # Return 0 to cancel 'post-auto' processing and prevent use of application
-            return 0;
-        }
-    
-        # User found, so return 1 to continue with processing after this 'auto'
-        return 1;
-    }
-
-B<Note:> Catalyst provides a number of different types of actions, such
-as C<Local>, C<Regex>, and C<Private>.  You should refer to
-L<Catalyst::Manual::Intro> for a more detailed explanation, but the
-following bullet points provide a quick introduction:
-
-=over 4
-
-=item *
-
-The majority of application use C<Local> actions for items that respond
-to user requests and C<Private> actions for those that do not directly
-respond to user input.
-
-=item *
-
-There are five types of C<Private> actions: C<begin>, C<end>,
-C<default>, C<index>, and C<auto>.
-
-=item *
-
-With C<begin>, C<end>, C<default>, C<index> private actions, only the
-most specific action of each type will be called.  For example, if you
-define a C<begin> action in your controller it will I<override> a 
-C<begin> action in your application/root controller -- I<only> the
-action in your controller will be called.
-
-=item *
-
-Unlike the other actions where only a single method is called for each 
-request, I<every> auto action along the chain of namespaces will be 
-called.  Each C<auto> action will be called I<from the application/root
-controller down through the most specific class>.
-
-=back
-
-By placing the authentication enforcement code inside the C<auto> method
-of C<lib/MyApp/Controller/Root.pm> (or C<lib/MyApp.pm>), it will be
-called for I<every> request that is received by the entire application.
-
-
-=head2 Displaying Content Only to Authenticated Users
-
-Let's say you want to provide some information on the login page that
-changes depending on whether the user has authenticated yet.  To do
-this, open C<root/src/login.tt2> in your editor and add the following
-lines to the bottom of the file:
-
-    <p>
-    [%
-       # This code illustrates how certain parts of the TT 
-       # template will only be shown to users who have logged in
-    %]
-    [% IF Catalyst.user_exists %]
-        Please Note: You are already logged in as '[% Catalyst.user.username %]'.
-        You can <a href="[% Catalyst.uri_for('/logout') %]">logout</a> here.
-    [% ELSE %]
-        You need to log in to use this application.
-    [% END %]
-    [%#
-       Note that this whole block is a comment because the "#" appears
-       immediate after the "[%" (with no spaces in between).  Although it 
-       can be a handy way to temporarily "comment out" a whole block of 
-       TT code, it's probably a little too subtle for use in "normal" 
-       comments.
-    %]
-
-Although most of the code is comments, the middle few lines provide a
-"you are already logged in" reminder if the user returns to the login
-page after they have already authenticated.  For users who have not yet
-authenticated, a "You need to log in..." message is displayed (note the
-use of an IF-THEN-ELSE construct in TT).
-
-
-=head2 Try Out Authentication
-
-Press C<Ctrl-C> to kill the previous server instance (if it's still
-running) and restart it:
-
-    $ script/myapp_server.pl
-
-B<IMPORTANT NOTE>: If you happen to be using Internet Explorer, you may
-need to use the command C<script/myapp_server.pl -k> to enable the
-keepalive feature in the development server.  Otherwise, the HTTP
-redirect on successful login may not work correctly with IE (it seems to
-work without -k if you are running the web browser and development
-server on the same machine).  If you are using browser a browser other
-than IE, it should work either way.  If you want to make keepalive the
-default, you can edit C<script/myapp_server.pl> and change the
-initialization value for C<$keepalive> to C<1>.  (You will need to do
-this every time you create a new Catalyst application or rebuild the
-C<myapp_server.pl> script.)
-
-Now trying going to L<http://localhost:3000/books/list> and you should
-be redirected to the login page, hitting Shift+Reload if necessary (the
-"You are already logged in" message should I<not> appear -- if it does,
-click the C<logout> button and try again). Note the C<***Root::auto User
-not found...> debug message in the development server output.  Enter
-username C<test01> and password C<mypass>, and you should be taken to
-the Book List page.
-
-Open C<root/src/books/list.tt2> and add the following lines to the
-bottom:
-
-    <p>
-      <a href="[% Catalyst.uri_for('/login') %]">Login</a>
-      <a href="[% Catalyst.uri_for('form_create') %]">Create</a>
-    </p>
-
-Reload your browser and you should now see a "Login" and "Create" links 
-at the bottom of the page (as mentioned earlier, you can update template 
-files without reloading the development server).  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.
-You should stay at the login page, but the message should change to "You
-need to log in to use this application."
-
-
-=head1 USING PASSWORD HASHES
-
-In this section we increase the security of our system by converting
-from cleartext passwords to SHA-1 password hashes.
-
-B<Note:> This section is optional.  You can skip it and the rest of the
-tutorial will function normally.
-
-Note that even with the techniques shown in this section, the browser
-still transmits the passwords in cleartext to your application.  We are
-just avoiding the I<storage> of cleartext passwords in the database by
-using a SHA-1 hash. If you are concerned about cleartext passwords
-between the browser and your application, consider using SSL/TLS, made
-easy with the Catalyst plugin Catalyst::Plugin:RequireSSL.
-
-
-=head2 Get a SHA-1 Hash for the Password
-
-Catalyst uses the C<Digest> module to support a variety of hashing
-algorithms.  Here we will use SHA-1 (SHA = Secure Hash Algorithm).
-First, we should compute the SHA-1 hash for the "mypass" password we are
-using.  The following command-line Perl script provides a "quick and
-dirty" way to do this:
-
-    $ perl -MDigest::SHA -e 'print Digest::SHA::sha1_hex("mypass"), "\n"'
-    e727d1464ae12436e899a726da5b2f11d8381b26
-    $
-
-B<Note:> You should probably modify this code for production use to
-not read the password from the command line.  By having the script
-prompt for the cleartext password, it avoids having the password linger
-in forms such as your C<.bash_history> files (assuming you are using
-BASH as your shell).  An example of such a script can be found in
-Appendix 3.
-
-
-=head2 Switch to SHA-1 Password Hashes in the Database
-
-Next, we need to change the C<password> column of our C<users> table to
-store this hash value vs. the existing cleartext password.  Open
-C<myapp03.sql> in your editor and enter:
-
-    --
-    -- Convert passwords to SHA-1 hashes
-    --
-    UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 1;
-    UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 2;
-    UPDATE users SET password = 'e727d1464ae12436e899a726da5b2f11d8381b26' WHERE id = 3;
-
-Then use the following command to update the SQLite database:
-
-    $ sqlite3 myapp.db < myapp03.sql
-
-B<Note:> We are using SHA-1 hashes here, but many other hashing
-algorithms are supported.  See C<Digest> for more information.
-
-
-=head2 Enable SHA-1 Hash Passwords in
-C<Catalyst::Plugin::Authentication::Store::DBIC>
-
-Edit C<myapp.yml> and update it to match (the C<password_type> and
-C<password_hash_type> are new, everything else is the same):
-
-    ---
-    name: MyApp
-    authentication:
-        dbic:
-            # Note this first definition would be the same as setting
-            # __PACKAGE__->config->{authentication}->{dbic}->{user_class} = 'MyAppDB::User'
-            # in lib/MyApp.pm (IOW, each hash key becomes a "name:" in the YAML file).
-            #
-            # This is the model object created by Catalyst::Model::DBIC from your
-            # schema (you created 'MyAppDB::User' but as the Catalyst startup
-            # debug messages show, it was loaded as 'MyApp::Model::MyAppDB::User').
-            # NOTE: Omit 'MyApp::Model' here just as you would when using 
-            # '$c->model("MyAppDB::User)'
-            user_class: MyAppDB::User
-            # This is the name of the field in your 'users' table that contains the user's name
-            user_field: username
-            # This is the name of the field in your 'users' table that contains the password
-            password_field: password
-            # Other options can go here for hashed passwords
-            # Enabled hashed passwords
-            password_type: hashed
-            # Use the SHA-1 hashing algorithm
-            password_hash_type: SHA-1
-
-
-=head2 Try Out the Hashed Passwords
-
-Press C<Ctrl-C> to kill the previous server instance (if it's still
-running) and restart it:
-
-    $ script/myapp_server.pl
-
-You should now be able to go to L<http://localhost:3000/books/list> and
-login as before.  When done, click the "Logout" link on the login page
-(or point your browser at L<http://localhost:3000/logout>).
-
-B<Note:> If you receive the debug screen in your browser with a 
-C<Can't call method "stash" on an undefined value...> error message,
-make sure that you are using v0.07 of 
-L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL>.
-The following command can be a useful way to quickly dump the version number
-of this module on your system:
-
-    perl -MCatalyst::Plugin::Authorization::ACL -e 'print $Catalyst::Plugin::Authorization::ACL::VERSION, "\n";'
-
-
-=head1 USING THE SESSION FOR FLASH
-
-As discussed in Part 3 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
-authentication, it does leverage the same session plugins.  Now that
-those plugins are enabled, let's go back and improve the "delete
-and redirect with query parameters" code seen at the end of the
-L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD> part of the 
-tutorial.
-
-First, open C<lib/MyApp/Controller/Books.pm> and modify C<sub delete>
-to match the following:
-
-    =head2 delete 
-    
-    Delete a book
-        
-    =cut
-    
-    sub delete : Local {
-        # $id = primary key of book to delete
-        my ($self, $c, $id) = @_;
-    
-        # Search for the book and then delete it
-        $c->model('MyAppDB::Book')->search({id => $id})->delete_all;
-    
-        # 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 with status msg as an arg
-        $c->response->redirect($c->uri_for('/books/list'));
-    }
-
-Next, open C<root/lib/site/layout> and update the TT code to pull from 
-flash vs. the C<status_msg> query parameter:
-
-    <div id="header">[% PROCESS site/header %]</div>
-    
-    <div id="content">
-    <span class="message">[% status_msg || Catalyst.flash.status_msg %]</span>
-    <span class="error">[% error_msg %]</span>
-    [% content %]
-    </div>
-    
-    <div id="footer">[% PROCESS site/footer %]</div>
-
-
-=head2 Try Out Flash
-
-Restart the development server and point your browser to 
-L<http://localhost:3000/books/url_create/Test/1/4> to create an extra
-book.  Click the "Return to list" link and delete the "Test" book you
-just added.  The C<flash> mechanism should retain our "Book deleted" 
-status message across the redirect.
-
-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
-information.
-
-
-=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/trunk/Catalyst-Runtime/lib/Catalyst/Manual/Tutorial/>.
-
-Copyright 2006, Kennedy Clark, under Creative Commons License
-(L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
diff --git a/lib/Catalyst/Manual/Tutorial/Authorization.pod b/lib/Catalyst/Manual/Tutorial/Authorization.pod
deleted file mode 100644 (file)
index 9af1ce3..0000000
+++ /dev/null
@@ -1,413 +0,0 @@
-=head1 NAME
-
-Catalyst::Manual::Tutorial::Authorization - Catalyst Tutorial - Part 5: Authorization
-
-
-=head1 OVERVIEW
-
-This is B<Part 5 of 9> for the Catalyst tutorial.
-
-L<Tutorial Overview|Catalyst::Manual::Tutorial>
-
-=over 4
-
-=item 1
-
-L<Introduction|Catalyst::Manual::Tutorial::Intro>
-
-=item 2
-
-L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
-
-=item 3
-
-L<Basic CRUD|Catalyst::Manual::Tutorial_BasicCRUD>
-
-=item 4
-
-L<Authentication|Catalyst::Manual::Tutorial::Authentication>
-
-=item 5
-
-B<Authorization>
-
-=item 6
-
-L<Debugging|Catalyst::Manual::Tutorial::Debugging>
-
-=item 7
-
-L<Testing|Catalyst::Manual::Tutorial::Testing>
-
-=item 8
-
-L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
-
-=item 9
-
-L<Appendices|Catalyst::Manual::Tutorial::Appendices>
-
-=back
-
-
-
-=head1 DESCRIPTION
-
-This part of the tutorial adds role-based authorization to the existing
-authentication implemented in Part 4.  It provides simple examples of
-how to use roles in both TT templates and controller actions.  The first
-half looks at manually configured authorization.  The second half looks
-at how the ACL authorization plugin can simplify your code.
-
-You can checkout the source code for this example from the catalyst
-subversion repository as per the instructions in
-L<Catalyst::Manual::Tutorial::Intro>
-
-=head1 BASIC AUTHORIZATION
-
-In this section you learn how to manually configure authorization.
-
-=head2 Update Plugins to Include Support for Authorization
-
-Edit C<lib/MyApp.pm> and add C<Authorization::Roles> to the list:
-
-    use Catalyst qw/
-            -Debug
-            ConfigLoader
-            Static::Simple
-            
-            StackTrace
-            
-            Authentication
-            Authentication::Store::DBIC
-            Authentication::Credential::Password
-            Authorization::Roles
-            
-            Session
-            Session::Store::FastMmap
-            Session::State::Cookie
-            /;
-
-
-=head2 Add Config Information for Authorization
-
-Edit C<myapp.yml> and update it to match (everything from the
-"authorization:" line down is new):
-
-    ---
-    name: MyApp
-    authentication:
-        dbic:
-            # Note this first definition would be the same as setting
-            # __PACKAGE__->config->{authentication}->{dbic}->{user_class} = 'MyAppDB::User'
-            # in lib/MyApp.pm (IOW, each hash key becomes a "name:" in the YAML file).
-            #
-            # This is the model object created by Catalyst::Model::DBIC from your
-            # schema (you created 'MyAppDB::User' but as the Catalyst startup
-            # debug messages show, it was loaded as 'MyApp::Model::MyAppDB::User').
-            # NOTE: Omit 'MyApp::Model' here just as you would when using 
-            # '$c->model("MyAppDB::User)'
-            user_class: MyAppDB::User
-            # This is the name of the field in your 'users' table that contains the user's name
-            user_field: username
-            # This is the name of the field in your 'users' table that contains the password
-            password_field: password
-            # Other options can go here for hashed passwords
-            # Enabled hashed passwords
-            password_type: hashed
-            # Use the SHA-1 hashing algorithm
-            password_hash_type: SHA-1
-    authorization:
-        dbic:
-            # This is the model object created by Catalyst::Model::DBIC from your
-            # schema (you created 'MyAppDB::Role' but as the Catalyst startup
-            # debug messages show, it was loaded as 'MyApp::Model::MyAppDB::Role').
-            # NOTE: Omit 'MyApp::Model' here just as you would when using 
-            # '$c->model("MyAppDB::User)'
-            role_class: MyAppDB::Role
-            # The name of the field in the 'roles' table that contains the role name
-            role_field: role
-            # The name of the accessor used to map a role to the users who have this role
-            # See the has_many() in MyAppDB/Role.pm
-            role_rel: map_user_role
-            # The name of the field in the user_role table that references the user
-            user_role_user_field: user_id
-
-
-=head2 Add Role-Specific Logic to the "Book List" Template
-
-Open C<root/src/books/list.tt2> in your editor and add the following
-lines to the bottom of the file:
-
-    <p>Hello [% Catalyst.user.username %], you have the following roles:</p>
-    
-    <ul>
-      [% # Dump list of roles -%]
-      [% FOR role = Catalyst.user.roles %]<li>[% role %]</li>[% END %]
-    </ul>
-    
-    <p>
-    [% # Add some simple role-specific logic to template %]
-    [% # Use $c->check_user_roles() to check authz -%]
-    [% IF Catalyst.check_user_roles('user') %]
-      [% # Give normal users a link for 'logout' %]
-      <a href="[% Catalyst.uri_for('/logout') %]">Logout</a>
-    [% END %]
-    
-    [% # Can also use $c->user->check_roles() to check authz -%]
-    [% IF Catalyst.check_user_roles('admin') %]
-      [% # Give admin users a link for 'create' %]
-      <a href="[% Catalyst.uri_for('form_create') %]">Create</a>
-    [% END %]
-    </p>
-
-This code displays a different combination of links depending on the
-roles assigned to the user.
-
-=head2 Limit C<Books::add> to C<admin> Users
-
-C<IF> statements in TT templates simply control the output that is sent
-to the user's browser; it provides no real enforcement (if users know or
-guess the appropriate URLs, they are still perfectly free to hit any
-action within your application).  We need to enhance the controller
-logic to wrap restricted actions with role-validation logic.
-
-For example, we might want to restrict the "formless create" action to
-admin-level users by editing C<lib/MyApp/Controller/Books.pm> and
-updating C<url_create> to match the following code:
-
-    =head2 url_create
-
-    Create a book with the supplied title and rating,
-    with manual authorization
-    
-    =cut
-    
-    sub url_create : Local {
-        # In addition to self & context, get the title, rating & author_id args
-        # from the URL.  Note that Catalyst automatically puts extra information
-        # after the "/<controller_name>/<action_name/" into @_
-        my ($self, $c, $title, $rating, $author_id) = @_;
-    
-        # Check the user's roles
-        if ($c->check_user_roles('admin')) {
-            # Call create() on the book model object. Pass the table 
-            # columns/field values we want to set as hash values
-            my $book = $c->model('MyAppDB::Book')->create({
-                    title   => $title,
-                    rating  => $rating
-                });
-            
-            # Add a record to the join table for this book, mapping to 
-            # appropriate author
-            $book->add_to_book_authors({author_id => $author_id});
-            # Note: Above is a shortcut for this:
-            # $book->create_related('book_authors', {author_id => $author_id});
-            
-            # Assign the Book object to the stash for display in the view
-            $c->stash->{book} = $book;
-        
-            # This is a hack to disable XSUB processing in Data::Dumper
-            # (it's used in the view).  This is a work-around for a bug in
-            # the interaction of some versions or Perl, Data::Dumper & DBIC.
-            # You won't need this if you aren't using Data::Dumper (or if
-            # you are running DBIC 0.06001 or greater), but adding it doesn't 
-            # hurt anything either.
-            $Data::Dumper::Useperl = 1;
-        
-            # Set the TT template to use
-            $c->stash->{template} = 'books/create_done.tt2';
-        } else {
-            # Provide very simple feedback to the user
-            $c->response->body('Unauthorized!');
-        }
-    }
-
-
-To add authorization, we simply wrap the main code of this method in an
-C<if> statement that calls C<check_user_roles>.  If the user does not
-have the appropriate permissions, they receive an "Unauthorized!"
-message.  Note that we intentionally chose to display the message this
-way to demonstrate that TT templates will not be used if the response
-body has already been set.  In reality you would probably want to use a
-technique that maintains the visual continuity of your template layout
-(for example, using the "status" or "error" message feature added in
-Part 2).
-
-B<TIP>: If you want to keep your existing C<url_create> method, you can
-create a new copy and comment out the original by making it look like a
-Pod comment.  For example, put something like C<=begin> before C<sub add
-: Local {> and C<=end> after the closing C<}>.
-
-=head2 Try Out Authentication And Authorization
-
-Press C<Ctrl-C> to kill the previous server instance (if it's still
-running) and restart it:
-
-    $ script/myapp_server.pl
-
-Now trying going to L<http://localhost:3000/books/list> and you should
-be taken to the login page (you might have to C<Shift+Reload> your
-browser and/or click the "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 C<Logout> link on the
-book list page.
-
-Now the "url_create" URL will work if you are already logged in as user
-C<test01>, but receive an authorization failure if you are logged in as
-C<test02>.  Try:
-
-    http://localhost:3000/books/url_create/test/1/6
-
-while logged in as each user.  Use one of the 'Logout' links (or go to
-L<http://localhost:3000/logout> in you browser directly) when you are
-done.
-
-
-=head1 ENABLE ACL-BASED AUTHORIZATION
-
-This section takes a brief look at how the
-L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL>
-plugin can automate much of the work required to perform role-based 
-authorization in a Catalyst application.
-
-=head2 Add the C<Catalyst::Plugin::Authorization::ACL> Plugin
-
-Open C<lib/MyApp.pm> in your editor and add the following plugin to the
-C<use Catalyst> statement:
-
-    Authorization::ACL
-
-Note that the remaining C<use Catalyst> plugins from earlier sections
-are not shown here, but they should still be included.
-
-=head2 Add ACL Rules to the Application Class
-
-Open C<lib/MyApp.pm> in your editor and add the following B<BELOW> the
-C<__PACKAGE__-E<gt>setup;> statement:
-
-    # Authorization::ACL Rules
-    __PACKAGE__->deny_access_unless(
-            "/books/form_create",
-            [qw/admin/],
-        );
-    __PACKAGE__->deny_access_unless(
-            "/books/form_create_do",
-            [qw/admin/],
-        );
-    __PACKAGE__->deny_access_unless(
-            "/books/delete",
-            [qw/user admin/],
-        );
-
-Each of the three statements above comprises an ACL plugin "rule".  The
-first two rules only allow admin-level users to create new books using
-the form (both the form itself and the data submission logic are
-protected).  The third statement allows both users and admins to delete
-books.  The C</books/url_create> action will continue to be protected by
-the "manually configured" authorization created earlier in this part of
-the tutorial.
-
-The ACL plugin permits you to apply allow/deny logic in a variety of
-ways.  The following provides a basic overview of the capabilities:
-
-=over 4
-
-=item * 
-
-The ACL plugin only operates on the Catalyst "private namespace".  You
-are using the private namespace when you use C<Local> actions.  C<Path>,
-C<Regex>, and C<Global> allow you to specify actions where the path and
-the namespace differ -- the ACL plugin will not work in these cases.
-
-=item * 
-
-Each rule is expressed in a separate
-C<__PACKAGE__-E<gt>deny_access_unless()> or
-C<__PACKAGE__-E<gt>allow_access_if()> line (there are several other
-methods that can be used for more complex policies, see the C<METHODS>
-portion of the
-L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL>
-documentation for more details).
-
-=item * 
-
-Each rule can contain multiple roles but only a single path.
-
-=item * 
-
-The rules are tried in order (with the "most specific" rules tested
-first), and processing stops at the first "match" where an allow or deny
-is specified.  Rules "fall through" if there is not a "match" (where a
-"match" means the user has the specified role).  If a "match" is found,
-then processing stops there and the appropriate allow/deny action is
-taken.
-
-=item * 
-
-If none of the rules match, then access is allowed.
-
-=item * 
-
-The rules currently need to be specific in the application class
-C<lib\MyApp.pm> B<after> the C<__PACKAGE__-E<gt>setup;> line.
-
-=back
-
-=head2 Add a Method to Handle Access Violations
-
-By default,
-L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL>
-throws an exception when authorization fails.  This will take the user
-to the Catalyst debug screen, or a "Please come back later" message if
-you are not using the C<-Debug> flag. This step uses the
-C<access_denied> method in order to provide more appropriate feedback to
-the user.
-
-Open C<lib/MyApp/Controller/Books.pm> in your editor and add the
-following method:
-
-    =head2 access_denied
-    
-    Handle Catalyst::Plugin::Authorization::ACL access denied exceptions
-    
-    =cut
-    
-    sub access_denied : Private {
-        my ($self, $c) = @_;
-    
-        # Set the error message
-        $c->stash->{error_msg} = 'Unauthorized!';
-    
-        # Display the list
-        $c->forward('list');
-    }
-
-Then run the Catalyst development server script:    
-
-    $ script/myapp_server.pl
-
-Log in as C<test02>.  Once at the book list, click the "Create" link to
-try the C<form_create> action.  You should receive a red "Unauthorized!"
-error message at the top of the list.  (Note that in reality you would
-probably want to place the "Create" link code in
-C<root/src/books/list.tt2> inside an C<IF> statement that only displays
-the list to admin-level users.)  If you log in as C<test01> you should
-be able to view the C<form_create> form and add a new book.
-
-When you are done, use one of the 'Logout' links (or go to the
-L<http://localhost:3000/logout> URL directly) when you are done.
-
-
-=head1 AUTHOR
-
-Kennedy Clark, C<hkclark@gmail.com>
-
-Please report any errors, issues or suggestions to the author.  The
-most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/Catalyst-Runtime/lib/Catalyst/Manual/Tutorial/>.
-
-Copyright 2006, Kennedy Clark, under Creative Commons License
-(L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
-
diff --git a/lib/Catalyst/Manual/Tutorial/BasicCRUD.pod b/lib/Catalyst/Manual/Tutorial/BasicCRUD.pod
deleted file mode 100644 (file)
index 8c524c7..0000000
+++ /dev/null
@@ -1,568 +0,0 @@
-=head1 NAME
-
-Catalyst::Manual::Tutorial::BasicCRUD - Catalyst Tutorial - Part 3: Basic CRUD
-
-
-=head1 OVERVIEW
-
-This is B<Part 3 of 9> for the Catalyst tutorial.
-
-L<Tutorial Overview|Catalyst::Manual::Tutorial>
-
-=over 4
-
-=item 1
-
-L<Introduction|Catalyst::Manual::Tutorial::Intro>
-
-=item 2
-
-L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
-
-=item 3
-
-B<Basic CRUD>
-
-=item 4
-
-L<Authentication|Catalyst::Manual::Tutorial::Authentication>
-
-=item 5
-
-L<Authorization|Catalyst::Manual::Tutorial::Authorization>
-
-=item 6
-
-L<Debugging|Catalyst::Manual::Tutorial::Debugging>
-
-=item 7
-
-L<Testing|Catalyst::Manual::Tutorial::Testing>
-
-=item 8
-
-L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
-
-=item 9
-
-L<Appendices|Catalyst::Manual::Tutorial::Appendices>
-
-=back
-
-
-
-=head1 DESCRIPTION
-
-This part of the tutorial builds on the fairly primitive application
-created in Part 2 to add basic support for Create, Read, Update, and
-Delete (CRUD) of C<Book> objects.  Note that the 'list' function in Part
-2 already implements the Read portion of CRUD (although Read normally
-refers to reading a single object; you could implement full read
-functionality using the techniques introduced below).  This section will
-focus on the Create and Delete aspects of CRUD.  More advanced
-capabilities, including full Update functionality, will be addressed in
-Part 8.
-
-You can checkout the source code for this example from the catalyst
-subversion repository as per the instructions in
-L<Catalyst::Manual::Tutorial::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).
-
-
-=head2 Include a Create Action in the Books Controller
-
-Edit C<lib/MyApp/Controller/Books.pm> and enter the following method:
-
-    =head2 url_create
-    
-    Create a book with the supplied title, rating, and author
-    
-    =cut
-    
-    sub url_create : Local {
-        # In addition to self & context, get the title, rating, & 
-        # author_id args from the URL.  Note that Catalyst automatically 
-        # puts extra information after the "/<controller_name>/<action_name/" 
-        # into @_
-        my ($self, $c, $title, $rating, $author_id) = @_;
-    
-        # Call create() on the book model object. Pass the table 
-        # columns/field values we want to set as hash values
-        my $book = $c->model('MyAppDB::Book')->create({
-                title  => $title,
-                rating => $rating
-            });
-        
-        # Add a record to the join table for this book, mapping to 
-        # appropriate author
-        $book->add_to_book_authors({author_id => $author_id});
-        # Note: Above is a shortcut for this:
-        # $book->create_related('book_authors', {author_id => $author_id});
-        
-        # Assign the Book object to the stash for display in the view
-        $c->stash->{book} = $book;
-    
-        # This is a hack to disable XSUB processing in Data::Dumper
-        # (it's used in the view).  This is a work-around for a bug in
-        # the interaction of some versions or Perl, Data::Dumper & DBIC.
-        # You won't need this if you aren't using Data::Dumper (or if
-        # you are running DBIC 0.06001 or greater), but adding it doesn't 
-        # hurt anything either.
-        $Data::Dumper::Useperl = 1;
-    
-        # Set the TT template to use
-        $c->stash->{template} = 'books/create_done.tt2';
-    }
-
-Notice that Catalyst takes "extra slash-separated information" from the
-URL and passes it as arguments in C<@_>.  The C<url_create> action then
-uses a simple call to the DBIC C<create> method to add the requested
-information to the database (with a separate call to
-C<add_to_book_authors> to update the join table).  As do virtually all
-controller methods (at least the ones that directly handle user input),
-it then sets the template that should handle this request.
-
-
-=head2 Include a Template for the C<url_create> Action:
-
-Edit C<root/src/books/create_done.tt2> and then enter:
-
-    [% # Use the TT Dumper plugin to Data::Dumper variables to the browser   -%]
-    [% # Not a good idea for production use, though. :-)  'Indent=1' is      -%]
-    [% # optional, but prevents "massive indenting" of deeply nested objects -%]
-    [% USE Dumper(Indent=1) -%]
-    
-    [% # Set the page title.  META 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 on    -%]
-    [% # simple strings (e.g., no variable interpolation).                   -%]
-    [% 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.  This is complicated by an    -%]
-    [% # issue in TT 2.15 where blessed hash objects are not handled right.      -%]
-    [% # First, fetch 'book.authors' from the DB once.                           -%]
-    [% authors = book.authors %]
-    [% # Now use IF statements to test if 'authors.first' is "working". If so,   -%]
-    [% # we use it.  Otherwise we use a hack that seems to keep TT 2.15 happy.   -%]
-    by '[% authors.first.last_name IF authors.first; 
-           authors.list.first.value.last_name IF ! authors.first %]'
-    
-    [% # 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' -%]
-    <p><a href="[% Catalyst.uri_for('/books/list') %]">Return to list</a></p>
-    
-    [% # Try out the TT Dumper (for development only!) -%]
-    <pre>
-    Dump of the 'book' variable:
-    [% 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>
-"pretty printing" of objects and variables.  Other than that, the rest
-of the code should be familiar from the examples in Part 2.
-
-B<IMPORTANT NOTE> As mentioned earlier, the C<MyApp::View::TT.pm> view
-class created by TTSite redefines the name used to access the Catalyst
-context object in TT templates from the usual C<c> to C<Catalyst>.
-
-=head2 Try the C<url_create> Feature
-
-If the application is still running from before, use C<Ctrl-C> to kill
-it. Then restart the server:
-
-    $ script/myapp_server.pl
-
-Note that new path for C</books/url_create> appears in the startup debug
-output.
-
-B<TIP>: You can use C<script/myapp_server.pl -r> to have the development
-server auto-detect changed files and reload itself (if your browser acts
-odd, you should also try throwing in a C<-k>).  If you make changes to
-the TT templates only, you do not need to reload the development server
-(only changes to "compiled code" such as Controller and Model C<.pm>
-files require a reload).
-
-Next, use your browser to enter the following URL:
-
-    http://localhost:3000/books/url_create/TCPIP_Illustrated_Vol-2/5/4
-
-Your browser should display " Added book 'TCPIP_Illustrated_Vol-2' by
-'Stevens' with a rating of 5." along with a dump of the new book model
-object.  You should also see the following DBIC debug messages displayed
-in the development server log messages:
-
-    INSERT INTO books (rating, title) VALUES (?, ?): `5', `TCPIP_Illustrated_Vol-2'
-    INSERT INTO book_authors (author_id, book_id) VALUES (?, ?): `4', `6'
-    SELECT author.id, author.first_name, author.last_name 
-        FROM book_authors me  JOIN authors author 
-        ON ( author.id = me.author_id ) WHERE ( me.book_id = ? ): '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)>.
-
-If you then click the "Return to list" link, you should find that there
-are now six books shown (if necessary, Shift-Reload your browser at the
-C</books/list> page).
-
-Then I<add 2 more copies of the same book> so that we have some extras for
-our delete logic that will be coming up soon.  Enter the same URL above
-two more times (or refresh your browser twice if it still contains this
-URL):
-
-    http://localhost:3000/books/url_create/TCPIP_Illustrated_Vol-2/5/4
-
-You should be able to click "Return to list" and now see 3 copies of 
-"TCP_Illustrated_Vol-2".
-
-
-=head1 MANUALLY BUILDING A CREATE FORM
-
-Although the C<url_create> action in the previous step does begin to
-reveal the power and flexibility of both Catalyst and DBIC, it's
-obviously not a very realistic example of how users should be expected
-to enter data.  This section begins to address that concern.
-
-
-=head2 Add Method to Display The Form
-
-Edit C<lib/MyApp/Controller/Books.pm> and add the following method:
-
-    =head2 form_create
-    
-    Display form to collect information for book to create
-    
-    =cut
-    
-    sub form_create : Local {
-        my ($self, $c) = @_;
-    
-        # Set the TT template to use
-        $c->stash->{template} = 'books/form_create.tt2';
-    }
-
-This action simply invokes a view containing a book creation form.
-
-=head2 Add a Template for the Form
-
-Open C<root/src/books/form_create.tt2> in your editor and enter:
-
-    [% META title = 'Manual Form Book Create' -%]
-    
-    <form method="post" action="[% Catalyst.uri_for('form_create_do') %]">
-    <table>
-      <tr><td>Title:</td><td><input type="text" name="title"></td></tr>
-      <tr><td>Rating:</td><td><input type="text" name="rating"></td></tr>
-      <tr><td>Author ID:</td><td><input type="text" name="author_id"></td></tr>
-    </table>
-    <input type="submit" name="Submit" value="Submit">
-    </form>
-
-Note that we have specified the target of the form data as
-C<form_create_do>, the method created in the section that follows.
-
-=head2 Add a Method to Process Form Values and Update Database
-
-Edit C<lib/MyApp/Controller/Books.pm> and add the following method to
-save the form information to the database:
-
-    =head2 form_create_do
-    
-    Take information from form and add to database
-    
-    =cut
-    
-    sub form_create_do : Local {
-        my ($self, $c) = @_;
-    
-        # Retrieve the values from the form
-        my $title     = $c->request->params->{title}     || 'N/A';
-        my $rating    = $c->request->params->{rating}    || 'N/A';
-        my $author_id = $c->request->params->{author_id} || '1';
-    
-        # Create the book
-        my $book = $c->model('MyAppDB::Book')->create({
-                title   => $title,
-                rating  => $rating,
-            });
-        # Handle relationship with author
-        $book->add_to_book_authors({author_id => $author_id});
-    
-        # Store new model object in stash
-        $c->stash->{book} = $book;
-    
-        # Avoid Data::Dumper issue mentioned earlier
-        # You can probably omit this    
-        $Data::Dumper::Useperl = 1;
-    
-        # Set the TT template to use
-        $c->stash->{template} = 'books/create_done.tt2';
-    }
-
-
-=head2 Test Out The Form
-
-If the application is still running from before, use C<Ctrl-C> to kill
-it.  Then restart the server:
-
-    $ script/myapp_server.pl
-
-Point your browser to L<http://localhost:3000/books/form_create> and
-enter "TCP/IP Illustrated, Vol 3" for the title, a rating of 5, and an
-author ID of 4.  You should then be forwarded to the same
-C<create_done.tt2> template seen in earlier examples.  Finally, click
-"Return to list" to view the full list of books.
-
-B<Note:> Having the user enter the primary key ID for the author is
-obviously crude; we will address this concern with a drop-down list in
-Part 8.
-
-
-=head1 A SIMPLE DELETE FEATURE
-
-Turning our attention to the delete portion of CRUD, this section
-illustrates some basic techniques that can be used to remove information
-from the database.
-
-
-=head2 Include a Delete Link in the List
-
-Edit C<root/src/books/list.tt2> and update it to the following (two
-sections have changed: 1) the additional '<th>Links</th>' table header,
-and 2) the four lines for the Delete link near the bottom).
-
-    [% # This is a TT comment.  The '-' at the end "chomps" the newline.  You won't -%]
-    [% # see this "chomping" in your browser because HTML ignores blank lines, but  -%]
-    [% # it WILL eliminate a blank line if you view the HTML source.  It's purely   -%]
-    [%- # optional, but both the beginning and the ending TT tags support chomping. -%]
-    
-    [% # Provide a title to root/lib/site/header -%]
-    [% META title = 'Book List' -%]
-    
-    <table>
-    <tr><th>Title</th><th>Rating</th><th>Author(s)</th><th>Links</th></tr>
-    [% # Display each book in a table row %]
-    [% FOREACH book IN books -%]
-      <tr>
-        <td>[% book.title %]</td>
-        <td>[% book.rating %]</td>
-        <td>
-          [% # 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 does not      -%]
-          [% # a value, so nothing will be printed here.  But, if you have something -%]
-          [% # in TT that does return a method and you don't want it printed, you    -%]
-          [% # can: 1) assign it to a bogus value, or 2) use the CALL keyword to     -%]
-          [% # call it and discard the return value.                                 -%]
-          [% tt_authors = [ ];
-             tt_authors.push(author.last_name) FOREACH author = book.authors %]
-          [% # Now use a TT 'virtual method' to display the author count in parens   -%]
-          ([% tt_authors.size %])
-          [% # Use another TT vmethod to join & print the names & comma separators   -%]
-          [% tt_authors.join(', ') %]
-        </td>
-        <td>
-          [% # Add a link to delete a book %]
-          <a href="[% Catalyst.uri_for('delete/') _ book.id %]">Delete</a>
-        </td>
-      </tr>
-    [% END -%]
-    </table>
-
-The additional code is obviously designed to add a new column to the
-right side of the table with a C<Delete> "button" (for simplicity, links
-will be used instead of full HTML buttons).
-
-=head2 Add a Delete Action to the Controller
-
-Open C<lib/MyApp/Controller/Books.pm> in your editor and add the
-following method:
-
-    =head2 delete 
-    
-    Delete a book
-        
-    =cut
-    
-    sub delete : Local {
-        # $id = primary key of book to delete
-        my ($self, $c, $id) = @_;
-    
-        # Search for the book and then delete it
-        $c->model('MyAppDB::Book')->search({id => $id})->delete_all;
-    
-        # Set a status message to be displayed at the top of the view
-        $c->stash->{status_msg} = "Book deleted.";
-    
-        # Forward to the list action/method in this controller
-        $c->forward('list');
-    }
-
-This method first deletes the book with the specified primary key ID.
-However, it also removes the corresponding entry from the
-C<book_authors> table.  Note that C<delete_all> was used instead of
-C<delete>: whereas C<delete_all> also removes the join table entries in
-C<book_authors>, C<delete> does not (only use C<delete_all> if you
-really need the cascading deletes... otherwise you are wasting resources).
-
-Then, rather than forwarding to a "delete done" page as we did with the
-earlier create example, it simply sets the C<status_msg> to display a
-notification to the user as the normal list view is rendered.
-
-The C<delete> action uses the context C<forward> method to return the
-user to the book list.  The C<detach> method could have also been used.
-Whereas C<forward> I<returns> to the original action once it is
-completed, C<detach> does I<not> return.  Other than that, the two are
-equivalent.
-
-
-=head2 Try the Delete Feature
-
-If the application is still running from before, use C<Ctrl-C> to kill
-it.  Then restart the server:
-
-    $ script/myapp_server.pl
-
-Then point your browser to L<http://localhost:3000/books/list> and click
-the "Delete" link next to the first "TCPIP_Illustrated_Vol-2".  A green 
-"Book deleted" status message should display at the top of the page, 
-along with a list of the eight remaining books.
-
-
-=head2 Fixing a Dangerous URL
-
-Note the URL in your browser once you have performed the deletetion in the 
-prior step -- it is still referencing the delete action:
-
-    http://localhost:3000/books/delete/6
-
-What if the user were to press reload with this URL still active?  In
-this case the redundant delete is harmless, but in other cases this
-could clearly be extremely dangerous.
-
-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 brower 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:
-
-    =head2 delete 
-    
-    Delete a book
-        
-    =cut
-    
-    sub delete : Local {
-        # $id = primary key of book to delete
-        my ($self, $c, $id) = @_;
-    
-        # Search for the book and then delete it
-        $c->model('MyAppDB::Book')->search({id => $id})->delete_all;
-    
-        # Set a status message to be displayed at the top of the view
-        $c->stash->{status_msg} = "Book deleted.";
-    
-        # Redirect the user back to the list page
-        $c->response->redirect($c->uri_for('/books/list'));
-    }
-
-
-=head2 Try the Delete and Redirect Logic
-
-Restart the development server and point your browser to 
-L<http://localhost:3000/books/list>.  Delete the first copy of 
-"TCPIP_Illustrated_Vol-2", 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 C<uri_for> to Pass Query Parameters
-
-There are several ways to pass information across a redirect. 
-In general, the best option is to use the C<flash> technique that we
-will see in Part 4 of the 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 
-    
-    Delete a book
-        
-    =cut
-    
-    sub delete : Local {
-        # $id = primary key of book to delete
-        my ($self, $c, $id) = @_;
-    
-        # Search for the book and then delete it
-        $c->model('MyAppDB::Book')->search({id => $id})->delete_all;
-    
-        # Redirect the user back to the list page with status msg as an arg
-        $c->response->redirect($c->uri_for('/books/list', 
-            {status_msg => "Book deleted."}));
-    }
-
-This modification simply leverages the ability of C<uri_for> to include
-an arbitrary number of name/value pairs in a hash reference.  Next, we 
-need to update C<root/lib/site/layout> to handle C<status_msg> as a 
-query parameter:
-
-    <div id="header">[% PROCESS site/header %]</div>
-    
-    <div id="content">
-    <span class="message">[% status_msg || Catalyst.request.params.status_msg %]</span>
-    <span class="error">[% error_msg %]</span>
-    [% content %]
-    </div>
-    
-    <div id="footer">[% PROCESS site/footer %]</div>
-
-
-=head2 Try the Delete and Redirect With Query Param Logic
-
-Restart the development server and point your browser to 
-L<http://localhost:3000/books/list>.  Then delete the remaining copy 
-of "TCPIP_Illustrated_Vol-2".  The green "Book deleted" status message
-should return.
-
-B<NOTE:> Although this did present an opportunity to show a handy
-capability of C<uri_for>, it would be much better to use Catalyst's
-C<flash> feature in this situation.  Although the technique here is 
-less dangerous than leaving the delete URL in the client's browser, 
-we have still exposed the status message to the user.  With C<flash>, 
-this message returns to its rightful place as a service-side 
-mechanism (we will migrate this code to C<flash> in the next part 
-of the tutorial).
-
-
-=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/trunk/Catalyst-Runtime/lib/Catalyst/Manual/Tutorial/>.
-
-Copyright 2006, Kennedy Clark, under Creative Commons License
-(L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
-
diff --git a/lib/Catalyst/Manual/Tutorial/CatalystBasics.pod b/lib/Catalyst/Manual/Tutorial/CatalystBasics.pod
deleted file mode 100644 (file)
index b150990..0000000
+++ /dev/null
@@ -1,1242 +0,0 @@
-=head1 NAME
-
-Catalyst::Manual::Tutorial::CatalystBasics - Catalyst Tutorial - Part 2: Catalyst Application Development Basics
-
-
-=head1 OVERVIEW
-
-This is B<Part 2 of 9> for the Catalyst tutorial.
-
-L<Tutorial Overview|Catalyst::Manual::Tutorial>
-
-=over 4
-
-=item 1
-
-L<Introduction|Catalyst::Manual::Tutorial::Intro>
-
-=item 2
-
-B<Catalyst Basics>
-
-=item 3
-
-L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD>
-
-=item 4
-
-L<Authentication|Catalyst::Manual::Tutorial::Authentication>
-
-=item 5
-
-L<Authorization|Catalyst::Manual::Tutorial::Authorization>
-
-=item 6
-
-L<Debugging|Catalyst::Manual::Tutorial::Debugging>
-
-=item 7
-
-L<Testing|Catalyst::Manual::Tutorial::Testing>
-
-=item 8
-
-L<Advanced CRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
-
-=item 9
-
-L<Appendices|Catalyst::Manual::Tutorial::Appendices>
-
-=back
-
-
-=head1 DESCRIPTION
-
-In this part of the tutorial, we will create a very basic Catalyst web
-application.  Though simple in many respects, this section will already
-demonstrate a number of powerful capabilities such as:
-
-=over 4
-
-=item * Helper Scripts
-
-Catalyst helper scripts that can be used to rapidly bootstrap the
-skeletal structure of an application.
-
-=item * MVC
-
-Model/View/Controller (MVC) provides an architecture that facilitates a
-clean "separation of control" between the different portions of your
-application. Given that many other documents cover this subject in
-detail, MVC will not be discussed in depth here (for an excellent
-introduction to MVC and general Catalyst concepts, please see
-L<Catalyst::Manual::About>. In short:
-
-=over 4
-
-=item * Model
-
-The model usually represents a data store. In most applications, the
-model equates to the objects that are created from and saved to your SQL
-database.
-
-=item * View
-
-The view takes model objects and renders them into something for the end
-user to look at. Normally this involves a template-generation tool that
-creates HTML for the user's web browser, but it could easily be code
-that generates other forms such as PDF documents, e-mails, or Excel
-spreadsheets.
-
-=item * Controller
-
-As suggested by its name, the controller takes user requests and routes
-them to the necessary model and view.
-
-=back
-
-=item * ORM
-
-The use of Object-Relational Mapping (ORM) technology for database
-access. Specifically, ORM provides an automated and standardized means
-to persist and restore objects to/from a relational database.
-
-=back
-
-You can checkout the source code for this example from the catalyst
-subversion repository as per the instructions in
-L<Catalyst::Manual::Tutorial::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>.
-
-In the case of this tutorial, use the Catalyst C<catalyst.pl> script to
-initialize the framework for an application called C<MyApp>:
-
-    $ catalyst.pl MyApp
-    created "MyApp"
-    created "MyApp/script"
-    created "MyApp/lib"
-    created "MyApp/root"
-    ...
-    created "MyApp/script/myapp_create.pl"
-    $ cd MyApp
-
-The C<catalyst.pl> helper script will display the names of the
-directories and files it creates.
-
-Though it's too early for any significant celebration, we already have a
-functioning application. Run the following command to run this
-application with the built-in development web server:
-
-    $ script/myapp_server.pl
-    [debug] Debug messages enabled
-    [debug] Loaded plugins:
-    .----------------------------------------------------------------------------.
-    | Catalyst::Plugin::ConfigLoader  0.13                                       |
-    | Catalyst::Plugin::Static::Simple  0.14                                     |
-    '----------------------------------------------------------------------------'
-    
-    [debug] Loaded dispatcher "Catalyst::Dispatcher"
-    [debug] Loaded engine "Catalyst::Engine::HTTP"
-    [debug] Found home "/home/me/MyApp"
-    [debug] Loaded Config "/home/me/myapp.yml"
-    [debug] Loaded components:
-    .-----------------------------------------------------------------+----------.
-    | Class                                                           | Type     |
-    +-----------------------------------------------------------------+----------+
-    | MyApp::Controller::Root                                         | instance |
-    '-----------------------------------------------------------------+----------'
-    
-    [debug] Loaded Private actions:
-    .----------------------+--------------------------------------+--------------.
-    | Private              | Class                                | Method       |
-    +----------------------+--------------------------------------+--------------+
-    | /default             | MyApp::Controller::Root              | default      |
-    | /end                 | MyApp::Controller::Root              | end          |
-    '----------------------+--------------------------------------+--------------'
-    
-    [info] MyApp powered by Catalyst 5.7002
-    You can connect to your server at http://localhost: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.  It doesn't make a difference at this point, but it will as soon
-as we get the database going in the next section.
-
-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.  Information similar to the 
-following should be appended to the logging output of the development 
-server:
-
-    [info] *** Request 1 (0.043/s) [6003] [Fri Jul  7 13:32:53 2006] ***
-    [debug] "GET" request for "/" from "127.0.0.1"
-    [info] Request took 0.067675s (14.777/s)
-    .----------------------------------------------------------------+-----------.
-    | Action                                                         | Time      |
-    +----------------------------------------------------------------+-----------+
-    | /default                                                       | 0.002844s |
-    | /end                                                           | 0.000207s |
-    '----------------------------------------------------------------+-----------'
-
-Press Ctrl-C to break out of the development server.
-
-
-=head1 CREATE A SQLITE DATABASE
-
-In this step, we make a text file with the required SQL commands to
-create a database table and load some sample data.  Open C<myapp01.sql>
-in your editor and enter:
-
-    --
-    -- Create a very simple database to hold book and author information
-    --
-    CREATE TABLE books (
-            id          INTEGER PRIMARY KEY,
-            title       TEXT ,
-            rating      INTEGER
-    );
-    -- 'book_authors' is a many-to-many join table between books & authors
-    CREATE TABLE book_authors (
-            book_id     INTEGER,
-            author_id   INTEGER,
-            PRIMARY KEY (book_id, author_id)
-    );
-    CREATE TABLE authors (
-            id          INTEGER PRIMARY KEY,
-            first_name  TEXT,
-            last_name   TEXT
-    );
-    ---
-    --- Load some sample data
-    ---
-    INSERT INTO books VALUES (1, 'CCSP SNRS Exam Certification Guide', 5);
-    INSERT INTO books VALUES (2, 'TCP/IP Illustrated, Volume 1', 5);
-    INSERT INTO books VALUES (3, 'Internetworking with TCP/IP Vol.1', 4);
-    INSERT INTO books VALUES (4, 'Perl Cookbook', 5);
-    INSERT INTO books VALUES (5, 'Designing with Web Standards', 5);
-    INSERT INTO authors VALUES (1, 'Greg', 'Bastien');
-    INSERT INTO authors VALUES (2, 'Sara', 'Nasseh');
-    INSERT INTO authors VALUES (3, 'Christian', 'Degu');
-    INSERT INTO authors VALUES (4, 'Richard', 'Stevens');
-    INSERT INTO authors VALUES (5, 'Douglas', 'Comer');
-    INSERT INTO authors VALUES (6, 'Tom', 'Christiansen');
-    INSERT INTO authors VALUES (7, 'Nathan', 'Torkington');
-    INSERT INTO authors VALUES (8, 'Jeffrey', 'Zeldman');
-    INSERT INTO book_authors VALUES (1, 1);
-    INSERT INTO book_authors VALUES (1, 2);
-    INSERT INTO book_authors VALUES (1, 3);
-    INSERT INTO book_authors VALUES (2, 4);
-    INSERT INTO book_authors VALUES (3, 5);
-    INSERT INTO book_authors VALUES (4, 6);
-    INSERT INTO book_authors VALUES (4, 7);
-    INSERT INTO book_authors VALUES (5, 8);
-
-B<TIP>: See Appendix 1 for tips on removing the leading spaces when
-cutting and pasting example code from POD-based documents.
-
-Then use the following command to build a C<myapp.db> SQLite database:
-
-    $ sqlite3 myapp.db < myapp01.sql
-
-If you need to create the database more than once, you probably want to
-issue the C<rm myapp.db> command to delete the database before you use
-the C<sqlite3 myapp.db < myapp01.sql> command.
-
-Once the C<myapp.db> database file has been created and initialized, you
-can use the SQLite command line environment to do a quick dump of the
-database contents:
-
-    $ sqlite3 myapp.db
-    SQLite version 3.2.2
-    Enter ".help" for instructions
-    sqlite> select * from books;
-    1|CCSP SNRS Exam Certification Guide|5
-    2|TCP/IP Illustrated, Volume 1|5
-    3|Internetworking with TCP/IP Vol.1|4
-    4|Perl Cookbook|5
-    5|Designing with Web Standards|5
-    sqlite> .q
-    $
-
-Or:
-
-    $ sqlite3 myapp.db "select * from books"
-    1|CCSP SNRS Exam Certification Guide|5
-    2|TCP/IP Illustrated, Volume 1|5
-    3|Internetworking with TCP/IP Vol.1|4
-    4|Perl Cookbook|5
-    5|Designing with Web Standards|5
-
-As with most other SQL tools, if you are using the full "interactive"
-environment you need to terminate your SQL commands with a ";" (it's not
-required if you do a single SQL statement on the command line).  Use
-".q" to exit from SQLite from the SQLite interactive mode and return to
-your OS command prompt.
-
-
-=head1 EDIT THE LIST OF CATALYST PLUGINS
-
-One of the greatest benefits of Catalyst is that it has such a large
-library of plugins available.  Plugins are used to seamlessly integrate
-existing Perl modules into the overall Catalyst framework.  In general,
-they do this by adding additional methods to the C<context> object
-(generally written as C<$c>) that Catalyst passes to every component
-throughout the framework.
-
-By default, Catalyst enables three plugins/flags:
-
-=over 4
-
-=item * 
-
-C<-Debug> Flag
-
-Enables the Catalyst debug output you saw when we started the
-C<script/myapp_server.pl> development server earlier.  You can remove
-this plugin when you place your application into production.
-
-As you may have noticed, C<-Debug> is not a plugin, but a I<flag>.
-Although most of the items specified on the C<use Catalyst> line of your
-application class will be plugins, Catalyst supports a limited number of
-flag options (of these, C<-Debug> is the most common).  See the
-documentation for C<Catalyst.pm> to get details on other flags 
-(currently C<-Engine>, C<-Home>, and C<-Log>).
-
-If you prefer, you can use the C<$c-E<gt>debug> method to enable debug
-messages.
-
-=item *
-
-L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader>
-
-C<ConfigLoader> provides an automatic way to load configurable
-parameters for your application from a central YAML file (versus having
-the values hard-coded inside your Perl modules).  If you have not been
-exposed to YAML before, it is a human-readable data serialization format
-that can be used to read (and write) values to/from text files.  We will
-see how to use this feature of Catalyst during the authentication and
-authorization sections (Part 4 and Part 5).
-
-=item *
-
-L<Catalyst::Plugin::Static::Simple|Catalyst::Plugin::Static::Simple>
-
-C<Static::Simple> provides an easy method of serving static content such
-as images and CSS files under the development server.
-
-=back
-
-To modify the list of plugins, edit C<lib/MyApp.pm> (this file is
-generally referred to as your I<application class>) and delete the line
-with:
-
-    use Catalyst qw/-Debug ConfigLoader Static::Simple/;
-
-Replace it with:
-
-    use Catalyst qw/
-            -Debug
-            ConfigLoader
-            Static::Simple
-            
-            StackTrace
-            /;
-
-This tells Catalyst to start using one new plugin:
-
-=over 4
-
-=item * 
-
-L<Catalyst::Plugin::StackTrace|Catalyst::Plugin::StackTrace>
-
-Adds a stack trace to the standard Catalyst "debug screen" (this is the
-screen Catalyst sends to your browser when an error occurs).
-
-Note: L<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.
-
-=back
-
-Note that when specifying plugins on the C<use Catalyst> line, you can
-omit C<Catalyst::Plugin::> from the name.  Additionally, you can spread
-the plugin names across multiple lines as shown here, or place them all
-on one (or more) lines as with the default configuration.
-
-B<TIP:> You may see examples that include the
-L<Catalyst::Plugin::DefaultEnd|Catalyst::Plugin::DefaultEnd>
-plugins.  As of Catalyst 5.7000, C<DefaultEnd> has been
-deprecated in favor of 
-L<Catalyst::Action::RenderView|Catalyst::Action::RenderView>
-(as the name of the package suggests, C<RenderView> is not
-a plugin, but an action). The purpose of both is essentially the same: 
-forward processing to the view to be rendered.  Applications generated
-under 5.7000 should automatically use C<RenderView> and "just work"
-for most applications.  For more information on C<RenderView> and 
-the various options for forwarding to your view logic, please refer 
-to the "Using RenderView for the Default View" section under 
-"CATALYST VIEWS" below.
-
-
-=head1 DATABASE ACCESS WITH C<DBIx::Class>
-
-Catalyst can be used with virtually any form of persistent datastore
-available via Perl.  For example, 
-L<Catalyst::Model::DBI|Catalyst::Model::DBI> can be used to
-easily access databases through the traditional Perl C<DBI> interface.
-However, most Catalyst applications use some form of ORM technology to
-automatically create and save model objects as they are used.  Although
-Tony Bowden's L<Class::DBI|Class::DBI> has been the traditional 
-Perl ORM engine, Matt Trout's L<DBIx::Class|DBIx::Class> (abbreviated 
-as "DBIC") has rapidly emerged as the Perl-based ORM technology of choice.  
-Most new Catalyst applications rely on DBIC, as will this tutorial.
-
-Note: See L<Catalyst::Model::CDBI> for more information on using
-Catalyst with L<Class::DBI|Class::DBI>.
-
-=head2 Create a DBIC Schema File
-
-DBIx::Class uses a schema file to load other classes that represent the
-tables in your database (DBIC refers to these "table objects" as "result
-sources"; see L<DBIx::Class::ResultSource>).  In this case, we want to
-load the model object for the C<books>, C<book_authors>, and C<authors>
-tables created in the previous step.
-
-Create C<lib/MyAppDB.pm> in your editor and insert:
-
-    package MyAppDB;
-    
-    =head1 NAME 
-    
-    MyAppDB - DBIC Schema Class
-    
-    =cut
-    
-    # Our schema needs to inherit from 'DBIx::Class::Schema'
-    use base qw/DBIx::Class::Schema/;
-    
-    # Need to load the DB Model classes here.
-    # You can use this syntax if you want:
-    #    __PACKAGE__->load_classes(qw/Book BookAuthor Author/);
-    # Also, if you simply want to load all of the classes in a directory
-    # of the same name as your schema class (as we do here) you can use:
-    #    __PACKAGE__->load_classes(qw//);
-    # But the variation below is more flexible in that it can be used to 
-    # load from multiple namespaces.
-    __PACKAGE__->load_classes({
-        MyAppDB => [qw/Book BookAuthor Author/]
-    });
-    
-    1;
-
-B<Note:> C<__PACKAGE__> is just a shorthand way of referencing the name
-of the package where it is used.  Therefore, in C<MyAppDB.pm>,
-C<__PACKAGE__> is equivalent to C<MyAppDB>.
-
-B<Note:> 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 as shown above.
-
-
-=head2 Create the DBIC "Result Source" Files
-
-In this step, we create "table classes" (again, these are called a
-"result source" classes in DBIC) that act as model objects for the
-C<books>, C<book_authors>, and C<authors> tables in our database.
-
-First, create a directory to hold the class:
-
-    $ mkdir lib/MyAppDB
-
-Then create C<lib/MyAppDB/Book.pm> in your editor and enter:
-
-    package MyAppDB::Book;
-    
-    use base qw/DBIx::Class/;  
-    
-    # Load required DBIC stuff
-    __PACKAGE__->load_components(qw/PK::Auto Core/);
-    # Set the table name
-    __PACKAGE__->table('books');
-    # Set columns in table
-    __PACKAGE__->add_columns(qw/id title rating/);
-    # Set the primary key for the table
-    __PACKAGE__->set_primary_key(qw/id/);
-    
-    #
-    # Set relationships:
-    #
-    
-    # has_many():
-    #   args:
-    #     1) Name of relationship, DBIC will create accessor with this name
-    #     2) Name of the model class referenced by this relationship
-    #     3) Column name in *foreign* table
-    __PACKAGE__->has_many(book_authors => 'MyAppDB::BookAuthor', 'book_id');
-    
-    # many_to_many():
-    #   args:
-    #     1) Name of relationship, DBIC will create accessor with this name
-    #     2) Name of has_many() relationship this many_to_many() is shortcut for
-    #     3) Name of belongs_to() relationship in model class of has_many() above 
-    #   You must already have the has_many() defined to use a many_to_many().
-    __PACKAGE__->many_to_many(authors => 'book_authors', 'author');
-    
-    
-    =head1 NAME
-    
-    MyAppDB::Book - A model object representing a book.
-    
-    =head1 DESCRIPTION
-    
-    This is an object that represents a row in the 'books' table of your application
-    database.  It uses DBIx::Class (aka, DBIC) to do ORM.
-    
-    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
-    Offline utilities may wish to use this class directly.
-    
-    =cut
-    
-    1;
-
-This defines both a C<has_many> and a C<many_to_many> relationship.  The
-C<many_to_many> relationship is optional, but it makes it easier to map
-a book to its collection of authors.  Without it, we would have to
-"walk" though the C<book_authors> table as in
-C<$book-E<gt>book_authors-E<gt>first-E<gt>author-E<gt>last_name> (we
-will see examples on how to use DBIC objects in your code soon, but note
-that because C<$book-E<gt>book_authors> can return multiple authors, we
-have to use C<first> to display a single author). C<many_to_many> allows
-us to use the shorter C<$book-E<gt>authors-E<gt>first-E<gt>last_name>.
-Note that you cannot define a C<many_to_many> relationship without also
-having the C<has_many> relationship in place.
-
-Next, create C<lib/MyAppDB/Author.pm> in your editor and enter:
-
-    package MyAppDB::Author;
-    
-    use base qw/DBIx::Class/;
-    
-    # Load required DBIC stuff
-    __PACKAGE__->load_components(qw/PK::Auto Core/);
-    # Set the table name
-    __PACKAGE__->table('authors');
-    # Set columns in table
-    __PACKAGE__->add_columns(qw/id first_name last_name/);
-    # Set the primary key for the table
-    __PACKAGE__->set_primary_key(qw/id/);
-    
-    #
-    # Set relationships:
-    #
-    
-    # has_many():
-    #   args:
-    #     1) Name of relationship, DBIC will create accessor with this name
-    #     2) Name of the model class referenced by this relationship
-    #     3) Column name in *foreign* table
-    __PACKAGE__->has_many(book_author => 'MyAppDB::BookAuthor', 'author_id');
-    
-    # many_to_many():
-    #   args:
-    #     1) Name of relationship, DBIC will create accessor with this name
-    #     2) Name of has_many() relationship this many_to_many() is shortcut for
-    #     3) Name of belongs_to() relationship in model class of has_many() above 
-    #   You must already have the has_many() defined to use a many_to_many().
-    __PACKAGE__->many_to_many(books => 'book_author', 'book');
-    
-    
-    =head1 NAME
-    
-    MyAppDB::Author - A model object representing an author of a book (if a book has 
-    multiple authors, each will be represented be separate Author object).
-    
-    =head1 DESCRIPTION
-    
-    This is an object that represents a row in the 'authors' table of your application
-    database.  It uses DBIx::Class (aka, DBIC) to do ORM.
-    
-    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
-    Offline utilities may wish to use this class directly.
-    
-    =cut
-    
-    1;
-
-Finally, create C<lib/MyAppDB/BookAuthor.pm> in your editor and enter:
-
-    package MyAppDB::BookAuthor;
-    
-    use base qw/DBIx::Class/;
-    
-    # Load required DBIC stuff
-    __PACKAGE__->load_components(qw/PK::Auto Core/);
-    # Set the table name
-    __PACKAGE__->table('book_authors');
-    # Set columns in table
-    __PACKAGE__->add_columns(qw/book_id author_id/);
-    # Set the primary key for the table
-    __PACKAGE__->set_primary_key(qw/book_id author_id/);
-    
-    #
-    # Set relationships:
-    #
-    
-    # belongs_to():
-    #   args:
-    #     1) Name of relationship, DBIC will create accessor with this name
-    #     2) Name of the model class referenced by this relationship
-    #     3) Column name in *this* table
-    __PACKAGE__->belongs_to(book => 'MyAppDB::Book', 'book_id');
-    
-    # belongs_to():
-    #   args:
-    #     1) Name of relationship, DBIC will create accessor with this name
-    #     2) Name of the model class referenced by this relationship
-    #     3) Column name in *this* table
-    __PACKAGE__->belongs_to(author => 'MyAppDB::Author', 'author_id');
-    
-    
-    =head1 NAME
-    
-    MyAppDB::BookAuthor - A model object representing the JOIN between an author and 
-    a book.
-    
-    =head1 DESCRIPTION
-    
-    This is an object that represents a row in the 'book_authors' table of your 
-    application database.  It uses DBIx::Class (aka, DBIC) to do ORM.
-    
-    You probably won't need to use this class directly -- it will be automatically
-    used by DBIC where joins are needed.
-    
-    For Catalyst, this is designed to be used through MyApp::Model::MyAppDB.
-    Offline utilities may wish to use this class directly.
-    
-    =cut
-    
-    1;
-
-B<Note:> This sample application uses a plural form for the database
-tables (e.g., C<books> and C<authors>) and a singular form for the model
-objects (e.g., C<Book> and C<Author>); however, Catalyst places no
-restrictions on the naming conventions you wish to use.
-
-=head2 Use C<Catalyst::Model::DBIC::Schema> To Load The Model Class
-
-When L<Catalyst::Model::DBIC::Schema|Catalyst::Model::DBIC::Schema> is
-in use, Catalyst essentially reads an existing copy of your database
-model and creates a new set of objects under C<MyApp::Model> for use
-inside of Catalyst.
-
-B<Note:> With 
-L<Catalyst::Model::DBIC::Schema|Catalyst::Model::DBIC::Schema> you 
-essentially end up with two sets of model classes (only one of which 
-you write... the other set is created automatically in memory when 
-your Catalyst application initializes).  For this tutorial application, 
-the important points to remember are: you write the I<result source> 
-files in C<MyAppDB>, but I<within Catalyst> you use the I<automatically 
-created model classes> in C<MyApp::Model>.
-
-Use the 
-L<Catalyst::Helper::Model::DBIC::Schema|Catalyst::Helper::Model::DBIC::Schema> 
-helper script to create the model class that loads up the model we 
-created in the previous step:
-
-    $ script/myapp_create.pl model MyAppDB DBIC::Schema MyAppDB dbi:SQLite:myapp.db '' '' '{ AutoCommit => 1 }'
-     exists "/root/dev/MyApp/script/../lib/MyApp/Model"
-     exists "/root/dev/MyApp/script/../t"
-    created "/root/dev/MyApp/script/../lib/MyApp/Model/MyAppDB.pm"
-    created "/root/dev/MyApp/script/../t/model_MyAppDB.t"
-
-
-Where the first C<MyAppDB> is the name of the class to be created by the
-helper in C<lib/MyApp/Model> and the second C<MyAppDB> is the name of
-existing schema file we created (in C<lib/MyAppDB.pm>).  You can see
-that the helper creates a model file under C<lib/MyApp/Model> (Catalyst
-has a separate directory under C<lib/MyApp> for each of the three parts
-of MVC: C<Model>, C<View>, and C<Controller> [although older Catalyst
-applications often use the directories C<M>, C<V>, and C<C>]).
-
-
-=head1 CREATE A CATALYST CONTROLLER
-
-Controllers are where you write methods that interact with user
-input--typically, controller methods respond to C<GET> and C<POST>
-messages from the user's web browser.
-
-Use the Catalyst C<create> script to add a controller for book-related
-actions:
-
-    $ script/myapp_create.pl controller Books
-     exists "/root/dev/MyApp/script/../lib/MyApp/Controller"
-     exists "/root/dev/MyApp/script/../t"
-    created "/root/dev/MyApp/script/../lib/MyApp/Controller/Books.pm"
-    created "/root/dev/MyApp/script/../t/controller_Books.t"
-
-Then edit C<lib/MyApp/Controller/Books.pm> and add the following method
-to the controller:
-
-    =head2 list
-    
-    Fetch all book objects and pass to books/list.tt2 in stash to be displayed
-    
-    =cut
-     
-    sub list : Local {
-        # Retrieve the usual perl OO '$self' for this object. $c is the Catalyst
-        # 'Context' that's used to 'glue together' the various components
-        # that make up the application
-        my ($self, $c) = @_;
-    
-        # Retrieve all of the book records as book model objects and store in the
-        # stash where they can be accessed by the TT template
-        $c->stash->{books} = [$c->model('MyAppDB::Book')->all];
-        
-        # Set the TT template to use.  You will almost always want to do this
-        # in your action methods (action methods respond to user input in
-        # your controllers).
-        $c->stash->{template} = 'books/list.tt2';
-    }
-
-B<Note:> Programmers experienced with object-oriented Perl should
-recognize C<$self> as a reference to the object where this method was
-called.  On the other hand, C<$c> will be new to many Perl programmers
-who have not used Catalyst before (it's sometimes written as
-C<$context>).  The Context object is automatically passed to all
-Catalyst components.  It is used to pass information between components
-and provide access to Catalyst and plugin functionality.
-
-B<TIP>: You may see the C<$c-E<gt>model('MyAppDB::Book')> used above
-written as C<$c-E<gt>model('MyAppDB')-E<gt>resultset('Book)>.  The two
-are equivalent.
-
-B<Note:> Catalyst actions are regular Perl methods, but they make use of
-Nicholas Clark's C<attributes> module (that's the C<: Local> next to the
-C<sub list> in the code above) to provide additional information to the 
-Catalyst dispatcher logic.
-
-
-=head1 CATALYST VIEWS
-
-Views are where you render output, typically for display in the user's
-web browser, but also possibly using other display output-generation
-systems.  As with virtually every aspect of Catalyst, options abound
-when it comes to the specific view technology you adopt inside your
-application.  However, most Catalyst applications use the Template
-Toolkit, known as TT (for more information on TT, see
-L<http://www.template-toolkit.org>). Other popular view technologies
-include Mason (L<http://www.masonhq.com> and
-L<http://www.masonbook.com>) and L<HTML::Template|HTML::Template>
-(L<http://html-template.sourceforge.net>).
-
-=head2 Create a Catalyst View Using C<TTSite>
-
-When using TT for the Catalyst view, there are two main helper scripts:
-
-=over 4
-
-=item *
-
-L<Catalyst::Helper::View::TT|Catalyst::Helper::View::TT>
-
-=item *
-
-L<Catalyst::Helper::View::TTSite|Catalyst::Helper::View::TTSite>
-
-=back
-
-Both are similar, but C<TT> merely creates the C<lib/MyApp/View/TT.pm>
-file and leaves the creation of any hierarchical template organization
-entirely up to you. (It also creates a C<t/view_TT.t> file for testing;
-test cases will be discussed in Part 7). The C<TTSite> helper creates a
-modular and hierarchical view layout with separate Template Toolkit (TT)
-files for common header and footer information, configuration values, a
-CSS stylesheet, and more.
-
-Enter the following command to enable the C<TTSite> style of view
-rendering for this tutorial:
-
-    $ script/myapp_create.pl view TT TTSite
-     exists "/root/dev/MyApp/script/../lib/MyApp/View"
-     exists "/root/dev/MyApp/script/../t"
-    created "/root/dev/MyApp/script/../lib/MyApp/View/TT.pm"
-    created "/root/dev/MyApp/script/../root/lib"
-    ...
-    created "/root/dev/MyApp/script/../root/src/ttsite.css"
-
-This puts a number of files in the C<root/lib> and C<root/src>
-directories that can be used to customize the look and feel of your
-application.  Also take a look at C<lib/MyApp/View/TT.pm> for config
-values set by the C<TTSite> helper.
-
-B<TIP>: Note that TTSite does one thing that could confuse people who
-are used to the normal C<TT> Catalyst view: it redefines the Catalyst
-context object in templates from its usual C<c> to C<Catalyst>. When
-looking at other Catalyst examples, remember that they almost always use
-C<c>.  Note that Catalyst and TT I<do not complain> when you use the
-wrong name to access the context object...TT simply outputs blanks for
-that bogus logic (see next tip to change this behavior with TT C<DEBUG>
-options).  Finally, be aware that this change in name I<only>
-applies to how the context object is accessed inside your TT templates;
-your controllers will continue to use C<$c> (or whatever name you use
-when fetching the reference from C<@_> inside your methods). (You can
-change back to the "default" behavior be removing the C<CATALYST_VAR>
-line from C<lib/MyApp/View/TT.pm>, but you will also have to edit
-C<root/lib/config/main> and C<root/lib/config/url>.  If you do this, be
-careful not to have a collision between your own C<c> variable and the
-Catalyst C<c> variable.)
-
-B<TIP>: When troubleshooting TT it can be helpful to enable variable
-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/TT.pm>:
-
-    __PACKAGE__->config({
-        CATALYST_VAR => 'Catalyst',
-        ...
-        DEBUG        => 'undef',
-        ...
-    });
-
-There are a variety of options you can use, such as 'undef', 'all', 
-'service', 'context', 'parser', 'provider', and 'service'.  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:> Please be sure to disable TT debug options before 
-continuing the tutorial (especially the 'undef' option -- leaving
-this enabled will conflict with several of the conventions used
-by this tutorial and TTSite to leave some variables undefined
-on purpose).
-
-
-=head2 Using C<RenderView> for the Default View
-
-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 v5.7000 ships with a new mechanism, 
-L<Catalyst::Action::RenderView|Catalyst::Action::RenderView>, that 
-automatically performs 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') {}
-
-The following bullet points provide a quick overview of the 
-C<RenderView> process:
-
-=over 4
-
-=item *
-
-C<Root.pm> is designed to hold application-wide logic.
-
-=item *
-
-At the end of a given user request, Catalyst will call the most specific 
-C<end> method that's appropriate.  For example, if the controller for a 
-request has an C<end> method defined, it will be called.  However, if 
-the controller does not define a controller-specific C<end> method, the 
-"global" C<end> method in C<Root.pm> will be called.
-
-=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>.
-
-=item *
-
-Because C<sub end> is empty, this effectively just runs the default 
-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 
-detailed information on how to extended C<RenderView> in C<sub end>.
-
-=back
-
-
-=head3 The History Leading Up To C<RenderView>
-
-Although C<RenderView> strikes a nice balance between default
-behavior and easy extensibility, it is a new feature that won't 
-appear in most existing Catalyst examples.  This section provides
-some brief background on the evolution of default view rendering
-logic with an eye to how they can be migrated to C<RenderView>:
-
-=over 4
-
-=item *
-
-Private C<end> Action in Application Class
-
-Older Catalyst-related documents often suggest that you add a "private 
-end action" to your application class (C<MyApp.pm>) or Root.pm 
-(C<MyApp/Controller/Root.pm>).  These examples should be easily 
-converted to L<RenderView|Catalyst::Action::RenderView> by simply adding 
-the attribute C<:ActionClass('RenderView')> to the C<sub end> 
-definition. If end sub is defined in your application class 
-(C<MyApp.pm>), you should also migrate it to 
-C<MyApp/Controller/Root.pm>.
-
-=item *
-
-L<Catalyst::Plugin::DefaultEnd|Catalyst::Plugin::DefaultEnd>
-
-C<DefaultEnd> represented the "next step" in passing processing from 
-your controller to your view.  It has the advantage of only requiring 
-that C<DefaultEnd> be added to the list of plugins in C<lib/MyApp.pm>. 
-It also allowed you to add "dump_info=1" (precede with "?" or "&" 
-depending on where it is in the URL) to I<force> the debug screen at the 
-end of the Catalyst request processing cycle.  However, it was more 
-difficult to extend than the C<RenderView> mechanism, and is now 
-deprecated.
-
-=item *
-
-L<Catalyst::Action::RenderView|Catalyst::Action::RenderView>
-
-As discussed above, the current recommended approach to handling your 
-view logic relies on 
-L<Catalyst::Action::RenderView|Catalyst::Action::RenderView>.  Although 
-similar in first appearance to the "private end action" approach, it 
-utilizes Catalyst's "ActionClass" mechanism to provide both automatic 
-default behavior (you don't have to include a plugin as with 
-C<DefaultEnd>) and easy extensibility.  As with C<DefaultEnd>, it allows 
-you to add "dump_info=1" (precede with "?" or "&" depending on where it 
-is in the URL) to I<force> the debug screen at the end of the Catalyst 
-request processing cycle.
-
-=back
-
-It is recommended that all Catalyst applications use or migrate to
-the C<RenderView> approach.
-
-
-=head2 Globally Customize Every View
-
-When using TTSite, files in the subdirectories of C<root/lib> can be
-used to make changes that will appear in every view.  For example, to
-display optional status and error messages in every view, edit
-C<root/lib/site/layout>, updating it to match the following (the two HTML
-C<span> elements are new):
-
-    <div id="header">[% PROCESS site/header %]</div>
-    
-    <div id="content">
-    <span class="message">[% status_msg %]</span>
-    <span class="error">[% error_msg %]</span>
-    [% content %]
-    </div>
-    
-    <div id="footer">[% PROCESS site/footer %]</div>
-
-If we set either message in the Catalyst stash (e.g.,
-C<$c-E<gt>stash-E<gt>{status_msg} = 'Request was successful!'>) it will
-be displayed whenever any view used by that request is rendered.  The
-C<message> and C<error> CSS styles are automatically defined in
-C<root/src/ttsite.css> and can be customized to suit your needs.
-
-B<Note:> The Catalyst stash only lasts for a single HTTP request.  If
-you need to retain information across requests you can use
-L<Catalyst::Plugin::Session|Catalyst::Plugin::Session> (we will use
-Catalyst sessions in the Authentication part of the tutorial).
-
-
-=head2 Create a TT Template Page
-
-To add a new page of content to the TTSite view hierarchy, just create a
-new C<.tt2> file in C<root/src>.  Only include HTML markup that goes
-inside the HTML <body> and </body> tags, TTSite will use the contents of
-C<root/lib/site> to add the top and bottom.
-
-First create a directory for book-related TT templates:
-
-    $ mkdir root/src/books
-
-Then 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. -%]
-    
-    [% # Provide a title to root/lib/site/header -%]
-    [% META title = 'Book List' -%]
-    
-    <table>
-    <tr><th>Title</th><th>Rating</th><th>Author(s)</th></tr>
-    [% # Display each book in a table row %]
-    [% FOREACH book IN books -%]
-      <tr>
-        <td>[% book.title %]</td>
-        <td>[% book.rating %]</td>
-        <td>
-          [% # 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 does not      -%]
-          [% # a value, so nothing will be printed here.  But, if you have something -%]
-          [% # in TT that does return a method and you don't want it printed, you    -%]
-          [% # can: 1) assign it to a bogus value, or 2) use the CALL keyword to     -%]
-          [% # call it and discard the return value.                                 -%]
-          [% tt_authors = [ ];
-             tt_authors.push(author.last_name) FOREACH author = book.authors %]
-          [% # Now use a TT 'virtual method' to display the author count in parens   -%]
-          ([% tt_authors.size %])
-          [% # Use another TT vmethod to join & print the names & comma separators   -%]
-          [% tt_authors.join(', ') %]
-        </td>
-      </tr>
-    [% END -%]
-    </table>
-
-As indicated by the inline comments above, the C<META title> line uses
-TT's META feature to provide a title to C<root/lib/site/header>.
-Meanwhile, the outer C<FOREACH> loop iterates through each C<book> model
-object and prints the C<title> and C<rating> fields.  An inner
-C<FOREACH> loop prints the last name of each author in a comma-separated
-list within a single table cell.
-
-If you are new to TT, the C<[%> and C<%]> tags are used to delimit TT
-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 (C<.>) operator.
-This applies to operations as diverse as method calls, hash lookups, and
-list index values (see
-L<http://www.template-toolkit.org/docs/default/Manual/Variables.html>
-for details and examples).  In addition to the usual C<Template> module
-Pod documentation, you can access the TT manual at
-L<http://www.template-toolkit.org/docs/default/>.
-
-B<NOTE>: The C<TTSite> helper creates several TT files using an
-extension of C<.tt2>. Most other Catalyst and TT examples use an
-extension of C<.tt>.  You can use either extension (or no extension at
-all) with TTSite and TT, just be sure to use the appropriate extension
-for both the file itself I<and> the C<$c-E<gt>stash-E<gt>{template} =
-...> line in your controller.  This document will use C<.tt2> for
-consistency with the files already created by the C<TTSite> helper.
-
-
-=head1 RUN THE APPLICATION
-
-First, let's enable an environment variable option that causes
-DBIx::Class to dump the SQL statements it's using to access the database
-(this option can provide extremely helpful troubleshooting information):
-
-    $ export DBIC_TRACE=1
-
-B<NOTE>: You can also use the older 
-C<export DBIX_CLASS_STORAGE_DBI_DEBUG=1>, that that's a lot more to
-type.
-
-This assumes you are using BASH as your shell -- adjust accordingly if
-you are using a different shell (for example, under tcsh, use
-C<setenv DBIX_CLASS_STORAGE_DBI_DEBUG 1>).
-
-B<NOTE>: You can also set this in your code using
-C<$class-E<gt>storage-E<gt>debug(1);>.  See
-L<DBIx::Class::Manual::Troubleshooting> for details (including options
-to log to file instead of displaying to the Catalyst development server
-log).
-
-Then run the Catalyst "demo server" script:    
-
-    $ script/myapp_server.pl
-
-Your development server log output should display something like:
-
-    $ script/myapp_server.pl
-    [debug] Debug messages enabled
-    [debug] Loaded plugins:
-    .----------------------------------------------------------------------------.
-    | Catalyst::Plugin::ConfigLoader  0.13                                       |
-    | Catalyst::Plugin::StackTrace  0.06                                         |
-    | Catalyst::Plugin::Static::Simple  0.14                                     |
-    '----------------------------------------------------------------------------'
-    
-    [debug] Loaded dispatcher "Catalyst::Dispatcher"
-    [debug] Loaded engine "Catalyst::Engine::HTTP"
-    [debug] Found home "/home/me/MyApp"
-    [debug] Loaded Config "/home/me/myapp.yml"
-    [debug] Loaded components:
-    .-----------------------------------------------------------------+----------.
-    | Class                                                           | Type     |
-    +-----------------------------------------------------------------+----------+
-    | MyApp::Controller::Books                                        | instance |
-    | MyApp::Controller::Root                                         | instance |
-    | MyApp::Model::MyAppDB                                           | instance |
-    | MyApp::Model::MyAppDB::Author                                   | class    |
-    | MyApp::Model::MyAppDB::Book                                     | class    |
-    | MyApp::Model::MyAppDB::BookAuthor                               | class    |
-    | MyApp::View::TT                                                 | instance |
-    '-----------------------------------------------------------------+----------'
-    
-    [debug] Loaded Private actions:
-    .----------------------+--------------------------------------+--------------.
-    | Private              | Class                                | Method       |
-    +----------------------+--------------------------------------+--------------+
-    | /default             | MyApp::Controller::Root              | default      |
-    | /end                 | MyApp::Controller::Root              | end          |
-    | /books/index         | MyApp::Controller::Books             | index        |
-    | /books/list          | MyApp::Controller::Books             | list         |
-    '----------------------+--------------------------------------+--------------'
-    
-    [debug] Loaded Path actions:
-    .-------------------------------------+--------------------------------------.
-    | Path                                | Private                              |
-    +-------------------------------------+--------------------------------------+
-    | /books/list                         | /books/list                          |
-    '-------------------------------------+--------------------------------------'
-    
-    [info] MyApp powered by Catalyst 5.7002
-    You can connect to your server at http://localhost:3000
-
-Some things you should note in the output above:
-
-=over 4
-
-=item * 
-
-Catalyst::Model::DBIC::Schema took our C<MyAppDB::Book> and made it
-C<MyApp::Model::MyAppDB::Book> (and similar actions were performed on
-C<MyAppDB::Author> and C<MyAppDB::BookAuthor>).
-
-=item * 
-
-The "list" action in our Books controller showed up with a path of
-C</books/list>.
-
-=back
-
-Point your browser to L<http://localhost:3000> and you should still get
-the Catalyst welcome page.
-
-Next, to view the book list, change the URL in your browser to
-L<http://localhost:3000/books/list>. You should get a list of the five
-books loaded by the C<myapp01.sql> script above, with TTSite providing
-the formatting for the very simple output we generated in our template.
-The count and space-separated list of author last names appear on the
-end of each row.
-
-Also notice in the output of the C<script/myapp_server.pl> that DBIC
-used the following SQL to retrieve the data:
-
-    SELECT me.id, me.title, me.rating FROM books me
-
-Along with a list of the following commands to retrieve the authors for
-each book (the lines have been "word wrapped" here to improve
-legibility):
-
-    SELECT author.id, author.first_name, author.last_name 
-        FROM book_authors me  
-        JOIN authors author ON ( author.id = me.author_id ) 
-        WHERE ( me.book_id = ? ): `1'
-
-You should see 5 such lines of debug output as DBIC fetches the author 
-information for each book.
-
-
-=head1 USING THE DEFAULT TEMPLATE NAME
-
-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):
-
-    =head2 list
-    
-    Fetch all book objects and pass to books/list.tt2 in stash to be displayed
-    
-    =cut
-    
-    sub list : Local {
-        # Retrieve the usual perl OO '$self' for this object. $c is the Catalyst
-        # 'Context' that's used to 'glue together' the various components
-        # that make up the application
-        my ($self, $c) = @_;
-    
-        # Retrieve all of the book records as book model objects and store in the
-        # stash where they can be accessed by the TT template
-        $c->stash->{books} = [$c->model('MyAppDB::Book')->all];
-    
-        # Set the TT template to use.  You will almost always want to do this
-        # in your action methods (actions methods respond to user input in
-        # your controllers).
-        #$c->stash->{template} = 'books/list.tt2';
-    }
-
-C<Catalyst::View::TT> defaults to looking for a template with no 
-extension.  In our case, we need to override this to look for an 
-extension of C<.tt2>.  Open C<lib/MyApp/View/TT.pm> and add the 
-C<TEMPLATE_EXTENSION> definition as follows:
-
-    __PACKAGE__->config({
-        CATALYST_VAR => 'Catalyst',
-        INCLUDE_PATH => [
-            MyApp->path_to( 'root', 'src' ),
-            MyApp->path_to( 'root', 'lib' )
-        ],
-        PRE_PROCESS  => 'config/main',
-        WRAPPER      => 'site/wrapper',
-        ERROR        => 'error.tt2',
-        TIMER        => 0,
-        TEMPLATE_EXTENSION => '.tt2',
-    });
-
-You should now be able to restart the development server as per the 
-previous section and access the L<http://localhost:3000/books/list>
-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 Part 2 and 
-Part 8 of the Tutorial).
-
-
-=head1 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>:
-
-    $c->stash->{template} = 'books/list.tt2';
-
-Then delete the C<TEMPLATE_EXTENSION> line in  
-C<lib/MyApp/View/TT.pm>.
-
-You should then be able to restart the development server and 
-access L<http://localhost:3000/books/list> in the same manner as
-with earlier sections.
-
-
-=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/trunk/Catalyst-Runtime/lib/Catalyst/Manual/Tutorial/>.
-
-Copyright 2006, Kennedy Clark, under Creative Commons License
-(L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
-
diff --git a/lib/Catalyst/Manual/Tutorial/Debugging.pod b/lib/Catalyst/Manual/Tutorial/Debugging.pod
deleted file mode 100644 (file)
index 6a958d3..0000000
+++ /dev/null
@@ -1,303 +0,0 @@
-=head1 NAME
-
-Catalyst::Manual::Tutorial::Debugging - Catalyst Tutorial - Part 6: Debugging
-
-=head1 OVERVIEW
-
-This is B<Part 6 of 9> for the Catalyst tutorial.
-
-L<Tutorial Overview|Catalyst::Manual::Tutorial>
-
-=over 4
-
-=item 1
-
-L<Introduction|Catalyst::Manual::Tutorial::Intro>
-
-=item 2
-
-L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
-
-=item 3
-
-L<Basic CRUD|Catalyst::Manual::Tutorial_BasicCRUD>
-
-=item 4
-
-L<Authentication|Catalyst::Manual::Tutorial::Authentication>
-
-=item 5
-
-L<Authorization|Catalyst::Manual::Tutorial::Authorization>
-
-=item 6
-
-B<Debugging>
-
-=item 7
-
-L<Testing|Catalyst::Manual::Tutorial::Testing>
-
-=item 8
-
-L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
-
-=item 9
-
-L<Appendices|Catalyst::Manual::Tutorial::Appendices>
-
-=back
-
-
-=head1 DESCRIPTION
-
-This part of the tutorial takes a brief look at the primary options
-available for troubleshooting Catalyst applications.
-
-Note that when it comes to debugging and troubleshooting, there are two
-camps:
-
-=over 4
-
-=item * 
-
-Fans of C<log> and C<print> statements embedded in the code.
-
-=item * 
-
-Fans of interactive debuggers.
-
-=back
-
-Catalyst is able to easily accommodate both styles of debugging.
-
-=head1 LOG STATEMENTS
-
-Folks in the former group can use Catalyst's C<$c-E<gt>log> facility.
-(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
-with the other debug output. To accomplish the same thing in a TTSite
-view use:
-
-    [% Catalyst.log.debug("This is a test log message") %]
-
-You can also use L<Data::Dumper|Data::Dumper> in both Catalyst code 
-(C<use Data::Dumper; $c-E<gt>log-E<gt>debug("$var is: ".Dumper($var));)>) 
-and TT templates (C<[% Dumper.dump(book) %]>.
-
-=head1 RUNNING CATALYST UNDER THE PERL DEBUGGER
-
-Members of the interactive-debugger fan club will also be at home with
-Catalyst applications.  One approach to this style of Perl debugging is
-to embed breakpoints in your code.  For example, open
-C<lib/MyApp/Controller/Books.pm> in your editor and add the
-C<DB::single=1> line as follows inside the C<list> method (I like to
-"left-justify" my debug statements so I don't forget to remove them, but
-you can obviously indent them if you prefer):
-
-    sub list : Local {
-        # Retrieve the usual perl OO '$self' for this object. $c is the Catalyst
-        # 'Context' that's used to 'glue together' the various components
-        # that make up the application
-        my ($self, $c) = @_;
-    
-    $DB::single=1;
-            
-        # Retrieve all of the book records as book model objects and store in the
-        # stash where they can be accessed by the TT template
-        $c->stash->{books} = [$c->model('MyAppDB::Book')->all];
-            
-        # Set the TT template to use.  You will almost always want to do this
-        # in your action methods.
-        $c->stash->{template} = 'books/list.tt2';
-    }
-
-This causes the Perl Debugger to enter "single step mode" when this command is 
-encountered (it has no effect when Perl is run without the C<-d> flag).
-
-To now run the Catalyst development server under the Perl debugger, simply 
-prepend C<perl -d> to the front of C<script/myapp_server.pl>:
-
-    $ perl -d script/myapp_server.pl
-
-This will start the interactive debugger and produce output similar to:
-
-    $ perl -d script/myapp_server.pl  
-    
-    Loading DB routines from perl5db.pl version 1.27
-    Editor support available.
-    
-    Enter h or `h h' for help, or `man perldebug' for more help.
-    
-    main::(script/myapp_server.pl:14):      my $debug         = 0;
-    
-      DB<1> 
-
-Press the C<c> key and hit C<Enter> to continue executing the Catalyst
-development server under the debugger.  Although execution speed will be
-slightly slower than normal, you should soon see the usual Catalyst
-startup debug information.
-
-Now point your browser to L<http://localhost:3000/books/list> and log
-in.  Once the breakpoint is encountered in the
-C<MyApp::Controller::list> method, the console session running the
-development server will drop to the Perl debugger prompt:
-
-    MyApp::Controller::Books::list(/home/me/MyApp/script/../lib/MyApp/Controller/Books.pm:40):
-    40:         $c->stash->{books} = [$c->model('MyAppDB::Book')->all];
-    
-      DB<1>
-
-You now have the full Perl debugger at your disposal.  First use the
-C<next> feature by typing C<n> to execute the C<all> method on the Book
-model (C<n> jumps over method/subroutine calls; you can also use C<s> to
-C<single-step> into methods/subroutines):
-
-      DB<1> n
-    SELECT me.id, me.authors, me.title, me.rating FROM books me:
-    MyApp::Controller::Books::list(/home/me/MyApp/script/../lib/MyApp/Controller/Books.pm:44):
-    44:         $c->stash->{template} = 'books/list.tt2';
-    
-      DB<1>
-
-This takes you to the next line of code where the template name is set.
-Notice that because we enabled C<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:
-
-      DB<1> m $c->model('MyAppDB::Book')
-    ()
-    (0+
-    (bool
-    MODIFY_CODE_ATTRIBUTES
-    _attr_cache
-    _collapse_result
-    _construct_object
-    _count
-    _result_class_accessor
-    _result_source_accessor
-    all
-    carp
-    <lines removed for brevity>
-    
-      DB<2>
-
-We can also play with the model directly:
-
-      DB<2> x ($c->model('MyAppDB::Book')->all)[1]->title
-    SELECT me.id, me.title, me.rating FROM books me:
-    0  'TCP/IP Illustrated, Volume 1'
-
-This uses the Perl debugger C<x> command to display the title of a book.
-
-Next we inspect the C<books> element of the Catalyst C<stash> (the C<4>
-argument to the C<x> command limits the depth of the dump to 4 levels):
-
-      DB<3> x 4 $c->stash->{books}
-    0  ARRAY(0xa8f3b7c)
-       0  MyApp::Model::MyAppDB::Book=HASH(0xb8e702c)
-          '_column_data' => HASH(0xb8e5e2c)
-             'id' => 1
-             'rating' => 5
-             'title' => 'CCSP SNRS Exam Certification Guide'
-          '_in_storage' => 1
-    <lines removed for brevity>
-
-Then enter the C<c> command to continue processing until the next
-breakpoint is hit (or the application exits):
-
-      DB<4> c
-    SELECT author.id, author.first_name, author.last_name FROM ...
-
-Finally, press C<Ctrl+C> to break out of the development server.
-Because we are running inside the Perl debugger, you will drop to the
-debugger prompt.  Press C<q> to exit the debugger and return to your OS
-shell prompt:
-
-      DB<4> q
-    $
-
-For more information on using the Perl debugger, please see C<perldebug>
-and C<perldebtut>.  You can also type C<h> or C<h h> at the debugger
-prompt to view the built-in help screens.
-
-
-=head1 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 
-you wait for a fix on CPAN.
-
-
-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/
-
-For example, you could make a copy of 
-L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication>
-with the following command:
-
-    mkdir -p lib/Catalyst/Plugin; cp \
-        `perldoc -l Catalyst::Plugin::Authentication` lib/Catalyst/Plugin
-
-B<Note:> Matt has also suggested the following tips for Perl 
-debugging:
-
-=over 4
-
-=item * 
-
-Check the version of an installed module:
-
-    perl -MModule::Name -e 'print $Module::Name::VERSION;'
-
-For example:
-
-    $ perl -MCatalyst::Plugin::Authentication -e \
-        'print $Catalyst::Plugin::Authentication::VERSION;'
-    0.07
-
-=item * 
-
-Check if a modules contains a given method:
-
-    perl -MModule::Name -e 'print Module::Name->can("method");'
-
-For example:
-
-    $ perl -MCatalyst::Plugin::Authentication -e \
-        'print Catalyst::Plugin::Authentication->can("prepare");'
-    CODE(0x9c8db2c)
-
-If the method exists, the Perl C<can> method returns a coderef.
-Otherwise, it returns undef and nothing will be printed.
-
-=back
-
-
-=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/trunk/Catalyst-Runtime/lib/Catalyst/Manual/Tutorial/>.
-
-Copyright 2006, Kennedy Clark, under Creative Commons License
-(L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
diff --git a/lib/Catalyst/Manual/Tutorial/Intro.pod b/lib/Catalyst/Manual/Tutorial/Intro.pod
deleted file mode 100644 (file)
index e409740..0000000
+++ /dev/null
@@ -1,342 +0,0 @@
-=head1 NAME
-
-Catalyst::Manual::Tutorial::Intro - Catalyst Tutorial - Part 1: Introduction
-
-
-=head1 OVERVIEW
-
-This is B<Part 1 of 9> of the Catalyst Tutorial.
-
-L<Tutorial Overview|Catalyst::Manual::Tutorial>
-
-=over 4
-
-=item 1
-
-B<Introduction>
-
-=item 2
-
-L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
-
-=item 3
-
-L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD>
-
-=item 4
-
-L<Authentication|Catalyst::Manual::Tutorial::Authentication>
-
-=item 5
-
-L<Authorization|Catalyst::Manual::Tutorial::Authorization>
-
-=item 6
-
-L<Debugging|Catalyst::Manual::Tutorial::Debugging>
-
-=item 7
-
-L<Testing|Catalyst::Manual::Tutorial::Testing>
-
-=item 8
-
-L<Advanced CRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
-
-=item 9
-
-L<Appendices|Catalyst::Manual::Tutorial::Appendices>
-
-=back
-
-=head1 DESCRIPTION
-
-This tutorial provides a multipart introduction to the Catalyst web
-framework. It seeks to provide a rapid overview of many of its most
-commonly used features. The focus is on the real-world best practices
-required in the construction of nearly all Catalyst applications.
-
-Although the primary target of the tutorial is users new to the Catalyst
-framework, experienced users may wish to review specific sections (for
-example, how to use DBIC for their model classes or how to add
-authentication and authorization to an existing application).
-
-You can obtain the code for all the tutorial examples from the
-catalyst subversion repository by issuing the command:
-
- svn co http://dev.catalyst.perl.org/repos/Catalyst/tags/examples/Tutorial/MyApp/5.7/ CatalystTutorial
-
-This will download the current code for each tutorial chapter in the
-CatalystTutorial directory.  Each example application directory has
-the same name as the tutorial chapter.
-
-Subjects covered include:
-
-=over 4
-
-=item * 
-
-A simple application that lists and adds books.
-
-=item *
-
-The use of L<DBIx::Class|DBIx::Class> (DBIC) for the model.
-
-=item * 
-
-How to write CRUD (Create, Read, Update, and Delete) operations in
-Catalyst.
-
-=item *
-
-Authentication ("auth").
-
-=item * 
-
-Role-based authorization ("authz").
-
-=item * 
-
-Attempts to provide an example showing current (5.7XXX) 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.yml>, the use of C<lib/MyApp/Controller/Root.pm> 
-vs. C<lib/MyApp.pm>, etc.
-
-=item * 
-
-The use of Template Toolkit (TT) and the
-L<Catalyst::Helper::View::TTSite|Catalyst::Helper::View::TTSite> 
-view helper.
-
-=item * 
-
-Useful techniques for troubleshooting and debugging Catalyst
-applications.
-
-=item * 
-
-The use of SQLite as a database (with code also provided for MySQL and
-PostgreSQL).
-
-=item * 
-
-The use of L<HTML::Widget|HTML::Widget> for automated form processing 
-and validation.
-
-=back
-
-This tutorial makes the learning process its main priority.  For
-example, the level of comments in the code found here would likely be
-considered excessive in a "normal project".  Because of their contextual
-value, this tutorial will generally favor inline comments over a
-separate discussion in the text.  It also deliberately tries to
-demonstrate multiple approaches to various features (in general, you
-should try to be as consistent as possible with your own production
-code).
-
-Furthermore, this tutorial tries to minimize the number of controllers,
-models, TT templates, and database tables.  Although this does result in
-things being a bit contrived at times, the concepts should be applicable
-to more complex environments.  More complete and complicated example
-applications can be found in the C<examples> area of the Catalyst
-Subversion repository at
-L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/>.
-
-B<Note:> There are a variety of other introductory materials available
-through the Catalyst web site and at
-L<http://dev.catalyst.perl.org/wiki/UserIntroductions> and
-L<http://dev.catalyst.perl.org/>.
-
-=head1 VERSIONS AND CONVENTIONS USED IN THIS TUTORIAL
-
-This tutorial was built using the following resources. Please note that
-you may need to make adjustments for different environments and
-versions:
-
-=over 4
-
-=item * 
-
-OS = CentOS 4 Linux (RHEL 4)
-
-=item * 
-
-Catalyst v5.6902
-
-=item * 
-
-DBIx::Class v0.06003
-
-=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 * 
-
-Catalyst::Plugin::Authentication -- 0.09
-
-=item *
-
-Catalyst::Plugin::Authentication::Store::DBIC -- 0.07
-
-=item *
-
-Catalyst::Plugin::Authorization::ACL -- 0.08
-
-=item *
-
-Catalyst::Plugin::Authorization::Roles -- 0.04
-
-=item *
-
-Catalyst::Plugin::ConfigLoader -- 0.13
-
-=item *
-
-Catalyst::Plugin::HTML::Widget -- 1.1
-
-=item *
-
-Catalyst::Plugin::Session -- 0.12
-
-=item *
-
-Catalyst::Plugin::Session::State::Cookie -- 0.05
-
-=item *
-
-Catalyst::Plugin::Session::Store::FastMmap -- 0.02
-
-=item *
-
-Catalyst::Plugin::StackTrace -- 0.06
-
-=item *
-
-Catalyst::Plugin::Static::Simple -- 0.14
-
-=back
-
-=item * 
-
-Since the web browser is being used on the same box where Perl and the
-Catalyst development server is running, the URL of
-C<http://localhost:3000> will be used (the Catalyst development server
-defaults to port 3000).  If you are running Perl on a different box than
-where your web browser is located (or using a different port number via
-the C<-p> I<port_number> option to the development server), then you
-will need to update the URL you use accordingly.
-
-=item * 
-
-Depending on the web browser you are using, you might need to hit
-C<Shift+Reload> to pull a fresh page when testing your application at
-various points.  Also, the C<-k> keepalive option to the development
-server can be necessary with some browsers (especially Internet
-Explorer).
-
-=back
-
-=head1 CATALYST INSTALLATION
-
-Unfortunately, one of the most daunting tasks faced by newcomers to
-Catalyst is getting it installed.  Although a compelling strength of
-Catalyst is that it can easily make use of many of the modules in the
-vast repository that is CPAN, this can result in initial installations
-that are both time consuming and frustrating.  However, there are a
-growing number of methods that can dramatically ease this undertaking.
-Of these, the following are likely to be applicable to the largest
-number of potential new users:
-
-=over 4
-
-=item * 
-
-Matt Trout's C<cat-install>
-
-Available at L<http://www.shadowcatsystems.co.uk/static/cat-install>,
-C<cat-install> can be a quick and painless way to get Catalyst up and
-running.  Just download the script from the link above and type C<perl
-cat-install>.
-
-=item * 
-
-Chris Laco's CatInABox
-
-Download the tarball from
-L<http://handelframework.com/downloads/CatInABox.tar.gz> and unpack it
-on your machine.  Depending on your OS platform, either run C<start.bat>
-or C<start.sh>.
-
-=item * 
-
-Pre-Built VMWare Images
-
-Under the VMWare community program, work is ongoing to develop a number
-of VMWare images where an entire Catalyst development environment has
-already been installed, complete with database engines and a full
-complement of Catalyst plugins.
-
-=back
-
-For additional information and recommendations on Catalyst installation,
-please refer to 
-L<Catalyst::Manual::Installation|Catalyst::Manual::Installation>.
-
-B<NOTE:> Step-by-step instructions to replicate the environment on
-which this tutorial was developed can be found at
-L<Catalyst::Manual::Installation::CentOS4|Catalyst::Manual::Installation::CentOS4>. 
-Using these instructions, you should be able to build a complete CentOS
-4.X server with Catalyst and all the plugins required to run this
-tutorial.
-
-=head1 DATABASES
-
-This tutorial will primarily focus on SQLite because of its simplicity
-of installation and use; however, modifications in the script required
-to support MySQL and PostgreSQL will be presented in Appendix 2.
-
-B<Note:> One of the advantages of the MVC design patterns is that
-applications become much more database independent.  As such, you will
-notice that only the C<.sql> files used to initialize the database
-change between database systems: the Catalyst code generally remains the
-same.
-
-=head1 WHERE TO GET WORKING CODE
-
-Each part of the tutorial has complete code available in the main
-Catalyst Subversion repository (see the note at the beginning of each
-part for the appropriate svn command to use).  Additionally, the final
-code is available as a ready-to-run tarball at
-L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/Tutorial/Final_Tarball/MyApp.tgz>.
-
-B<NOTE:> You can run the test cases for the final code with the following 
-commands:
-
-    wget http://dev.catalyst.perl.org/repos/Catalyst/trunk/examples/Tutorial/Final_Tarball/MyApp.tgz
-    tar zxvf MyApp.tgz
-    cd MyApp
-    CATALYST_DEBUG=0 prove --lib lib  t
-
-
-=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/trunk/Catalyst-Runtime/lib/Catalyst/Manual/Tutorial/>.
-
-Copyright 2006, Kennedy Clark, under Creative Commons License
-(L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
-
-
diff --git a/lib/Catalyst/Manual/Tutorial/Testing.pod b/lib/Catalyst/Manual/Tutorial/Testing.pod
deleted file mode 100644 (file)
index f3b2b64..0000000
+++ /dev/null
@@ -1,351 +0,0 @@
-=head1 NAME
-
-Catalyst::Manual::Tutorial::Testing - Catalyst Tutorial - Part 7: Testing
-
-
-=head1 OVERVIEW
-
-This is B<Part 7 of 9> for the Catalyst tutorial.
-
-L<Tutorial Overview|Catalyst::Manual::Tutorial>
-
-=over 4
-
-=item 1
-
-L<Introduction|Catalyst::Manual::Tutorial::Intro>
-
-=item 2
-
-L<Catalyst Basics|Catalyst::Manual::Tutorial::CatalystBasics>
-
-=item 3
-
-L<Basic CRUD|Catalyst::Manual::Tutorial_BasicCRUD>
-
-=item 4
-
-L<Authentication|Catalyst::Manual::Tutorial::Authentication>
-
-=item 5
-
-L<Authorization|Catalyst::Manual::Tutorial::Authorization>
-
-=item 6
-
-L<Debugging|Catalyst::Manual::Tutorial::Debugging>
-
-=item 7
-
-B<Testing>
-
-=item 8
-
-L<AdvancedCRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
-
-=item 9
-
-L<Appendices|Catalyst::Manual::Tutorial::Appendices>
-
-=back
-
-=head1 DESCRIPTION
-
-You may have noticed that the Catalyst Helper scripts automatically
-create basic C<.t> test scripts under the C<t> directory.  This part of
-the tutorial briefly looks at how these tests can be used to not only
-ensure that your application is working correctly at the present time,
-but also provide automated regression testing as you upgrade various
-pieces of your application over time.
-
-You can checkout the source code for this example from the catalyst
-subversion repository as per the instructions in
-L<Catalyst::Manual::Tutorial::Intro>
-
-=head1 RUNNING THE "CANNED" CATALYST TESTS
-
-There are a variety of ways to run Catalyst and Perl tests (for example,
-C<perl Makefile.PL> and C<make test>), but one of the easiest is with the
-C<prove> command.  For example, to run all of the tests in the C<t>
-directory, enter:
-
-    $ prove --lib lib t
-
-The redirection used by the Authentication plugins will cause the
-default C<t/01app.t> to fail.  You can fix this by changing the line in
-C<t/01app.t> that read:
-
-    ok( request('/')->is_success, 'Request should succeed' );
-
-to:
-
-    ok( request('/login')->is_success, 'Request should succeed' );
-
-So that a redirect is not necessary.  Also, the C<t/controller_Books.t>
-and C<t/controller_Logout.t> default test cases will fail because of the
-authorization.  You can delete these two files to prevent false error
-messages:
-
-    $ rm t/controller_Books.t
-    $ rm t/controller_Logout.t
-
-As you can see in the C<prove> command line above, the C<--lib> option
-is used to set the location of the Catalyst C<lib> directory.  With this
-command, you will get all of the usual development server debug output,
-something most people prefer to disable while running tests cases.
-Although you can edit the C<lib/MyApp.pm> to comment out the C<-Debug>
-plugin, it's generally easier to simply set the C<CATALYST_DEBUG=0>
-environment variable.  For example:
-
-    $ CATALYST_DEBUG=0 prove --lib lib t
-
-During the C<t/02pod> and C<t/03podcoverage> tests, you might notice the
-C<all skipped: set TEST_POD to enable this test> warning message.  To
-execute the Pod-related tests, add C<TEST_POD=1> to the C<prove>
-command:
-
-    $ CATALYST_DEBUG=0 TEST_POD=1 prove --lib lib t
-
-If you omitted the Pod comments from any of the methods that were
-inserted, you might have to go back and fix them to get these tests to
-pass. :-)
-
-Another useful option is the C<verbose> (C<-v>) option to C<prove>.  It
-prints the name of each test case as it is being run:
-
-    $ CATALYST_DEBUG=0 TEST_POD=1 prove --lib lib -v t
-
-=head1 RUNNING A SINGLE TEST
-
-You can also run a single script by appending its name to the C<prove>
-command. For example:
-
-    $ CATALYST_DEBUG=0 prove --lib lib t/01app.t
-
-Note that you can also run tests directly from Perl without C<prove>.
-For example:
-
-    $ CATALYST_DEBUG=0 perl -Ilib t/01app.t
-
-=head1 ADDING YOUR OWN TEST SCRIPT
-
-Although the Catalyst helper scripts provide a basic level of checks
-"for free," testing can become significantly more helpful when you write
-your own script to exercise the various parts of your application.  The
-L<Test::WWW::Mechanize::Catalyst|Test::WWW::Mechanize::Catalyst> module 
-is very popular for writing these sorts of test cases.  This module 
-extends L<Test::WWW::Mechanize|Test::WWW::Mechanize> (and therefore 
-L<WWW::Mechanize|WWW::Mechanize>) to allow you to automate the action of
-a user "clicking around" inside your application.  It gives you all the
-benefits of testing on a live system without the messiness of having to
-use an actual web server, 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
-    
-    use strict;
-    use warnings;
-    
-    # Load testing framework and use 'no_plan' to dynamically pick up
-    # all tests. Better to replace "'no_plan'" with "tests => 30" so it
-    # knows exactly how many tests need to be run (and will tell you if
-    # not), but 'no_plan' is nice for quick & dirty tests
-    
-    use Test::More 'no_plan';
-    
-    # Need to specify the name of your app as arg on next line
-    # Can also do:
-    #   use Test::WWW::Mechanize::Catalyst "MyApp";
-    
-    use ok "Test::WWW::Mechanize::Catalyst" => "MyApp";
-        
-    # Create two 'user agents' to simulate two different users ('test01' & 'test02')
-    my $ua1 = Test::WWW::Mechanize::Catalyst->new;
-    my $ua2 = Test::WWW::Mechanize::Catalyst->new;
-    
-    # Use a simplified for loop to do tests that are common to both users
-    # Use get_ok() to make sure we can hit the base URL
-    # Second arg = optional description of test (will be displayed for failed tests)
-    # Note that in test scripts you send everything to 'http://localhost'
-    $_->get_ok("http://localhost/", "Check redirect of base URL") for $ua1, $ua2;
-    # Use title_is() to check the contents of the <title>...</title> tags
-    $_->title_is("Login", "Check for login title") for $ua1, $ua2;
-    # Use content_contains() to match on text in the html body
-    $_->content_contains("You need to log in to use this application",
-        "Check we are NOT logged in") for $ua1, $ua2;
-    
-    # Log in as each user
-    # Specify username and password on the URL
-    $ua1->get_ok("http://localhost/login?username=test01&password=mypass", "Login 'test01'");
-    # Use the form for user 'test02'; note there is no description here
-    $ua2->submit_form(
-        fields => {
-            username => 'test02',
-            password => 'mypass',
-        });
-    
-    # Go back to the login page and it should show that we are already logged in
-    $_->get_ok("http://localhost/login", "Return to '/login'") for $ua1, $ua2;
-    $_->title_is("Login", "Check for login page") for $ua1, $ua2;
-    $_->content_contains("Please Note: You are already logged in as ",
-        "Check we ARE logged in" ) for $ua1, $ua2;
-    
-    # 'Click' the 'Logout' link (see also 'text_regex' and 'url_regex' options)
-    $_->follow_link_ok({n => 1}, "Logout via first link on page") for $ua1, $ua2;
-    $_->title_is("Login", "Check for login title") for $ua1, $ua2;
-    $_->content_contains("You need to log in to use this application",
-        "Check we are NOT logged in") for $ua1, $ua2;
-    
-    # Log back in
-    $ua1->get_ok("http://localhost/login?username=test01&password=mypass", "Login 'test01'");
-    $ua2->get_ok("http://localhost/login?username=test02&password=mypass", "Login 'test02'");
-    # Should be at the Book List page... do some checks to confirm
-    $_->title_is("Book List", "Check for book list title") for $ua1, $ua2;
-    
-    $ua1->get_ok("http://localhost/books/list", "'test01' book list");
-    $ua1->get_ok("http://localhost/login", "Login Page");
-    $ua1->get_ok("http://localhost/books/list", "'test01' book list");
-    
-    $_->content_contains("Book List", "Check for book list title") for $ua1, $ua2;
-    # Make sure the appropriate logout buttons are displayed
-    $_->content_contains("/logout\">Logout</a>",
-        "Both users should have a 'User Logout'") for $ua1, $ua2;
-    $ua1->content_contains("/books/form_create\">Create</a>",
-        "Only 'test01' should have a create link");
-    
-    $ua1->get_ok("http://localhost/books/list", "View book list as 'test01'");
-    
-    # User 'test01' should be able to create a book with the "formless create" URL
-    $ua1->get_ok("http://localhost/books/url_create/TestTitle/2/4",
-        "'test01' formless create");
-    $ua1->title_is("Book Created", "Book created title");
-    $ua1->content_contains("Added book 'TestTitle'", "Check title added OK");
-    $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");
-    
-    # Make sure the new book shows in the list
-    $ua1->get_ok("http://localhost/books/list", "'test01' book list");
-    $ua1->title_is("Book List", "Check logged in and at book list");
-    $ua1->content_contains("Book List", "Book List page test");
-    $ua1->content_contains("TestTitle", "Look for 'TestTitle'");
-    
-    # Make sure the new book can be deleted
-    # Get all the Delete links on the list page
-    my @delLinks = $ua1->find_all_links(text => 'Delete');
-    # Use the final link to delete the last book
-    $ua1->get_ok($delLinks[$#delLinks]->url, 'Delete last book');
-    # Check that delete worked
-    $ua1->content_contains("Book List", "Book List page test");
-    $ua1->content_contains("Book deleted", "Book was deleted");
-    
-    # User 'test02' should not be able to add a book
-    $ua2->get_ok("http://localhost/books/url_create/TestTitle2/2/5", "'test02' add");
-    $ua2->content_contains("Unauthorized!", "Check 'test02' cannot add");
-
-The C<live_app.t> test cases uses copious comments to explain each step
-of the process.  In addition to the techniques shown here, there are a
-variety of other methods available in 
-L<Test::WWW::Mechanize::Catalyst|Test::WWW::Mechanize::Catalyst> (for 
-example, regex-based matching). Consult the documentation for more
-detail.
-
-B<TIP>: For I<unit tests> vs. the "full application tests" approach used
-by L<Test::WWW::Mechanize::Catalyst|Test::WWW::Mechanize::Catalyst>, see 
-L<Catalyst::Test|Catalyst::Test>.
-
-B<Note:> The test script does not test the C<form_create> and
-C<form_create_do> actions.  That is left as an exercise for the reader
-(you should be able to complete that logic using the existing code as a
-template).
-
-To run the new test script, use a command such as:
-
-    $ CATALYST_DEBUG=0 prove --lib lib -v t/live_app01.t
-
-or
-
-    $ DBIC_TRACE=0 CATALYST_DEBUG=0 prove --lib lib -v 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 (Part 6) to
-isolate and fix any problems.
-
-If you want to run the test case under the Perl interactive debugger,
-try a command such as:
-
-    $ DBIC_TRACE=0 CATALYST_DEBUG=0 perl -d -Ilib t/live_app01.t
-
-Note that although this tutorial uses a single custom test case for
-simplicity, you may wish to break your tests into different files for
-better organization.
-
-B<TIP:> If you have a test case that fails, you will receive an error
-similar to the following:
-
-    #   Failed test 'Check we are NOT logged in'
-    #   in t/live_app01.t at line 31.
-    #     searched: "\x{0a}<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Tran"...
-    #   can't find: "You need to log in to use this application."
-
-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 
-failed test:
-
-    warn $ua1->content;
-
-This will cause the full HTML returned by the request to be displayed.
-
-
-=head1 SUPPORTING BOTH PRODUCTION AND TEST DATABASES
-
-You may wish to leverage the techniques discussed in this tutorial to
-maintain both a "production database" for your live application and a
-"testing database" for your test cases.  One advantage to
-L<Test::WWW::Mechanize::Catalyst|Test::WWW::Mechanize::Catalyst> is that
-it runs your full application; however, this can complicate things when
-you want to support multiple databases.  One solution is to allow the
-database specification to be overridden with an environment variable.
-For example, open C<lib/MyApp/Model/MyAppDB.pm> in your editor and
-change the C<__PACKAGE__-E<gt>config(...> declaration to resemble:
-
-    my $dsn = $ENV{MYAPP_DSN} ||= 'dbi:SQLite:myapp.db';
-    __PACKAGE__->config(
-        schema_class => 'MyAppDB',
-        connect_info => [
-            $dsn,
-            '',
-            '',
-            { AutoCommit => 1 },
-    
-        ],
-    );
-
-Then, when you run your test case, you can use commands such as:
-
-    $ cp myapp.db myappTEST.db
-    $ CATALYST_DEBUG=0 MYAPP_DSN="dbi:SQLite:myappTEST.db" prove --lib lib -v t/live_app01.t
-
-This will modify the DSN only while the test case is running.  If you
-launch your normal application without the C<MYAPP_DSN> environment
-variable defined, it will default to the same C<dbi:SQLite:myapp.db> as
-before.
-
-
-=head1 AUTHOR
-
-Kennedy Clark, C<hkclark@gmail.com>
-
-Please report any errors, issues or suggestions to the author.  The
-most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/trunk/Catalyst-Runtime/lib/Catalyst/Manual/Tutorial/>.
-
-Copyright 2006, Kennedy Clark, under Creative Commons License
-(L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
-