created "MyApp/root"
...
created "MyApp/script/myapp_create.pl"
+ Change to application directory and Run "perl Makefile.PL" to make sure your install is complete
$ cd MyApp
This creates a similar skeletal structure to what we saw in Chapter 2 of
the tutorial, except with C<MyApp> and C<myapp> substituted for
-C<Hello> and C<hello>.
+C<Hello> and C<hello>. (As noted in Chapter 2, omit the ".pl" from
+the command if you are using Strawberry Perl.)
=head1 EDIT THE LIST OF CATALYST PLUGINS
C<script/myapp_server.pl> development server earlier. You can remove
this item when you place your application into production.
-As you may have noticed, C<-Debug> is not a plugin, but a I<flag>.
+To be technically correct, it turns out that C<-Debug> is not a plugin, but a I<flag>.
Although most of the items specified on the C<__PACKAGE__-E<gt>setup>
line of your application class will be plugins, Catalyst supports a
limited number of flag options (of these, C<-Debug> is the most
common). See the documentation for C<Catalyst.pm> to get details on
other flags (currently C<-Engine>, C<-Home>, and C<-Log>).
-If you prefer, you can use the C<$c-E<gt>debug> method to enable debug
-messages.
+If you prefer, there are several other ways to enable debug output:
+
+=over 4
+
+=item *
+
+Use the C<$c-E<gt>debug> method
+
+=item *
+
+The C<-d> option to C<script/myapp_server.pl>
+
+=item *
+
+The C<CATALYST_DEBUG=1> environment variable (or set it to
+zero to templorarily disable debug output).
+
+=back
B<TIP>: Depending on your needs, it can be helpful to permanently
remove C<-Debug> from C<lib/MyApp.pm> and then use the C<-d> option
Then replace it with:
# Load plugins
- use Catalyst qw/-Debug
+ use Catalyst qw/
+ -Debug
ConfigLoader
Static::Simple
browser, not in the console window from which you're running your
application, which is where logging output usually goes.
-Make sure that when adding new plugins that you include them as a new
-dependancies within the Makefile.PL file. For example, after adding
+Make sure when adding new plugins you also include them as a new
+dependancy within the Makefile.PL file. For example, after adding
the StackTrace plugin the Makefile.PL should include the following
line:
=item *
-When specifying plugins on the C<__PACKAGE__-E<gt>setup> 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.
+When specifying plugins, you can omit C<Catalyst::Plugin::> from the
+name. Additionally, you can spread the plugin names across multiple
+lines as shown here or place them all on one line.
=back
=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
+an action, but you do not want Catalyst to directly expose the method
to your users. Catalyst will not map C<:Private> methods to a URI.
Use them for various sorts of "special" methods (the C<begin>,
C<auto>, etc. discussed below) or for methods you want to be able to
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 accept. See
-L<Catalyst::Manual::Intro/Action_types> for more information and a few
-examples.
+C<http://localhost:3000/books/list>, but "C<:Path('/list')>" would
+match on C<http://localhost:3000/list> (because of the leading slash).
+You can use C<:Args()> to specify how many arguments an action should
+accept. See L<Catalyst::Manual::Intro/Action_types> for more
+information and examples.
=item *
=head2 Create a Catalyst View
-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 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 Chapter 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 C<TTSite> was useful to bootstrap a project, its use is now
-deprecated and it should 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."
+When using TT for the Catalyst view, the main helper script
+is L<Catalyst::Helper::View::TT|Catalyst::Helper::View::TT>.
+You may also come across references to
+L<Catalyst::Helper::View::TTSite|Catalyst::Helper::View::TTSite>,
+but its use is now deprecated.
Enter the following command to enable the C<TT> style of view
rendering for this tutorial:
B<NOTE:> Make sure to add a comma after '.tt2' outside the single
quote.
-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>. 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...
+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>. Stick with these conventions for the
+tutorial, but feel free to use whatever options you desire in your
+applications (as with most things Perl, there's more than one way to
+do it...).
B<Note:> We will use C<root/src> as the base directory for our
-template files, which a full naming convention of
+template files, with a full naming convention of
C<root/src/_controller_name_/_action_name_.tt2>. Another popular option is to
use C<root/> as the base (with a full filename pattern of
C<root/_controller_name_/_action_name_.tt2>).
The C<[%> and C<%]> tags are used to delimit Template Toolkit code. TT
supports a wide variety of directives for "calling" other files,
looping, conditional logic, etc. In general, TT simplifies the usual
-range of Perl operators down to the single dot (C<.>) operator. This
+range of Perl operators down to the single dot (".") operator. This
applies to operations as diverse as method calls, hash lookups, and list
index values (see
L<http://search.cpan.org/perldoc?Template::Manual::Variables> for
-details and examples). In addition to the usual C<Template> module Pod
+details and examples). In addition to the usual L<Template> module Pod
documentation, you can access the TT manual at
L<http://search.cpan.org/perldoc?Template::Manual>.
-B<TIP:> While you can build all sorts of complex logic into your TT
-templates, you should in general keep the "code" part of your templates
-as simple as possible. If you need more complex logic, create helper
-methods in your model that abstract out a set of code into a single call
-from your TT template. (Note that the same is true of your controller
-logic as well -- complex sections of code in your controllers should
-often be pulled out and placed into your model objects.)
+B<TIP:> While you can build all sorts of complex logic into your TT
+templates, you should in general keep the "code" part of your
+templates as simple as possible. If you need more complex logic,
+create helper methods in your model that abstract out a set of code
+into a single call from your TT template. (Note that the same is true
+of your controller logic as well -- complex sections of code in your
+controllers should often be pulled out and placed into your model
+objects.) In Chapter 4 of the tutorial we will explore some extremely
+helpful and powerful features of L<DBIx::Class> that allow you to pull
+code out of your views and controllers and place it where it
+rightfully belongs in a model class.
=head2 Test Run The Application
If you run into problems getting your application to run correctly, it
might be helpful to refer to some of the debugging techniques covered in
-the L<Debugging|Catalyst::Manual::Tutorial::07_Debugging> part of the
+the L<Debugging|Catalyst::Manual::Tutorial::07_Debugging> chapter of the
tutorial.
your OS command prompt.
Please note that here we have chosen to use 'singular' table names. This
-is because the default inflection code for L<DBIx::Class:Schema::Loader>
+is because the default inflection code for L<DBIx::Class::Schema::Loader>
does NOT handle plurals. There has been much philosophical discussion
on whether table names should be plural or singular. There is no one
correct answer, as long as one makes a choice and remains consistent
with it. If you prefer plural table names (e.g. they are easier and
more natural to read) then you will need to pass it an inflect_map
-option. See L<DBIx::Class:Schema::Loader> for more information.
+option. See L<DBIx::Class::Schema::Loader> for more information.
For using other databases, such as PostgreSQL or MySQL, see
L<Appendix 2|Catalyst::Manual::Tutorial::10_Appendices>.
Catalyst can be used with virtually any form of datastore available
via Perl. For example, L<Catalyst::Model::DBI|Catalyst::Model::DBI>
-can be used to access databases through the traditional Perl C<DBI>
+can be used to access databases through the traditional Perl L<DBI>
interface or you can use a model to access files of any type on the
filesystem. However, most Catalyst applications use some form of
object-relational mapping (ORM) technology to create objects
$ perl -MCatalyst::Model::DBIC::Schema -e \
'print "$Catalyst::Model::DBIC::Schema::VERSION\n"'
- 0.23
+ 0.31
-(please note that the '\' above is a line continuation marker and
-should NOT be included as part of the command)
+Please note the '\' above. Depending on your environment, you might
+be able to cut and paste the text as shown or need to remove the '\'
+character to that the command is all on a single line.
-If you don't have version 0.23 or higher, please run this command
-to install it directly from CPAN:
+You should have version 0.31 or greater if you are following along
+with Debian 5. In other environments, you may need to run this
+command to install it directly from CPAN:
$ sudo cpan Catalyst::Model::DBIC::Schema
And re-run the version print command to verify that you are now at
-0.23 or higher.
+0.31 or higher.
=head2 Create Static DBIx::Class Schema Files
automatically build the required files for us:
$ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \
- create=static components=TimeStamp dbi:SQLite:myapp.db
+ create=static dbi:SQLite:myapp.db
exists "/home/me/MyApp/script/../lib/MyApp/Model"
exists "/home/me/MyApp/script/../t"
Dumping manual schema for MyApp::Schema to directory /home/me/MyApp/script/../lib ...
created "/home/me/MyApp/script/../lib/MyApp/Model/DB.pm"
created "/home/me/MyApp/script/../t/model_DB.t"
-(please note that the '\' above is a line continuation marker and
-should NOT be included as part of the command)
+Please note the '\' above. Depending on your environment, you might
+be able to cut and paste the text as shown or need to remove the '\'
+character to that the command is all on a single line.
The C<script/myapp_create.pl> command breaks down like this:
=item *
-C<components=TimeStamp> causes the help to include the
-L<DBIx::Class::TimeStamp|DBIx::Class::TimeStamp> DBIC component.
-
-=item *
-
And finally, C<dbi:SQLite:myapp.db> is the standard DBI connect string
for use with SQLite.
$
$ # Then re-run the helper to build the files for "load_namespaces"
$ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \
- create=static components=TimeStamp dbi:SQLite:myapp.db
- $
- $ # Note that the '\' above is a line continuation marker and
- $ # should NOT be included as part of the command
-
+ create=static dbi:SQLite:myapp.db
$
$ # Now convert the existing files over
$ cd lib/MyApp/Schema
[debug] Statistics enabled
[debug] Loaded plugins:
.----------------------------------------------------------------------------.
- | Catalyst::Plugin::ConfigLoader 0.23 |
- | Catalyst::Plugin::StackTrace 0.10 |
- | Catalyst::Plugin::Static::Simple 0.21 |
+ | Catalyst::Plugin::ConfigLoader 0.27 |
+ | Catalyst::Plugin::StackTrace 0.11 |
+ | Catalyst::Plugin::Static::Simple 0.25 |
'----------------------------------------------------------------------------'
[debug] Loaded dispatcher "Catalyst::Dispatcher"
| /books/list | /books/list |
'-------------------------------------+--------------------------------------'
- [info] MyApp powered by Catalyst 5.80003
+ [info] MyApp powered by Catalyst 5.80013
You can connect to your server at http://debian:3000
B<NOTE:> Be sure you run the C<script/myapp_server.pl> command from
Also notice in the output of the C<script/myapp_server.pl> that
DBIx::Class used the following SQL to retrieve the data:
- SELECT me.id, me.title, me.rating FROM books me
+ SELECT me.id, me.title, me.rating FROM book me
because we enabled DBIC_TRACE.
=head2 Configure TT.pm For The Wrapper
In order to create a wrapper, you must first edit your TT view and
-tell it where to find your wrapper file. Your TT view is located in
-C<lib/MyApp/View/TT.pm>.
+tell it where to find your wrapper file.
-Edit C<lib/MyApp/View/TT.pm> and change it to match the following:
+Edit you TT view in C<lib/MyApp/View/TT.pm> and change it to match the
+following:
__PACKAGE__->config(
# Change default TT extension
# 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 (aka, foreign key in peer table)
- __PACKAGE__->has_many(book_author => 'MyApp::Schema::Result::BookAuthor', 'book_id');
+ __PACKAGE__->has_many(book_authors => 'MyApp::Schema::Result::BookAuthor', 'book_id');
# many_to_many():
# args:
# 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(author => 'book_author', 'author');
+ __PACKAGE__->many_to_many(authors => 'book_authors', 'author');
B<Note:> Be careful to put this code I<above> the C<1;> at the end of the
will see examples on how to use DBIx::Class objects in your code soon,
but note that because C<$book-E<gt>book_author> can return multiple
authors, we have to use C<first> to display a single author).
-C<many_to_many> allows us to use the shorter C<$book-E<gt>author-
-E<gt>first-E<gt>last_name>. Note that you cannot define a
-C<many_to_many> relationship without also having the C<has_many>
-relationship in place.
+C<many_to_many> allows us to use the shorter
+C<$book-E<gt>author-E<gt>first-E<gt>last_name>. Note that you cannot
+define a C<many_to_many> relationship without also having the
+C<has_many> relationship in place.
Then edit C<lib/MyApp/Schema/Result/Author.pm> and add relationship
information as follows (again, be careful to put in above the C<1;> but
# 1) Name of relationship, DBIC will create an accessor with this name
# 2) Name of the model class referenced by this relationship
# 3) Column name in *foreign* table (aka, foreign key in peer table)
- __PACKAGE__->has_many(book_author => 'MyApp::Schema::Result::BookAuthor', 'author_id');
+ __PACKAGE__->has_many(book_authors => 'MyApp::Schema::Result::BookAuthor', 'author_id');
# many_to_many():
# args:
# 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(book => 'book_author', 'book');
+ __PACKAGE__->many_to_many(books => 'book_authors', 'book');
Finally, do the same for the "join table,"
C<lib/MyApp/Schema/Result/BookAuthor.pm>:
Result Classes we created).
Then hit the URL L<http://localhost:3000/books/list> with your browser
-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.
+and be sure that the book list still displays correctly. 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
...
<td>
+ [% # NOTE: See Chapter 4 for a better way to do this! -%]
[% # First initialize a TT variable to hold a list. Then use a TT FOREACH -%]
[% # loop in 'side effect notation' to load just the last names of the -%]
- [% # authors into the list. Note that the 'push' TT vmethod does not print -%]
+ [% # authors into the list. Note that the 'push' TT vmethod doesn't return -%]
[% # a value, so nothing will be printed here. But, if you have something -%]
- [% # in TT that does return a 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. -%]
+ [% # in TT that does return a value and you don't want it printed, you -%]
+ [% # 1) assign it to a bogus value, or -%]
+ [% # 2) use the CALL keyword to call it and discard the return value. -%]
[% tt_authors = [ ];
tt_authors.push(author.last_name) FOREACH author = book.authors %]
[% # Now use a TT 'virtual method' to display the author count in parens -%]
</td>
...
+B<IMPORTANT NOTE:> Again, you should keep as much "logic code" as
+possible out of your views. This kind of logic belongs in your model
+(the same goes for controllers -- keep them as "thin" as possible and
+push all of the "complicated code" out to your model objects). Avoid
+code like you see in the previous example -- we are only using it here
+to show some extra features in TT until we get to the more advanced
+model features we will see in Chapter 4 (see
+L<Catalyst::Manual::Tutorial::04_BasicCRUD/EXPLORING THE POWER OF DBIC>).
+
Then hit "Reload" in your browser (note that you don't need to reload
the development server or use the C<-r> option when updating TT
templates) and you should now see the number of authors each book has
debug output (one for each book as the authors are being retrieved by
DBIx::Class):
- SELECT me.id, me.title, me.rating FROM books me:
+ SELECT me.id, me.title, me.rating FROM book me:
SELECT author.id, author.first_name, author.last_name FROM book_author me
- JOIN author author ON ( author.id = me.author_id ) WHERE ( me.book_id = ? ): '1'
+ JOIN author author ON author.id = me.author_id WHERE ( me.book_id = ? ): '1'
SELECT author.id, author.first_name, author.last_name FROM book_author me
- JOIN author author ON ( author.id = me.author_id ) WHERE ( me.book_id = ? ): '2'
+ JOIN author author ON author.id = me.author_id WHERE ( me.book_id = ? ): '2'
SELECT author.id, author.first_name, author.last_name FROM book_author me
- JOIN author author ON ( author.id = me.author_id ) WHERE ( me.book_id = ? ): '3'
+ JOIN author author ON author.id = me.author_id WHERE ( me.book_id = ? ): '3'
SELECT author.id, author.first_name, author.last_name FROM book_author me
- JOIN author author ON ( author.id = me.author_id ) WHERE ( me.book_id = ? ): '4'
+ JOIN author author ON author.id = me.author_id WHERE ( me.book_id = ? ): '4'
SELECT author.id, author.first_name, author.last_name FROM book_author me
- JOIN author author ON ( author.id = me.author_id ) WHERE ( me.book_id = ? ): '5'
+ JOIN author author ON author.id = me.author_id WHERE ( me.book_id = ? ): '5'
Also note in C<root/src/books/list.tt2> that we are using "| html", a
type of TT filter, to escape characters such as E<lt> and E<gt> to <
=back
+=head2 RenderView's "dump_info" Feature
+
+One of the nice features of C<RenderView> is that it automatically
+allows you to add C<dump_info=1> to the end of any URL for your
+application and it will force the display of the "exception dump"
+screen to the client browser. You can try this out by starting the
+development server as before and then point your browser to this URL:
+
+ http://localhost:3000/books/list?dump_info=1
+
+You should get a page with the following message at the top:
+
+ Caught exception in MyApp::Controller::Root->end "Forced debug -
+ Scrubbed output at /usr/share/perl5/Catalyst/Action/RenderView.pm line 46."
+
+Along with a summary of your application's state at the end of the
+processing for that request. The "Stash" section should show a
+summarized version of the DBIC book model objects. If desired, you
+can adjust the summarization logic (called "scrubbing" logic) -- see
+L<Catalyst::Action::RenderView|Catalyst::Action::RenderView> for
+details.
+
+Note that you shouldn't need to worry about "normal clients" using
+this technique to "reverse engineer" your application -- C<RenderView>
+only supports the C<dump_info=1> feature when your application is
+running in C<-Debug> mode (something you won't do once you have your
+application deployed in production).
+
+
=head2 Using The Default Template Name
By default, C<Catalyst::View::TT> will look for a template that uses the
B<IMPORTANT:> Make sure that you do NOT skip the following section
before continuing to the next chapter 4 Basic CRUD.
+
=head2 Return To A Manually Specified Template
In order to be able to use C<$c-E<gt>forward> and C<$c-E<gt>detach>
Please report any errors, issues or suggestions to the author. The
most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.70/trunk/lib/Catalyst/Manual/Tutorial/>.
+L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
Copyright 2006-2008, Kennedy Clark, under Creative Commons License
(L<http://creativecommons.org/licenses/by-sa/3.0/us/>).