=head1 OVERVIEW
-This is B<Part 2 of 9> for the Catalyst tutorial.
+This is B<Part 2 of 10> for the Catalyst tutorial.
L<Tutorial Overview|Catalyst::Manual::Tutorial>
=item 3
-L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD>
+L<More Catalyst Basics|Catalyst::Manual::Tutorial::MoreCatalystBasics>
=item 4
-L<Authentication|Catalyst::Manual::Tutorial::Authentication>
+L<Basic CRUD|Catalyst::Manual::Tutorial::BasicCRUD>
=item 5
-L<Authorization|Catalyst::Manual::Tutorial::Authorization>
+L<Authentication|Catalyst::Manual::Tutorial::Authentication>
=item 6
-L<Debugging|Catalyst::Manual::Tutorial::Debugging>
+L<Authorization|Catalyst::Manual::Tutorial::Authorization>
=item 7
-L<Testing|Catalyst::Manual::Tutorial::Testing>
+L<Debugging|Catalyst::Manual::Tutorial::Debugging>
=item 8
-L<Advanced CRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
+L<Testing|Catalyst::Manual::Tutorial::Testing>
=item 9
+L<Advanced CRUD|Catalyst::Manual::Tutorial::AdvancedCRUD>
+
+=item 10
+
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:
+In this part of the tutorial, we will create a very basic Catalyst web
+application, demonstrating a number of powerful capabilities, such as:
=over 4
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:
+L<Catalyst::Manual::About|Catalyst::Manual::About>). In short:
=over 4
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.
+that generates other forms such as PDF documents, e-mails, spreadsheets,
+or even "behind the scenes" formats such as XML and JSON.
=item * Controller
You can checkout the source code for this example from the catalyst
subversion repository as per the instructions in
-L<Catalyst::Manual::Tutorial::Intro>
+L<Catalyst::Manual::Tutorial::Intro|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"
+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 this first part of the tutorial, use the Catalyst
+C<catalyst.pl> script to initialize the framework for an
+application called C<Hello>:
+
+ $ catalyst.pl Hello
+ created "Hello"
+ created "Hello/script"
+ created "Hello/lib"
+ created "Hello/root"
...
- created "MyApp/script/myapp_create.pl"
- $ cd MyApp
+ created "Hello/script/hello_create.pl"
+ $ cd Hello
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
+directories and files it creates:
+
+ Changes # Record of application changes
+ lib # Lib directory for your app's Perl modules
+ Hello # Application main code directory
+ Controller # Directory for Controller modules
+ Model # Directory for Models
+ View # Directory for Views
+ Hello.pm # Base application module
+ Makefile.PL # Makefile to build application
+ hello.conf # Application configuration file
+ README # README file
+ root # Equiv of htdocs, dir for templates, css, javascript
+ favicon.ico
+ static # Directory for static files
+ images # Directory for image files used in welcome screen
+ script # Directory for Perl scripts
+ hello_cgi.pl # To run your app as a cgi (not recommended)
+ hello_create.pl # To create models, views, controllers
+ hello_fastcgi.pl # To run app as a fastcgi program
+ hello_server.pl # The normal development server
+ hello_test.pl # Test your app from the command line
+ t # Directory for tests
+ 01app.t # Test scaffold
+ 02pod.t
+ 03podcoverage.t
+
+
+Catalyst will "auto-discover" modules in the Controller, Model, and
+View directories. When you use the hello_create.pl script it will
+create Perl module scaffolds in those directories, plus test files in
+the "t" directory. The default location for templates is in the "root"
+directory. The scripts in the script directory will always start with
+the lowercased version of your application name. If your app is
+MaiTai, then the create script would be "maitai_create.pl".
+
+Though it's too early for any significant celebration, we already have
+a functioning application. We can use the Catalyst supplied script to
+start up a development server and view the default Catalyst page in
+your browser. All scripts in the script directory should be run from
+the base directory of your application, so change to the Hello
+directory.
+
+Run the following command to start up the built-in development web
+server (make sure you didn't forget the "C<cd Hello>" from the
+previous step):
+
+ $ script/hello_server.pl
[debug] Debug messages enabled
+ [debug] Statistics enabled
[debug] Loaded plugins:
.----------------------------------------------------------------------------.
- | Catalyst::Plugin::ConfigLoader 0.13 |
- | Catalyst::Plugin::Static::Simple 0.14 |
+ | Catalyst::Plugin::ConfigLoader 0.20 |
+ | Catalyst::Plugin::Static::Simple 0.20 |
'----------------------------------------------------------------------------'
[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] Found home "/home/me/Hello"
+ [debug] Loaded Config "/home/me/Hello/hello.conf"
[debug] Loaded components:
.-----------------------------------------------------------------+----------.
| Class | Type |
+-----------------------------------------------------------------+----------+
- | MyApp::Controller::Root | instance |
+ | Hello::Controller::Root | instance |
'-----------------------------------------------------------------+----------'
[debug] Loaded Private actions:
.----------------------+--------------------------------------+--------------.
| Private | Class | Method |
+----------------------+--------------------------------------+--------------+
- | /default | MyApp::Controller::Root | default |
- | /end | MyApp::Controller::Root | end |
+ | /default | Hello::Controller::Root | default |
+ | /end | Hello::Controller::Root | end |
+ | /index | Hello::Controller::Root | index |
'----------------------+--------------------------------------+--------------'
- [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.
+ [debug] Loaded Path actions:
+ .-------------------------------------+--------------------------------------.
+ | Path | Private |
+ +-------------------------------------+--------------------------------------+
+ | / | /default |
+ | / | /index |
+ '-------------------------------------+--------------------------------------'
+
+ [info] Hello powered by Catalyst 5.71000
+ You can connect to your server at http://debian:3000
Point your web browser to L<http://localhost:3000> (substituting a
different hostname or IP address as appropriate) and you should be
-greeted by the Catalyst welcome screen. 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)
+greeted by the Catalyst welcome screen (if you get some other welcome
+screen or an "Index" screen, you probably forgot to specify port 3000
+in your URL). Information similar to the following should be appended
+to the logging output of the development server:
+
+ [info] *** Request 1 (0.005/s) [20712] [Sun Mar 8 15:49:09 2009] ***
+ [debug] "GET" request for "/" from "1.1.1.98"
+ [info] Request took 0.007342s (136.203/s)
.----------------------------------------------------------------+-----------.
| Action | Time |
+----------------------------------------------------------------+-----------+
- | /default | 0.002844s |
- | /end | 0.000207s |
+ | /index | 0.000491s |
+ | /end | 0.000595s |
'----------------------------------------------------------------+-----------'
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 HELLO WORLD
-=head1 CREATE A CATALYST CONTROLLER
+=head2 The Simplest Way
-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.
+The Root.pm controller is a place to put global actions that usually
+execute on the root URL. Open the C<lib/Hello/Controller/Root.pm> file in
+your editor. You will see the "index" subroutine, which is
+responsible for displaying the welcome screen that you just saw in
+your browser. Later on you'll want to change that to something more
+reasonable, such as a "404" message or a redirect, but for now just
+leave it alone.
-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];
+ sub index :Path :Args(0) {
+ my ( $self, $c ) = @_;
- # 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';
+ # Hello World
+ $c->response->body( $c->welcome_message );
}
-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.
+The "C<$c>" here refers to the Catalyst context, which is used to
+access the Catalyst application. In addition to many other things,
+the Catalyst context provides access to "response" and "request"
+objects. (See L<Catalyst|Catalyst>,
+L<Catalyst::Response|Catalyst::Response>, and
+L<Catalyst::Request|Catalyst::Request>)
+
+C<$c-E<gt>response-E<gt>body> sets the HTTP response (see
+L<Catalyst::Response|Catalyst::Response>), while C<$c-E<gt>welcome_message>
+is a special method that returns the welcome message that you saw in
+your browser.
+
+The ":Path :Args(0)" after the method name are attributes which determine
+which URLs will be dispatched to this method. (Depending on your version of
+Catalyst, it used to say "Private" but using that with 'default' or 'index'
+is currently deprecated.)
+
+Some MVC frameworks handle dispatching in a central place. Catalyst,
+by policy, prefers to handle URL dispatching with attributes on
+controller methods. There is a lot of flexibility in specifying which
+URLs to match. This particular method will match all URLs, because it
+doesn't specify the path (nothing comes after "Path"), but will only
+accept a single args because of the ":Args(0)".
+
+The default is to map URLs to controller names, and because of
+the way that Perl handles namespaces through package names,
+it is simple to create hierarchical structures in
+Catalyst. This means that you can create controllers with deeply
+nested actions in a clean and logical way.
+
+For example, the URL C<http://hello.com/admin/articles/create> maps
+to the package C<Hello::Controller::Admin::Articles>, and the C<create>
+method.
+
+Add the following subroutine to your C<lib/Hello/Controller/Root.pm>
+file:
+
+ sub hello : Global {
+ my ( $self, $c ) = @_;
+
+ $c->response->body("Hello, World!");
+ }
-=item *
+B<TIP>: See Appendix 1 for tips on removing the leading spaces when
+cutting and pasting example code from POD-based documents.
-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.
+Here you're sending your own string to the webpage.
-=item *
+Save the file, start the server (stop and restart it if it's still
+up), and go to L<http://localhost:3000/hello> to
+see "Hello, World!"
-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 *
+=head2 Hello, World! Using a View and a Template
-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>.
+In the Catalyst world a "View" is not a page of XHTML or a template
+designed to present a page to a browser. It is the module that
+determines the I<type> of view -- HTML, pdf, XML, etc. For the
+thing that generates the I<content> of that view, (such as the
+default Toolkit Template) the actual templates go under the
+"root" directory.
-=back
+To create a TT view, run:
+ $ script/hello_create.pl view TT TT
-=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>:
+This creates the C<lib/Hello/View/TT.pm> module, which is a subclass of
+C<Catalyst::View::TT>.
=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>.
+The "view" keyword tells the create script that you are creating a view.
=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.
+The first "TT" tells the script to name the View module "TT.pm", which is a
+commonly used name for TT views. (You can name it anything you want, such as
+"HTML.pm".)
=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.
+The final "TT" tells it that you are creating a Template Toolkit view.
=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>, but 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.
+If you look at C<lib/Hello/View/TT.pm> you will find that it only contains a
+config statement to set the TT extension to ".tt".
+
+Now that the TT.pm "View" exists, Catalyst will autodiscover it and be
+able to use it to display the view templates, using the "process"
+method that it inherits from the C<Catalyst::View::TT class>.
+
+Template Toolkit is a very full featured template facility, with
+excellent documentation at L<http://template-toolkit.org/>,
+but since this is not a TT tutorial, we'll stick to only basic TT
+usage here (and explore some of the more common TT features in later
+parts of the tutorial).
+
+Create a C<root/hello.tt> template file (put it in the C<root> under
+the C<Hello> directory that is the base of your application). Here is
+a simple sample:
+
+ <p>
+ This is a TT view template, called '[% template.name %]'.
+ </p>
+
+[% and %] are markers for the TT parts of the template. Inside you can
+access Perl variables and classes, and use TT directives. In this
+case, we're using a special TT variable that defines the name of the
+template file (C<hello.tt>). The rest of the template is normal HTML.
+
+Change the hello method in C<lib/Hello/Controller/Root.pm> to the
+following:
+
+ sub hello : Global {
+ my ( $self, $c ) = @_;
+
+ $c->stash->{template} = 'hello.tt';
+ }
-Also notice in the output of the C<script/myapp_server.pl> that DBIC
-used the following SQL to retrieve the data:
+This time, instead of doing C<$c-E<gt>response-E<gt>body()>, you are setting
+the value of the "template" hash key in the Catalyst "stash", an area
+for putting information to share with other parts of your application.
+The "template" key determines which template will be displayed at the
+end of the method. Catalyst controllers have a default "end" action
+for all methods which causes the first (or default) view to be
+rendered (unless there's a C<$c-E<gt>response-E<gt>body()> statement). So your
+template will be magically displayed at the end of your method.
- SELECT me.id, me.title, me.rating FROM books me
+After saving the file, restart the development server, and look at
+L<http://localhost:3000/hello> again. You should
+see the template that you just made.
-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'
+=head1 CREATE A SIMPLE CONTROLLER AND AN ACTION
-You should see 5 such lines of debug output as DBIC fetches the author
-information for each book.
+Create a controller named "Site" by executing the create script:
+ $ script/hello_create.pl controller Site
-=head1 USING THE DEFAULT TEMPLATE NAME
+This will create a C<lib/Hello/Controller/Site.pm> file (and a test
+file). Bring Site.pm up in your editor, and you can see that there's
+not much there. Most people probably don't bother to use the create
+script to make controllers after they're used to using Catalyst.
-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):
+In C<lib/Hello/Controller/Site.pm>, add the following method:
- =head2 list
-
- Fetch all book objects and pass to books/list.tt2 in stash to be displayed
+ sub test : Local {
+ my ( $self, $c ) = @_;
- =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->stash->{username} = "John";
+ $c->stash->{template} = 'site/test.tt';
}
-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',
- });
+Notice the "Local" attribute on the C<test> method. This will cause
+the C<test> action (now that we have assigned an action type to the
+method it appears as a controller "action" to Catalyst) to be executed
+on the "controller/method" URL, or, in this case, "site/test". We
+will see additional information on controller actions throughout the
+rest of the tutorial, but if you are curious take a look at
+L<Catalyst::Manual::Intro/Actions>.
-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.
+It's not actually necessary to set the template value as we do here.
+By default TT will attempt to render a template that follows the
+naming pattern "controller/method.tt", and we're following that
+pattern here. However, in other situations you will need to specify
+the template (such as if you've "forwarded" to the method, or if it
+doesn't follow the default naming convention).
-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).
+We've also put the variable "username" into the stash, for use in the
+template.
+Make a subdirectory "site" in the "root" directory. Copy the hello.tt
+file into the directory as C<root/site/test.tt>, or create a new
+template file at that location. Include a line like:
-=head1 RETURN TO A MANUALLY-SPECIFIED TEMPLATE
+ <p>Hello, [% username %]!</p>
-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>:
+Bring up or restart the server. Notice in the server output that
+C</site/test> is listed in the Loaded Path actions. Go to
+L<http://localhost:3000/site/test> in your browser.
- $c->stash->{template} = 'books/list.tt2';
+You should see your test.tt file displayed, including the name "John"
+that you set in the controller.
-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
+=head1 AUTHORS
+Gerda Shank, C<gerda.shank@gmail.com>
Kennedy Clark, C<hkclark@gmail.com>
Please report any errors, issues or suggestions to the author. The
most recent version of the Catalyst Tutorial can be found at
-L<http://dev.catalyst.perl.org/repos/Catalyst/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/>).
+L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.70/trunk/lib/Catalyst/Manual/Tutorial/>.
+Copyright 2006-2008, Kennedy Clark & Gerda Shank, under Creative Commons License
+(L<http://creativecommons.org/licenses/by-sa/3.0/us/>).