this feature of Catalyst during the authentication and authorization
sections (Part 5 and Part 6).
-B<IMPORTANT NOTE:> If you are using a version of
-L<Catalyst::Devel|Catalyst::Devel> prior to version 1.06, you need to
-be aware that Catalyst changed from a default format of YAML to the
-more straightforward C<Config::General> format. This tutorial use the
-newer C<myapp.conf> configuration file for C<Config::General> instead
-of C<myapp.yml> for YAML. However, Catalyst has long supported both
-formats and Catalyst will automatically use either C<myapp.conf> or
-C<myapp.yml> (or any other format supported by
-L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader> and
-L<Config::Any|Config::Any>). If you are using a versions of
-Catalyst::Devel prior to 1.06, you can convert to the newer format by
-simply creating the C<myapp.yml> file manually and deleting
-C<myapp.yml>. The default contents of C<myapp.conf> should only
-consist of one line: C<name MyApp>.
+B<IMPORTANT NOTE:> If you are using a version of
+L<Catalyst::Devel|Catalyst::Devel> prior to version 1.06, be aware
+that Catalyst changed the default format from YAML to the more
+straightforward C<Config::General> style. This tutorial uses the
+newer C<myapp.conf> file for C<Config::General>. However, Catalyst
+supports both formats and will automatically use either C<myapp.conf>
+or C<myapp.yml> (or any other format supported by
+L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader> and
+L<Config::Any|Config::Any>). If you are using a version of
+Catalyst::Devel prior to 1.06, you can convert to the newer format by
+simply creating the C<myapp.conf> file manually and deleting
+C<myapp.yml>. The default contents of the C<myapp.conf> you create
+should only consist of one line:
+
+ name MyApp
B<TIP>: This script can be useful for converting between configuration
formats:
perl -Ilib -e 'use MyApp; use Config::General;
Config::General->new->save_file("myapp.conf", MyApp->config);'
-B<NOTE:> The default C<myapp.conf> should look like:
-
- name MyApp
-
=item *
L<Catalyst::Plugin::Static::Simple|Catalyst::Plugin::Static::Simple>
=back
-For out application, we want to add one new plugin into the mix. To
+For our application, we want to add one new plugin into the mix. To
do this, edit C<lib/MyApp.pm> (this file is generally referred to as
your I<application class>) and delete the line with:
StackTrace
/);
+B<Note:> Recent versions of C<Catalyst::Devel> have used a variety of
+techniques to load these plugins/flags. If you are following along in
+Ubuntu 8.10, you should have C<Catalyst::Devel> v1.07 and see the
+default code shown above. If you are using v1.08, you should see the
+following by default:
+
+ use Catalyst qw/-Debug
+ ConfigLoader
+ Static::Simple/;
+ ...
+ __PACKAGE__->setup();
+
+Don't let these variations confuse you -- they all accomplish the same
+result.
+
This tells Catalyst to start using one new plugin,
L<Catalyst::Plugin::StackTrace|Catalyst::Plugin::StackTrace>, to add a
stack trace to the standard Catalyst "debug screen" (the screen
is used to pass information between components and provide access to
Catalyst and plugin functionality.
-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. Many newer Catalyst
-applications are switching to the use of "Literal" C<:Path> actions
-and C<Args> attribute in lieu of C<: Local> and C<: Private>. For
-example, C<sub any_method :Path :Args(0)> can be used instead of C<sub
-index :Private> (because no path was supplied to C<Path> it matches
-the "empty" URL in the namespace of that module... the same thing
-C<sub index> would do) or C<sub list :Path('list') :Args(0)> could be
-used instead of the C<sub list : Local> above (the C<list> argument to
-C<Path> would make it match on the URL C<list> under C<books>, the
-namespace of the current module). See "Action Types" in
-L<Catalyst::Manual::Intro|Catalyst::Manual::Intro> as well as Part 5
-of this tutorial (Authentication) for additional information. Another
-popular but more advanced feature is C<Chained> actions that allow a
-single URL to "chain together" multiple action method calls, each with
-an appropriate number of arguments (see
-L<Catalyst::DispatchType::Chained|Catalyst::DispatchType::Chained> for
-details).
+Catalyst actions are regular Perl methods, but they make use of
+attributes (the "C<: Local>" next to the "C<sub list>" in the code
+above) to provide additional information to the Catalyst dispatcher
+logic (note that the space between the colon and the attribute name is
+optional... you will see attributes written both ways). Most Catalyst
+Controllers use one of five action types:
+
+=over 4
+
+=item *
+
+B<:Private> -- Use C<:Private> for methods that you want to make into
+an action, but you do not want Catalyst to directly expose the action
+to your users. Catalyst will not map C<:Private> methods to a URI.
+Use them for various sorts of "special" methods (the C<begin>,
+C<auto>, etc. discussed below) or for methods you want to be able to
+C<forward> or C<detach> to. (If the method is a plain old "helper
+method" that you don't want to be an action at all, then just define
+the method without any attribute -- you can call it in your code, but
+the Catalyst dispatcher will ignore it.)
+
+There are five types of "special" build-in C<:Private> actions:
+C<begin>, C<end>, C<default>, C<index>, and C<auto>.
+
+=over 4
+
+=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
+
+=item *
+
+B<:Path> -- C<:Path> actions let you map a method to an explicit URI
+path. For example, "C<:Path('list')>" in
+C<lib/MyApp/Controller/Books.pm> would match on the URL
+C<http://localhost:3000/books/list> but "C<:Path('/list')>" would match
+on C<http://localhost:3000/list>. You can use C<:Args()> to specify
+how many arguments an action should except. See
+L<Catalyst::Manual::Intro/Action_types> for more information and a few
+examples.
+
+=item *
+
+B<:Local> -- C<:Local> is merely a shorthand for
+"C<:Path('_name_of_method_')>". For example, these are equivalent:
+"C<sub create_book :Local {...}>" and
+"C<sub create_book :Path('create_book') {...}>".
+
+=item *
+
+B<:Global> -- C<:Global> is merely a shorthand for
+"C<:Path('/_name_of_method_')>". For example, these are equivalent:
+"C<sub create_book :Global {...}>" and
+"C<sub create_book :Path('/create_book') {...}>".
+
+=item *
+
+B<:Chained> -- Newer Catalyst applications tend to use the Chained
+dispatch form of action types because of its power and flexibility.
+It allows a series of controller methods to automatically be dispatched
+to service a single user request. See
+L<Catalyst::Manual::Tutorial::BasicCRUD|Catalyst::Manual::Tutorial::BasicCRUD>
+and L<Catalyst::DispatchType::Chained|Catalyst::DispatchType::Chained>
+for more information on chained actions.
+
+=back
+
+You should refer to L<Catalyst::Manual::Intro/Action_types> for
+additional information and for coverage of some lesser-used action
+types not discussed here (C<Regex> and C<LocalRegex>).
=head1 CATALYST VIEWS
=back
-Both are similar, but C<TT> merely creates the C<lib/MyApp/View/TT.pm>
+Both helpers are similar. C<TT> 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 8.) On the other hand, the
-C<TTSite> helper creates a modular and hierarchical view layout with
+test cases will be discussed in Part 8.) C<TTSite>, on the other hand,
+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.
-While TTSite is useful to bootstrap a project, most in the Catalyst
-community recommend that it's easier to learn both Catalyst and
-Template Toolkit if you use the more basic TT approach. Consequently,
-this tutorial will use "plain old TT."
+While C<TTSite> was useful to bootstrap a project, its use is now
+deprecated and to be considered historical. For most Catalyst
+applications it adds redundant functionality and structure; many in the
+Catalyst community recommend that it's easier to learn both Catalyst and
+Template Toolkit if you use the more basic C<TT> approach.
+Consequently, this tutorial will use "plain old TT."
Enter the following command to enable the C<TT> style of view
rendering for this tutorial:
TEMPLATE_EXTENSION => '.tt2',
# Set the location for TT files
INCLUDE_PATH => [
- MyApp->path_to( 'root/src' ),
+ MyApp->path_to( 'root', 'src' ),
],
);
This changes the default extension for Template Toolkit from '.tt' to
'.tt2' and changes the base directory for your template files from
-C<root> to C<root/src>.
+C<root> to C<root/src>. These changes from the default are done mostly
+to facilitate the application we're developing in this tutorial; as with
+most things Perl, there's more than one way to do it...
=head2 Create a TT Template Page
".q" to exit from SQLite from the SQLite interactive mode and return to
your OS command prompt.
+For using other databases, such as PostgreSQL or MySQL, see
+L<Appendix 2|Catalyst::Manual::Tutorial::Appendices>.
=head1 DATABASE ACCESS WITH C<DBIx::Class>
created "/home/me/MyApp/script/../t/model_DB.t"
+The C<script/myapp_create.pl> command breaks down like this:
+
+=over 4
+
+=item *
+
C<DB> is the name of the model class to be created by the helper in
-C<lib/MyApp/Model>. C<DBIC::Schema> is the type of the model to
-create. C<MyApp::Schema> is the name of the DBIC schema file written
-to C<lib/MyApp/Schema.pm>. Because we specified C<create=dynamic> to
-the helper, it use
+C<lib/MyApp/Model>.
+
+=item *
+
+C<DBIC::Schema> is the type of the model to create.
+
+=item *
+
+C<MyApp::Schema> is the name of the DBIC schema file written to
+C<lib/MyApp/Schema.pm>.
+
+=item *
+
+Because we specified C<create=dynamic> to the helper, it use
L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> to
dynamically load the schema information from the database every time
-the application starts. And finally, C<dbi:SQLite:myapp.db> is the
-standard DBI connect string for use with SQLite.
+the application starts.
+
+=item *
+
+And finally, C<dbi:SQLite:myapp.db> is the standard DBI connect string
+for use with SQLite.
+
+=back
B<NOTE:> Although the C<create=dynamic> option to the DBIC helper
makes for a nifty demonstration, is only really suitable for very
$c->stash->{template} = 'books/list.tt2';
}
-B<TIP>: You may see the C<$c-E<gt>model('DB::Book')> un-commented above
-written as C<$c-E<gt>model('DB')-E<gt>resultset('Book')>. The two
-are equivalent.
+B<TIP>: You may see the C<$c-E<gt>model('DB::Books')> un-commented
+above written as C<$c-E<gt>model('DB')-E<gt>resultset('Books')>. The
+two are equivalent. Either way, C<$c-E<gt>model> returns a
+L<DBIx::Class::ResultSet|DBIx::Class::ResultSet> which handles queries
+against the database and iterating over the set of results that are
+returned.
+
+We are using the C<-E<gt>all> to fetch all of the books. DBIC
+supports a wide variety of more advanced operations to easily do
+things like filtering and sorting the results. For example, the
+following could be used to sort the results by descending title:
+
+ $c->model('DB::Books')->search({}, {order_by => 'title DESC'});
+
+Some other examples are provided in
+L<DBIx::Class::Manual::Cookbook/Complex WHERE clauses>, with
+additional information found at L<DBIx::Class::ResultSet/search>,
+L<DBIx::Class::Manual::FAQ/Searching>,
+L<DBIx::Class::Manual::Intro|DBIx::Class::Manual::Intro>
+and L<Catalyst::Model::DBIC::Schema|Catalyst::Model::DBIC::Schema>.
=head2 Test 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):
+First, let's enable an environment variable that causes DBIx::Class to
+dump the SQL statements usedß to access the database. This is a
+helpful trick when you are trying to debug your database-oriented
+code:
$ export DBIC_TRACE=1
TEMPLATE_EXTENSION => '.tt2',
# Set the location for TT files
INCLUDE_PATH => [
- MyApp->path_to( 'root/src' ),
+ MyApp->path_to( 'root', 'src' ),
],
# Set to 1 for detailed timer stats in your HTML as comments
TIMER => 0,
</div><!-- end bodyblock -->
<div id="footer">Copyright (c) your name goes here</div>
- </div><!-- end outter -->
+ </div><!-- end outer -->
</body>
</html>
class (L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> is
only being used by the helper to load the schema once and then create
the static files for us) and C<Schema.pm> only contains a call to the
-C<load_classes> method. You will also find that C<lib/MyApp/Schema>
+C<load_classes> method. You will also find that C<lib/MyApp>
contains a C<Schema> subdirectory, with one file inside this directory
for each of the tables in our simple database (C<Authors.pm>,
C<BookAuthors.pm>, and C<Books.pm>). These three files were created
automatically created information at the top of each file should your
database structure get updated.
-Also note the "flow" of the model information across the various files
-and directories. Catalyst will initially load the model from
-C<lib/MyApp/Model/DB.pm>. This file contains a reference to
-C<lib/MyApp/Schema.pm>, so that file is loaded next. Finally,
-the call to C<load_classes> in C<Schema.pm> will load each of the
-table-specific "results source" files from the C<lib/MyApp/Schema>
-subdirectory. These three table-specific DBIC schema files will then be
-used to create three table-specific Catalyst models every time the
-application starts (you can see these three model files listed in
-the debug output generated when you launch the application).
+Also note the "flow" of the model information across the various files
+and directories. Catalyst will initially load the model from
+C<lib/MyApp/Model/DB.pm>. This file contains a reference to
+C<lib/MyApp/Schema.pm>, so that file is loaded next. Finally, the
+call to C<load_classes> in C<Schema.pm> will load each of the "result
+class" files from the C<lib/MyApp/Schema> subdirectory. The end
+result is that Catalyst will dynamically create three table-specific
+Catalyst models every time the application starts (you can see these
+three model files listed in the debug output generated when you launch
+the application).
+
+B<NOTE:> The version of
+L<Catalyst::Model::DBIC::Schema|Catalyst::Model::DBIC::Schema> in
+Ubuntu 8.10 uses the older DBIC C<load_classes> vs. the newer
+C<load_namspaces> technique. For new applications, please try to use
+C<load_namespaces> since it more easily supports a very useful DBIC
+technique called "ResultSet Classes." This tutorial expects to migrate to
+C<load_namespaces> when the next release of Ubuntu comes out.
+
+If you wish to try C<load_namespaces> now, you can manually do the
+equivalent of the C<create=static> operation outside of the Catalyst
+helper:
+
+ perl -MDBIx::Class::Schema::Loader=make_schema_at,dump_to_dir:./lib -e \
+ 'make_schema_at("MyApp::Schema", { debug => 1, use_namespaces => 1, \
+ components => ["InflateColumn::DateTime"] },["dbi:SQLite:myapp.db", "", "" ])'
+
+And then use the helper to only create the Catalyst model class:
+
+ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema dbi:SQLite:myapp.db
+
+B<However>, it is important to note that C<load_namespaces> will look
+for your C<Books.pm>, <Authors.pm>, etc. files in
+C<lib/MyApp/Schema/Result> (it adds the subdirection "Result" so that
+there can also be a "ResultSet" directory next to it in the
+hierarchy). Therefore, if you switch to C<load_namespaces>, you will
+need to modify the path to these "result class" files throughout the
+rest of the tutorial. Our recommendation for now would be to complete
+the tutorial using C<load_classes> and the try converting to
+C<load_namespaces> after you are done.
=head2 Updating the Generated DBIC Schema Files
# 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
+ # 3) Column name in *foreign* table (aka, foreign key in peer table)
__PACKAGE__->has_many(book_authors => 'MyApp::Schema::BookAuthors', 'book_id');
# many_to_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
+ # 3) Column name in *foreign* table (aka, foreign key in peer table)
__PACKAGE__->has_many(book_author => 'MyApp::Schema::BookAuthors', 'author_id');
# many_to_many():
three dynamically created model class (one for each of the
table-specific schema classes we created).
-Then hit the URL L<http://localhost:3000/books/list> and be sure that
-the book list is displayed.
+Then hit the URL L<http://localhost:3000/books/list> and be sure that
+the book list is displayed via the relationships established above. You
+can leave the development server running for the next step if you wish.
-You can leave the development server running for the next step if you
-wish.
+B<Note:> You will not see the authors yet because the view does not yet
+use the new relations. Read on to the next section where we update the
+template to do that.
=head1 UPDATING THE VIEW
debug output (one for each book as the authors are being retrieved by
DBIC).
-Also note that we are using "| html", a type of TT filter, to escape
-characters such as E<lt> and E<gt> to < and > and avoid various
-types of dangerous hacks against your application. In a real
-application, you would probably want to put "| html" at the end of
-every field where a user has control over the information that can
-appear in that field (and can therefore inject markup or code if you
-don't "neutralize" those fields). In addition to "| html", Template
-Toolkit has a variety of other useful filters that can found in the
-documentation for L<Template::Filters|Template::Filters>.
+ SELECT me.id, me.title, me.rating FROM books me:
+ SELECT me.book_id, me.author_id FROM book_authors me WHERE ( me.book_id = ? ): '1'
+ SELECT me.book_id, me.author_id FROM book_authors me WHERE ( me.book_id = ? ): '2'
+ SELECT me.book_id, me.author_id FROM book_authors me WHERE ( me.book_id = ? ): '3'
+ SELECT me.book_id, me.author_id FROM book_authors me WHERE ( me.book_id = ? ): '4'
+ SELECT me.book_id, me.author_id FROM book_authors me WHERE ( me.book_id = ? ): '5'
+
+Also note in C<root/src/books/list.tt2> that we are using "| html", a
+type of TT filter, to escape characters such as E<lt> and E<gt> to <
+and > and avoid various types of dangerous hacks against your
+application. In a real application, you would probably want to put
+"| html" at the end of every field where a user has control over the
+information that can appear in that field (and can therefore inject
+markup or code if you don't "neutralize" those fields). In addition to
+"| html", Template Toolkit has a variety of other useful filters that
+can found in the documentation for
+L<Template::Filters|Template::Filters>.
=head1 RUNNING THE APPLICATION FROM THE COMMAND LINE