Catalyst::Manual::Tutorial::Authentication - Catalyst Tutorial - Part 4: Authentication
-
=head1 OVERVIEW
This is B<Part 4 of 9> for the Catalyst tutorial.
=item 9
-L<Appendicies|Catalyst::Manual::Tutorial::Appendicies>
+L<Appendices|Catalyst::Manual::Tutorial::Appendicies>
=back
-
=head1 DESCRIPTION
Now that we finally have a simple yet functional application, we can
-focus on providing authentication (with authorization coming in Part 5).
+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.
IMPORTANT: Does not work yet. Will be completed for final version.
-
=head1 BASIC AUTHENTICATION
-This section explores how add authentication logic to a Catalyst application.
-
+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 add the
-role information here although it will not be used until the
+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:
$ sqlite3 myapp.db < myapp02.sql
-=head2 Add User and Role Information to Dbic Schema
+=head2 Add User and Role Information to DBIC Schema
This step adds DBIC-based classes for the user-related database tables
-(the role information will not be used until the Part 5):
+(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
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
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:
# Clear the user's state
$c->logout;
- # Send the user to the starting
+ # Send the user to the starting point
$c->response->redirect($c->uri_for('/'));
}
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:
+Edit the existing C<lib/MyApp/Controller/Root.pm> class file and insert
+the following method:
=head2 auto
B<Note:> Catalyst provides a number of different types of actions, such
as C<Local>, C<Regex>, and C<Private>. You should refer to
-L<Catalyst::Manual::Intro|Catalyst::Manual::Intro> for a more detailed
-explanation, but the following bullet points provide a quick
-introduction:
+L<Catalyst::Manual::Intro> for a more detailed explanation, but the
+following bullet points provide a quick introduction:
=over 4
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
Now trying going to L<http://localhost:3000/books/list> and you should
be redirected to the login page, hitting Shift+Reload if necessary (the
"You are already logged in" message should I<not> appear -- if it does,
-click the C<logout> button and try again). Make note of the
-C<***Root::auto User not found...> debug message in the development
-server output. Enter username C<test01> and password C<mypass>, and you
-should be taken to the Book List page.
+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:
+Open C<root/src/books/list.tt2> and add the following lines to the
+bottom:
<p>
<a href="[% Catalyst.uri_for('/login') %]">Login</a>
need to log in to use this application."
-
=head1 USING PASSWORD HASHES
In this section we increase the security of our system by converting
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.
-
+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 L<Catalyst::Plugin:RequireSSL>.
=head2 Get a SHA-1 Hash for the Password
e727d1464ae12436e899a726da5b2f11d8381b26
$
-
=head2 Switch to SHA-1 Password Hashes in the Database
Next, we need to change the C<password> column of our C<users> table to
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>
=item 9
-L<Appendicies|Catalyst::Manual::Tutorial::Appendicies>
+L<Appendices|Catalyst::Manual::Tutorial::Appendices>
=back
IMPORTANT: Does not work yet. Will be completed for final version.
-
=head1 BASIC AUTHORIZATION
In this section you learn how to manually configure authorization.
-
-=head2 Update Plugins to Include Support Authorization
+=head2 Update Plugins to Include Support for Authorization
Edit C<lib/MyApp.pm> and add C<Authorization::Roles> to the list:
=head2 Add Config Information for Authorization
-Edit C<myapp.yml> and update it to match (everything from the "authorization:" line down is new):
+Edit C<myapp.yml> and update it to match (everything from the
+"authorization:" line down is new):
---
name: MyApp
[% END %]
</p>
-This code displays a different combination of links depending on the roles assigned to the user..
-
+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
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.
+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
=head2 url_create
- Create a book with the supplied title, and rating
+ Create a book with the supplied title and rating,
with manual authorization
=cut
}
-To add authorization, we simply write the main code of this method in an
+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
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:
+Press C<Ctrl-C> to kill the previous server instance (if it's still
+running) and restart it:
$ script/myapp_server.pl
L<http://localhost:3000/logout> in you browser directly) when you are
done.
-
-
=head1 ENABLE ACL-BASED AUTHORIZATION
This section takes a brief look at how the
-L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL>
-can automate much of the work required to perform role-based
-authorization in a Catalyst application.
-
+L<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
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
=back
-
=head2 Add a Method to Handle Access Violations
By default,
-L<Catalyst::Plugin::Authorization::ACL|Catalyst::Plugin::Authorization::ACL>
+L<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
L<http://localhost:3000/logout> URL directly) when you are done.
-
=head1 AUTHOR
Kennedy Clark, C<hkclark@gmail.com>
Please report any errors, issues or suggestions to the author.
-Copyright 2006, Kennedy Clark, under Creative Commons License (L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
+Copyright 2006, Kennedy Clark, under Creative Commons License
+(L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
Version: .94
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
+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
IMPORTANT: Does not work yet. Will be completed for final version.
-
=head1 FORMLESS SUBMISSION
Our initial attempt at object creation will utilize the "URL arguments"
=head2 url_create
- Create a book with the supplied title, rating and author
+ 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 @_
+ # 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
[% # '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 -%]
+ [% # 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 (we
-are talking TT plugins here, not Catalyst plugins) to add extra
-functionality to the base TT capabilities. Here, the plugin allows
-L<Data::Dumper|Data::Dumper> "pretty printing" of objects and variables.
-Other than that, the rest of the code should be familiar from the
-examples in Part 2.
+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:
+it. Then restart the server:
$ script/myapp_server.pl
B<TIP>: You can use C<script/myapp_server.pl -r> to have the development
server auto-detect changed files and reload itself (if your browser acts
odd, you should also try throwing in a C<-k>). If you make changes to
-just the TT templates, you do not need to reload the development server
+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).
C</books/list> page).
-
=head1 MANUALLY BUILDING A CREATE FORM
Although the C<url_create> action in the previous step does begin to
$c->stash->{template} = 'books/form_create.tt2';
}
-This action merely invokes a view containing a book creation form.
-
+This action simply invokes a view containing a book creation form.
=head2 Add a Template for the Form
Note that we have specified the target of the form data as
C<form_create_do>, the method created in the section that follows.
-
=head2 Add Method to Process Form Values and Update Database
Edit C<lib/MyApp/Controller/Books.pm> and add the following method to
# Store new model object in stash
$c->stash->{book} = $book;
- # Avoid Data::Dumper issue mention earlier
+ # Avoid Data::Dumper issue mentioned earlier
# You can probably omit this
$Data::Dumper::Useperl = 1;
=head2 Test Out The Form
-If the application is still running from before, use C<Ctrl-C> to kill it. Then restart the server:
+If the application is still running from before, use C<Ctrl-C> to kill
+it. Then restart the server:
$ script/myapp_server.pl
"Return to list" to view the full list of books.
B<Note:> Having the user enter the primary key ID for the author is
-obviously a bit crude; we will address this concern with a drop-down
-list in Part 8.
-
-
+obviously crude; we will address this concern with a drop-down list in
+Part 8.
=head1 A SIMPLE DELETE FEATURE
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
deleted" status message should display at the top of the page, along
with a list of the six remaining books.
-
=head1 AUTHOR
Kennedy Clark, C<hkclark@gmail.com>
C<end> action to your application class (C<MyApp.pm>) or Root.pm
(C<MyApp/Controller/Root.pm>). In most of these cases, you can convert
to L<DefaultEnd|Catalyst::Plugin::DefaultEnd> by deleting the C<end>
-action and using the plugin instead.
+action and using the plugin instead. There are certainly cases when
+you'd want to write your own custom C<end> action, but for most
+circumstances, DefaultEnd will be exactly what you want.
=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
+omit C<Catalyst::Plugin::> from the name. Additionally, you can spread
the plugin names across multiple lines as shown here, or place them all
on one (or more) lines as with the default configuration.
=head1 DATABASE ACCESS WITH C<DBIx::Class>
Catalyst can be used with virtually any form of persistent datastore
-available via Perl. For example,
-L<Catalyst::Model::DBI|Catalyst::Model::DBI> can be used to easily
-access databases through the traditional Perl DBI interface. However,
-most Catalyst applications use some form of ORM technology to
+available via Perl. For example, L<Catalyst::Model::DBI> can be used to
+easily access databases through the traditional Perl L<DBI> interface.
+However, most Catalyst applications use some form of ORM technology to
automatically create and save model objects as they are used. Although
-Tony Bowden's L<Class::DBI|Class::DBI> has been the traditional Perl ORM
-engine, Matt Trout's L<DBIx::Class|DBIx::Class> (abbreviated as "DBIC")
-has rapidly emerged as the Perl-based ORM technology of choice. Most
-new Catalyst applications rely on DBIC, as will this tutorial.
-
-Note: See L<Catalyst::Model::CDBI| Catalyst:: Model::CDBI > for more
-information on using Catalyst with L<Class::DBI|Class::DBI>. Catalyst
-can also be used with "plain old DBI"; see L<Catalyst::Model::DBI|
-Catalyst::Model::DBI>.
+Tony Bowden's L<Class::DBI> has been the traditional Perl ORM engine,
+Matt Trout's L<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>.
=head2 Create a DBIC Schema File
DBIx::Class uses a schema file to load other classes that represent the
tables in your database (DBIC refers to these "table objects" as "result
-sources," see L<DBIx::Class::ResultSource|DBIx::Class::ResultSource>).
-In this case, we want to load the model object for the C<books>,
-C<book_authors>, and C<authors> tables created in the previous step.
+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.
Open C<lib/MyAppDB.pm> in your editor and insert:
=head1 NAME
- MyAppDB -- DBIC Schema Class
+ MyAppDB - DBIC Schema Class
=cut
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>
+C<__PACKAGE__> is equivalent to C<MyAppDB>
=head2 Create the DBIC "Result Source" Files
In this step, we create "table classes" (again, these are called a
-"result source" classes in DBIC) that acts as model objects for the
+"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:
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.
+have to use C<first> to display a single author). C<many_to_many> allows
+us to use the shorter C<$book-E<gt>authors-E<gt>first-E<gt>last_name>.
+Note that you cannot define a C<many_to_many> relationship without also
+having the C<has_many> relationship in place.
Next, open C<lib/MyAppDB/Author.pm> in your editor and enter:
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
+When L<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>.
+B<Note:> With L<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:
+Use the L<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 }'
applications often use the directories C<M>, C<V>, and C<C>]).
-
=head1 CREATE A CATALYST CONTROLLER
-Controllers are where you write methods that respond to C<GET> and C<POST> messages from the user's web browser.
+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:
+Use the Catalyst C<create> script to add a controller for book-related
+actions:
$ script/myapp_create.pl controller Books
Nicholas Clark's C<attributes> module to provide additional information
to the Catalyst dispatcher logic.
-
=head1 CATALYST VIEWS
-Views are where you render output for display in the user's web browser
-(or possibly using other display technology). As with virtually every
-aspect of Catalyst, options abound when it comes to the specific view
-technology you adopt inside your application. However, most Catalyst
-applications use the Template Toolkit, known as TT (for more information
-on TT, see L<http://www.template-toolkit.org>). Other popular View
-technologies include Mason (L<http://www.masonhq.com> and
-L<http://www.masonbook.com>) and L<HTML::Template|HTML::Template>
+Views are where you render output, typically for display in the user's
+web browser, but also possibly using other display our 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>
(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:
=item *
-L<Catalyst::Helper::View::TT|Catalyst::Helper::View::TT>
+L<Catalyst::Helper::View::TT>
=item *
-L<Catalyst::Helper::View::TTSite|Catalyst::Helper::View::TTSite>
+L<Catalyst::Helper::View::TTSite>
=back
Both are similar, but C<TT> merely creates the C<lib/MyApp/View/TT.pm>
file and leaves the creation of any hierarchical template organization
-entirely up to you (it also creates a C<t/view_TT.t> file for testing;
-test cases will be discussed in Part 7). Conversely, the C<TTSite>
-helper creates a modular and hierarchical view layout with separate
-Template Toolkit (TT) files for common header and footer information,
-configuration values, a CSS stylesheet, etc.
+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 the tutorial:
+rendering for this tutorial:
$ script/myapp_create.pl view TT TTSite
B<TIP>: Note that TTSite does one thing that could confuse people who
are used to the normal C<TT> Catalyst View: it redefines the Catalyst
-context object in templates from its usual C<c> to C<Catalyst>. Also
-keep this in mind when looking at other Catalyst examples (they almost
-always use C<c>). Note that Catalyst and TT I<do not complain> when you
-use the wrong name to access the context... it simply outputs blanks for
+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. Finally, be aware that this change in name I<only>
-applies to how the context object is accessed inside your TT templates,
+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
+when fetching the reference from C<@_> inside your methods). (You can
change back to the "default" behavior be removing the C<CATALYST_VAR>
line from C<lib/MyApp/View/TT.pm>, but you will also have to edit
C<root/lib/config/main> and C<root/lib/config/url>. If you do this, be
careful not to have a collision between your own C<c> variable and the
Catalyst C<c> variable.)
-
-
=head2 Globally Customize Every View
When using TTSite, files in the subdirectories of C<root/lib> can be
used to make changes that will appear in every view. For example, to
display optional status and error messages in every view, edit
-C<root/lib/site/layout> update it to match the following (the two HTML
+C<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="footer">[% PROCESS site/footer %]</div>
If we set either message in the Catalyst stash (e.g.,
-C<$c-E<gt>stash-E<gt>{status_msg} = 'Hello world'>) it will be displayed
-whenever any view used by that request is rendered. The C<message> and
-C<error> CSS styles are automatically defined in C<root/src/ttsite.css>
-and can be customized to suit your needs.
+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
+L<Catalyst::Plugin::Session> (we will use
Catalyst sessions in the Authentication part).
cell (a simple space is used between the names; in reality you would
probably want to modify the code to use a comma as a separator).
-If you are new to TT, the [% and %] tags are used to delimit "variable
-text". TT supports a wide variety of directives for "calling" other
+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
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
B<NOTE>: You can also set this in your code using
C<$class-E<gt>storage-E<gt>debug(1);>. See
-L<DBIx::Class::Manual::Troubleshooting|DBIx::Class::Manual::Troubleshooting>
-for details (including options to log to file vs. the Catalyst
-development server log.
+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:
=back
-
Point your browser to L<http://localhost:3000> and you should still get
the Catalyst welcome page.
Please report any errors, issues or suggestions to the author.
-Copyright 2006, Kennedy Clark, under Creative Commons License (L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
+Copyright 2006, Kennedy Clark, under Creative Commons License
+(L<http://creativecommons.org/licenses/by-nc-sa/2.5/>).
Version: .94