[Catalyst-commits] r14415 - in trunk/examples/CatalystAdvent/root: 2012 static static/2012

jnapiorkowski at dev.catalyst.perl.org jnapiorkowski at dev.catalyst.perl.org
Wed Dec 5 17:26:06 GMT 2012


Author: jnapiorkowski
Date: 2012-12-05 17:26:06 +0000 (Wed, 05 Dec 2012)
New Revision: 14415

Added:
   trunk/examples/CatalystAdvent/root/2012/10.pod
   trunk/examples/CatalystAdvent/root/2012/11.pod
   trunk/examples/CatalystAdvent/root/2012/12.pod
   trunk/examples/CatalystAdvent/root/2012/13.pod
   trunk/examples/CatalystAdvent/root/2012/14.pod
   trunk/examples/CatalystAdvent/root/2012/15.pod
   trunk/examples/CatalystAdvent/root/2012/6.pod
   trunk/examples/CatalystAdvent/root/2012/7.pod
   trunk/examples/CatalystAdvent/root/2012/8.pod
   trunk/examples/CatalystAdvent/root/2012/9.pod
   trunk/examples/CatalystAdvent/root/static/2012/
   trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.01.tar.gz
   trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.02.tar.gz
   trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.03.tar.gz
   trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.04.tar.gz
   trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.05.tar.gz
   trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.06.tar.gz
   trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.07.tar.gz
   trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.08.tar.gz
   trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.09.tar.gz
Log:
really adding the articles this time

Added: trunk/examples/CatalystAdvent/root/2012/10.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2012/10.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2012/10.pod	2012-12-05 17:26:06 UTC (rev 14415)
@@ -0,0 +1,248 @@
+=head1 Catalyst in 9 steps - step 4: A light controller, light model, standalone lib and a TT view
+
+Note: You can download the archive with the application created in this fourth step from:
+
+=begin xhtml
+
+<a href="/calendar/static/2012/WebApp-0.04.tar.gz">WebApp-0.04.tar.gz</a>
+
+=end xhtml
+
+We now have an independent library that does the business logic, some CLI apps and a Catalyst web app that can access it. There is a light model that makes the independent library available in the web app and a controller that does the web logic and the HTML formatting of the results.
+
+Our app is better than it was after the first step, but it still has a problem. If the app will grow and we would need to collaborate with a professional web designer, it would be very hard to her to understand the whole Perl code mixed with the HTML code, especially if she doesn't know Perl.
+It would be better if we would let the controller do only web logic which is the job of the controllers and let the views format the results in the format we want (as HTML, css, xls, JSON or others).
+
+The controller should just get the data structure from the model and then forward it to the view that will format it as we want. If a certain view should format that data structure to a simple format like css or JSON, that view should only receive the data structure and possible a few configuration data, but nothing more.
+On the other hand, if that view should format the data structure as HTML, then it is not enough if it receives only that data structure and possible some configuration data, because an HTML document might be much more complex than a css or JSON one, so it should have an HTML template and embed that data structure in it.
+
+In order to use templates we must use a templating system like L<Template-Toolkit|https://metacpan.org/module/Template>, L<HTML::Template|https://metacpan.org/module/HTML::Template>, L<Mason|https://metacpan.org/module/Mason> or others.
+
+There are many templating systems that can be used in a Catalyst app, but the most used templating system is Template-Toolkit and it is also the one used by default in most Catalyst tutorials, so we will use it in our app also.
+
+In order to be able to use Template-Toolkit templates in the application, we need to install the modules Template-Toolkit and L<Catalyst::View::TT|https://metacpan.org/module/Catalyst::View::TT> using either cpan, cpanm or ppm.
+
+After doing this, the first thing is to create a view which inherits from the module Catalyst::View::TT. We will create a view named TT.pm in the directory lib/WebApp/View. The content of this module will be:
+
+    package WebApp::View::TT;
+    use strict;
+    use warnings;
+    use base 'Catalyst::View::TT';
+    1;
+
+That's all. You've seen that the view is also very light and very similar to the light model we created, but it inherits from Catalyst::View::TT and not from Catalyst::Model::Adaptor.
+
+Just as the model needed some configuration data that let it know what's the standalone class it needs to instantiate and what's the path to the database file, the view also needs some configuration that lets it know where it can find the template files and other few things.
+
+We will add the configuration directly in the config file now. This configuration will look like:
+
+    <View::TT>
+        INCLUDE_PATH "__path_to(root,templates)__"
+        TEMPLATE_EXTENSION .tt
+        render_die 1
+    </View::TT>
+
+Here are the short explanations for these lines:
+
+        INCLUDE_PATH "__path_to(root,templates)__"
+
+By default, the view will look for the templates directly in the "root" directory where we also have other items. If we like to have a distinct directory just for templates, we will create a directory named "templates" under the "root" directory, but in this case we need to specify the path to this directory using the key INCLUDE_PATH in the configuration file.
+
+        TEMPLATE_EXTENSION .tt
+
+By default, the path to the template that will be used by a controller action (subroutine) will be composed from the name of the controller and the name of that action. For example, the subroutine add() from the controller WebApp::Controller::Manage will be in our case:
+
+    root/templates/manage/add
+
+But if we want that all the template files to have the .tt extension, we must specify that extension using the key TEMPLATE_EXTENSION in the config file.
+If we do that, the template file for the add() subroutine will be:
+
+    root/templates/manage/add.tt
+
+        render_die 1
+
+This key should be set to on for new applications to throw an error in case of errors in the templates.
+
+We now need to delete the HTML code from the controller and put into templates.
+
+After deleting the HTML code from the controller, it will look like:
+
+    package WebApp::Controller::Manage;
+    use strict;
+    use warnings;
+    use base 'Catalyst::Controller';
+
+    sub add : Local {
+        my ( $self, $c ) = @_;
+
+        if ( $c->request->params->{submit} ) {
+            $c->model( 'DB' )->add( $c->request->params );
+            $c->res->redirect( "/manage" );
+        }
+    }
+
+    sub edit : Local {
+        my ( $self, $c, $wanted_id ) = @_;
+
+        if ( $c->request->params->{submit} ) {
+            $c->model( 'DB' )->edit( $wanted_id, $c->request->params );
+            $c->res->redirect( "/manage" );
+        }
+        else {
+            my $members = $c->model( 'DB' )->retrieve( $wanted_id );
+
+            $c->stash(
+                wanted_id => $wanted_id,
+                first_name => $members->[0]{first_name},
+                last_name => $members->[0]{last_name},
+                email => $members->[0]{email},
+            );
+        }
+    }
+
+    sub delete : Local {
+        my ( $self, $c, $wanted_id ) = @_;
+        $c->model( 'DB' )->delete( $wanted_id );
+        $c->res->redirect( "/manage" );
+    }
+
+    sub index :Path :Args(0) {
+        my ( $self, $c ) = @_;
+        my $members = $c->model( 'DB' )->retrieve;
+        $c->stash( members => $members );
+    }
+
+    1;
+
+
+In the add() subroutine remained just the code which verifies if the form was submitted until now, and the code that was printing the HTML code has been deleted.
+
+It might look strange that the application works as it should even though there is no code nowhere to tell it what to do in case that the form was not submitted.
+
+Simplified, the flow is something like:
+
+At the end of a controller action (subroutine), $c->response->body should not be null. It should contain something to be printed on the browser.
+
+If $c->response->body is null, that subroutine must forward to one of the views which were created in the application, and that view will generate that content and it will store it in $c->response->body.
+
+The forward to a view could be done with a line like:
+
+    $c->forward( 'View::TT' );
+
+If this line is used, Catalyst forwards to the view WebApp::View::TT.
+If at the end of the subroutine there is no such a line, like in the case of our add() subroutine, Catalyst will forward to the default view.
+
+In case there is just a single view defined in the application, like in our case, that view will be the default view, so no need to forward to it, because Catalyst will forward automaticly if there is no content in $c->response->body.
+
+If there are more views defined in the application, but we want to forward to the default view without explicit use of $c->forward, we need to specify in the configuration file which of them is the default one, using a code like:
+
+    default_view TT
+
+So in case the add form was not submitted, the add() subroutine finishes, Catalyst sees that there is no explicit forward to any view, so it forwards to the default view, which is the TT view because it is the single one we have.
+
+TT view gets the parameters we stored in $c->stash (there are no such parameters defined now in add() subroutine), reads the template root/templates/manage/add.tt, generate the final HTML content from that template and store the results in $c->response->body which is then sent to the browser.
+
+In the edit() subroutine we deleted the HTML code and we added a code which puts in the stash the variables we need to embed in the template in the specified places:
+
+            $c->stash(
+                wanted_id => $wanted_id,
+                first_name => $members->[0]{first_name},
+                last_name => $members->[0]{last_name},
+                email => $members->[0]{email},
+            );
+
+The values of this hash will replace in the template the special places which contain variables with the same name as the keys of this hash.
+For example, in the template we will have a place noted as [% wanted_id %]. It will be replaced with the value of the variable $wanted_id because we added "wanted_id => $wanted_id" in the stash.
+
+At the end of the edit() subroutine Catalyst will also do an implicit forward to the default view which will render the template root/templates/manage/edit.tt.
+
+Because the delete subroutine doesn't print anything but just deletes the record from the database and redirects to the page with the list of persons, we don't have HTML in it, so we don't change anything in it.
+
+In the index() subroutine we delete the HTML code and the loop that generates the table rows and we move that logic to the index.tt template.
+
+In place of that code we just added a line which adds the arrayref $members to the stash for making it available in the template using:
+
+        $c->stash( members => $members );
+
+And at the end of the subroutine Catalyst also forwards to the default view that renders the template and generate the table with persons.
+
+We will now see how the templates we created look like.
+
+The add.tt template contains exactly the same HTML code it was in the add() subroutine of the controller, because it is just a static HTML content, with no variables to embed inside. We just arranged it a little.
+
+In the edit.tt template the HTML code outside the form is exactly the same as it was in the controller, but the code inside the form look a little bit different:
+
+        <form action="/manage/edit/[% wanted_id %]" method="post">
+          First name: <input type="text" name="first_name" value="[% first_name %]" /><br />
+          Last name: <input type="text" name="last_name" value="[% last_name %]" /><br />
+          Email: <input type="text" name="email" value="[% email %]" /><br />
+          <input type="submit" name="submit" value="Save" />
+        </form>
+
+So instead of including the name of the Perl variables in the template we included markers that will be replaced with the values of their corresponding variables from the stash.
+
+The biggest changes were made in the template index.tt which should generate the table with all the persons from the database.
+
+The code outside the table is not changed but only arranged a little. Here is the content of the template that generates the table:
+
+        <table>
+        [% FOREACH m IN members %]
+          <tr>
+            <th>[% m.id %]</th>
+            <td><a href="/manage/edit/[% m.id %]">[% m.last_name %], [% m.first_name %]</a></td>
+            <td>[% m.email %]</td>
+            <td><a href="/manage/delete/[% m.id %]">delete member</a></td>
+          </tr>
+        [% END %]
+        </table>
+
+The following directive:
+
+        [% FOREACH m IN members %]
+
+is similar with the following line in Perl:
+
+    foreach $m ( @$members ) {
+
+The following directive:
+
+    [% m.id %]
+
+is similar with the following one in Perl:
+
+    $m->{id}
+
+The FOREACH directive is ended with the [% END %].
+
+Of course, these are just a few of the features offered by Template-Toolkit, but the other features it offers are not more complicated than them.
+
+It is recommended to read the POD docs of:
+    L<Template::Manual::Intro|https://metacpan.org/module/Template::Manual::Intro>
+    L<Template::Manual::Directives|https://metacpan.org/module/Template::Manual::Directives>
+    L<Template::Manual::Syntax|https://metacpan.org/module/Template::Manual::Syntax>
+    L<Template::Manual::Variables|https://metacpan.org/module/Template::Manual::Variables>
+    L<Template::Manual::VMethods|https://metacpan.org/module/Template::Manual::VMethods>
+    L<Template::Manual::Filters|https://metacpan.org/module/Template::Manual::Filters>
+    L<Template::Manual::Config|https://metacpan.org/module/Template::Manual::Config>
+
+
+We have now a standalone library that we can access with CLI scripts and with a web interface offered by a Catalyst app. The Catalyst app uses a light model that access the standalone library, a controller that does only the web logic as it should, and a light view that uses Template-Toolkit to render the templates as HTML. These templates are stored in distinct files which are much easier to understand by a web designer.
+
+As we are going to do on each step, we will test the application using the same actions:
+
+Run again the development server:
+
+    perl script/webapp_server.pl
+
+And then access it at the following URL:
+
+    http://localhost:3000/manage
+
+Click on the "Add new member". It will open the page with the add form.
+Add some data in the form and submit it. It will add that record in the database and it will redirect to the page with the persons.
+Click on the name of the person. It will open the edit form. Change some values and submit that form. It will make the change in the database and it will redirect to the page with the list of persons.
+Click on "Delete member" link for the person you want. It will delete that record from the database and it will redirect to the page with the list of persons.
+
+=head2 Author:
+
+Octavian Rasnita <orasnita at gmail.com>

Added: trunk/examples/CatalystAdvent/root/2012/11.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2012/11.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2012/11.pod	2012-12-05 17:26:06 UTC (rev 14415)
@@ -0,0 +1,190 @@
+=head1 Catalyst in 9 steps - step 5: A controller with a new SQLite DB, a light model and a TT view
+
+Note: You can download the archive with the application created in this fifth step from:
+
+=begin xhtml
+
+<a href="/calendar/static/2012/WebApp-0.05.tar.gz">WebApp-0.05.tar.gz</a>
+
+=end xhtml
+
+Our application has now a better structure, but a big problem is still the fact that the core of the application, the standalone library that does the business logic is pretty poor. As we said at the beginning, it can't accept values that contain vertical bars (|) because that character is used as a field separator. It also can't accept values with end of line chars. It is not scalable and it would not work well if we need to add a very large number of records in it. So a good idea would be to use a relational database instead.
+
+We will use an L<SQLite|http://www.sqlite.org/> database because it is easier to use than other relational databases which require a server like MySQL, PostgreSQL or oracle, but the application will be very similar if we would be using one of those databases.
+
+We could use directly the module L<DBI|https://metacpan.org/module/DBI> to connect to the relational databases, but in order to have a secure database connection which gets reconnected if it drops, we need to use a model which inherits from L<Catalyst::Model::DBI|https://metacpan.org/module/Catalyst::Model::DBI>. This model uses L<DBIx::Connector|https://metacpan.org/module/DBIx::Connector> which automaticly re-makes the connection to the database if it drops.
+
+So we will need to have the modules L<DBD::SQLite|https://metacpan.org/module/DBD::SQLite> and Catalyst::Model::DBI installed.
+
+We will be able to use the database handle offered by this model in our controller, so with other words we will be doing the business logic in the controller.
+If we don't need to have a standalone application that can run outside of the Catalyst app we can use this method because it is more simple.
+
+Here is what we need to do to change our application to use a relational database instead of that text file:
+
+First we will need to delete some things that won't be necessary from now:
+
+    - the directory standalone_app1_script which contains the CLI scripts that can access the text file database;
+    - the module lib/StandaloneApp1.pm;
+    - the test script t/04standalone_app1.t
+    - the configuration data for the model Standalone from the file webapp.conf.
+
+Then we will need to create our database, using L<sqlite3|http://www.sqlite.org/download.html> application.
+
+We will name the new SQLite database file database_file2.db and we will place it in the same "data" directory.
+So, in order to create this database, we will change the current directory to the "data" directory and we will give the command:
+
+    sqlite3 database_file2.db
+
+This command will create the empty database and it will change the prompt to "sqlite>".
+At this prompt we can give any SQL commands we want, or the command .help for more help or .exit for exitting to the shell prompt.
+
+We will type the following SQL code, which can be found in the file named database2.sql in the main directory of the application:
+
+    create table if not exists persons(
+        id integer primary key autoincrement,
+        first_name text,
+        last_name text,
+        email text
+    );
+
+Now we need to modify the model DB.pm which inherits from Catalyst::Model::Adaptor and make it inherit from Catalyst::Model::DBI. Its content will become:
+
+    package WebApp::Model::DB;
+    use strict;
+    use warnings;
+    use base 'Catalyst::Model::DBI';
+    1;
+
+And just as the old model, it also needs some settings which we will add in the configuration file of the application.
+
+The configuration for this model will look like:
+
+    <Model::DB>
+        dsn dbi:SQLite:__path_to(data,database_file2.db)__
+    </Model::DB>
+
+In this configuration appears the tipical DSN we use when connecting to a database with DBI.
+In the path to the SQLite database file we used again the macro __path_to()__ for specifying the file name data/database_file2.db.
+
+If you'll want to use another database like MySQL for example, the configuration could accept other needed options like:
+
+    <Model::DB>
+        dsn dbi:mysql:database_name
+        user the_user_name
+        password the_password
+    </Model::DB>
+
+After we created the table in the database and the model and added its configuration to the config file, we just need to change the code in the controller Manage.pm to work with the new database type.
+
+After this change it will have the following content:
+
+    package WebApp::Controller::Manage;
+    use strict;
+    use warnings;
+    use base 'Catalyst::Controller';
+
+    sub add : Local {
+        my ( $self, $c ) = @_;
+
+        if ( $c->request->params->{submit} ) {
+            my $first_name = $c->request->params->{first_name};
+            my $last_name = $c->request->params->{last_name};
+            my $email = $c->request->params->{email};
+
+            my $dbh = $c->model( 'DB' )->dbh;
+
+            my $p = $dbh->prepare( "insert into persons(first_name, last_name, email) values(?,?,?)" );
+
+            $p->execute( $first_name, $last_name, $email );
+
+            $c->res->redirect( "/manage" );
+        }
+    }
+
+    sub edit : Local {
+        my ( $self, $c, $wanted_id ) = @_;
+
+        my $dbh = $c->model( 'DB' )->dbh;
+
+        if ( $c->request->params->{submit} ) {
+            my $first_name = $c->request->params->{first_name};
+            my $last_name = $c->request->params->{last_name};
+            my $email = $c->request->params->{email};
+
+            my $p = $dbh->prepare( "update persons set first_name=?, last_name=?, email=? where id=?" );
+
+            $p->execute( $first_name, $last_name, $email, $wanted_id );
+
+            $c->res->redirect( "/manage" );
+        }
+        else {
+            my $p = $dbh->prepare( "select first_name, last_name, email from persons where id=?" );
+            $p->execute( $wanted_id );
+            my ( $first_name, $last_name, $email ) = $p->fetchrow_array;
+
+            $c->stash(
+                wanted_id => $wanted_id,
+                first_name => $first_name,
+                last_name => $last_name,
+                email => $email,
+            );
+        }
+    }
+
+    sub delete : Local {
+        my ( $self, $c, $wanted_id ) = @_;
+
+        my $dbh = $c->model( 'DB' )->dbh;
+        my $p = $dbh->prepare( "delete from persons where id=?" );
+        $p->execute( $wanted_id );
+
+        $c->res->redirect( "/manage" );
+    }
+
+    sub index :Path :Args(0) {
+        my ( $self, $c ) = @_;
+
+        my $dbh = $c->model( 'DB' )->dbh;
+        my $members = $dbh->selectall_arrayref( "select * from persons order by id", { Slice => {} } );
+
+        $c->stash( members => $members );
+    }
+
+    1;
+
+
+The code specific to the relational database we use in the add() subroutine is:
+
+    my $dbh = $c->model( 'DB' )->dbh;
+
+    my $p = $dbh->prepare(
+        "insert into persons(first_name, last_name, email) values(?,?,?)" );
+
+    $p->execute( $first_name, $last_name, $email );
+
+The first line of this code calls the model DB and the method dbh() that returns the database handle.
+This line of code appears in all the subroutines of this controller.
+
+The last 2 lines is common code for adding a record in a relational database.
+All the subroutines of this controller do a similar thing: Gets the $dbh database handle, then add/edit/delete/select the records to/from the database, so there is anything special.
+
+So in this step, after we created the database, we created the light model DB.pm and added its configuration and then changed the controller Manage.pm to work with this database.
+
+As we are going to do on each step, we will test the application using the same actions:
+
+Run again the development server:
+
+    perl script/webapp_server.pl
+
+And then access it at the following URL:
+
+    http://localhost:3000/manage
+
+Click on the "Add new member". It will open the page with the add form.
+Add some data in the form and submit it. It will add that record in the database and it will redirect to the page with the persons.
+Click on the name of the person. It will open the edit form. Change some values and submit that form. It will make the change in the database and it will redirect to the page with the list of persons.
+Click on "Delete member" link for the person you want. It will delete that record from the database and it will redirect to the page with the list of persons.
+
+=head1 Author:
+
+Octavian Rasnita <orasnita at gmail.com>

Added: trunk/examples/CatalystAdvent/root/2012/12.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2012/12.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2012/12.pod	2012-12-05 17:26:06 UTC (rev 14415)
@@ -0,0 +1,161 @@
+=head1 Catalyst in 9 steps - step 6: A light controller, a fat model with an SQLite DB and a TT view 
+
+Note: You can download the archive with the application created in this sixth step from:
+
+=begin xhtml
+
+<a href="/calendar/static/2012/WebApp-0.06.tar.gz">WebApp-0.06.tar.gz</a>
+
+=end xhtml
+
+We managed to change the database type of the application from a simple text file to a more reliable and scalable relational database, but now we have business code in the controller again and if we will need to use a part of it in other controllers, we would need to duplicate that code, which is bad.
+
+The previous controller Manage.pm that we made on step 4 is much better because it contains just web logic code and not business code. It cleanly calls the model DB and its methods add(), edit(), delete() and retrieve(), and those subroutines from the model do the business logic.
+
+So we will continue with the application we made in the previous step, but we will abandon the controller Manage.pm that we made in that step and we will get and use the controller Manage.pm we made at step 4 because it is much better.
+
+We will add the subroutines add/edit/delete/retrieve in the model DB.pm that we created in the previous step, and in the Manage controller we will just execute those subroutines.
+
+We will see that these subroutines will be very similar to the code we added in the controller Manage.
+
+After adding these subroutines to the model DB, it will become a fat model, (like the model we used in the step 2), and it will have the following content:
+
+    package WebApp::Model::DB;
+    use strict;
+    use warnings;
+    use base 'Catalyst::Model::DBI';
+
+    sub add {
+        my ( $self, $params ) = @_;
+
+        my $first_name = $params->{first_name};
+        my $last_name = $params->{last_name};
+        my $email = $params->{email};
+
+        my $dbh = $self->dbh;
+
+        my $p = $dbh->prepare( "insert into persons(first_name, last_name, email) values(?,?,?)" );
+
+            $p->execute( $first_name, $last_name, $email );
+    }
+
+    sub edit {
+        my ( $self, $wanted_id, $params ) = @_;
+
+        my $first_name = $params->{first_name};
+        my $last_name = $params->{last_name};
+        my $email = $params->{email};
+
+        my $dbh = $self->dbh;
+
+        my $p = $dbh->prepare( "update persons set first_name=?, last_name=?, email=? where id=?" );
+
+        $p->execute( $first_name, $last_name, $email, $wanted_id );
+    }
+
+    sub delete {
+        my ( $self, $wanted_id ) = @_;
+
+        my $dbh = $self->dbh;
+
+        my $p = $dbh->prepare( "delete from persons where id=?" );
+        $p->execute( $wanted_id );
+    }
+
+    sub retrieve {
+        my ( $self, $wanted_id ) = @_;
+
+        my $dbh = $self->dbh;
+
+        my $members;
+
+        if ( $wanted_id ) {
+            $members = $dbh->selectall_arrayref( "select * from persons where id=? order by id",
+              { Slice => {} }, $wanted_id );
+        }
+        else {
+            $members = $dbh->selectall_arrayref( "select * from persons order by id",
+              { Slice => {} } );
+        }
+
+        return $members;
+    }
+
+    1;
+
+
+The subroutines will receive the same parameters like the subroutines from the fat model which we used on step 2.
+The difference between the current model and the model from step 2 is that now the business logic use a relational database and not a text file for storing data.
+Another difference between these 2 models is that the current model doesn't inherit from Catalyst::Model, but from Catalyst::Model::DBI, which offers in addition a few more subroutines, one of them beeing dbh() that returns the handle to the database.
+
+The add() subroutine will be called with a parameter $params which is a hashref and from this hashref we are getting the variables $first_name, $last_name and $email.
+
+        my $dbh = $self->dbh;
+
+This line calls the method dbh() of the current model which returns the database handle.
+
+Then the next lines add the new record in the database, using the identical code we used in the controller Manage.
+
+The subroutines edit() and delete() do similar things, but they don't add a new record but update or delete an existing record.
+
+The subroutine retrieve() does 2 things: If it is called without parameters, it selects and returns an arrayref with all the records from the database, and if it is called with a parameter - the ID of the record we want, it selects and returns an arrayref that contain just the wanted record.
+
+So, what do we have now?
+
+On this step we just moved the business logic from the controller Manage to the model DB.
+We are now using the old controller Manage that we created on step 4 without any change. We use the same SQLite database as in the previous step, the same configuration for the model, the same view and templates.
+The advantage of moving the business logic to the model is that now we are able to call it in more controllers if we need it, without needing to have duplicated code.
+
+Because the controller Manage just calls the methods from the model, it is still working fine even though it was made to work when we were using a text file database.
+And because the new model returns the same type of data to the controller which then forwards it to the view, the view and the templates are also working without any change.
+
+This is the advantage of separation of code in controllers, models and views. We can use the same controller, view and templates with a totally changed business logic.
+And if the web designers would want to add design elements in the templates, they will work with existing views, controllers and models if the new design doesn't need additional data from database.
+
+As we are going to do on each step, we will test the application using the same actions:
+
+Run again the development server:
+
+    perl script/webapp_server.pl
+
+And then access it at the following URL:
+
+    http://localhost:3000/manage
+
+Click on the "Add new member". It will open the page with the add form.
+Add some data in the form and submit it. It will add that record in the database and it will redirect to the page with the persons.
+Click on the name of the person. It will open the edit form. Change some values and submit that form. It will make the change in the database and it will redirect to the page with the list of persons.
+Click on "Delete member" link for the person you want. It will delete that record from the database and it will redirect to the page with the list of persons.
+
+Each time we created a Catalyst component (controller, model or view) we created the file and its content manually, but Catalyst offers more helper scripts that can be used to create these components with a command line.
+
+Here are a few examples of such commands:
+
+Create a Manage controller which inherits from Catalyst::Controller:
+
+    perl script/webapp_create.pl controller Manage
+
+Create a DB model which inherits from Catalyst::Model:
+
+    perl script/webapp_create.pl model DB
+
+Create a DB model which inherits from Catalyst::Model::DBI:
+
+    perl script/webapp_create.pl model DB DBI
+
+Create a DB model which inherits from Catalyst::Model::Adaptor:
+
+    perl script/webapp_create.pl model DB Adaptor
+
+Create a TTView view which inherits from Catalyst::View::TT:
+
+    perl script/webapp_create.pl view TTView TT
+
+We haven't used these commands in our application because some of the helper scripts use Moose, some of them don't use it but instead use "use base" or "use parent" and it might have been a little unclear for those who don't know Moose yet why is this difference.
+We also wanted to show that the components we need to create in a Catalyst application can work without Moose.
+
+Moose is great, but it may be a stopper for those who don't know it but want to use Catalyst, not because Catalyst cannot be used without it, but because most Catalyst tutorials include it.
+
+=head2 Author:
+
+Octavian Rasnita <orasnita at gmail.com>

Added: trunk/examples/CatalystAdvent/root/2012/13.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2012/13.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2012/13.pod	2012-12-05 17:26:06 UTC (rev 14415)
@@ -0,0 +1,222 @@
+=head1 Catalyst in 9 steps - step 7: Light model, standalone lib, SQLite DB, TT view, CLI scripts
+
+Note: You can download the archive with the application created in this seventh step from:
+
+=begin xhtml
+
+<a href="/calendar/static/2012/WebApp-0.07.tar.gz">WebApp-0.07.tar.gz</a>
+
+=end xhtml
+
+The application we created on the previous step is good if we don't need to access its database using other applications.
+
+If we need to create CLI scripts or other external applications to do the same business logic, than using a fat model that inherits from L<Catalyst::Model::DBI|https://metacpan.org/module/Catalyst::Model::DBI> is not a good solution.
+
+Just like when we were using a text file as a database, the solution is to create a standalone library which provides the methods for accessing the relational database.
+As on step 3, we will do this in the same way, by creating a light model which inherits from L<Catalyst::Model::Adaptor|https://metacpan.org/module/Catalyst::Model::Adaptor> and a standalone library that provides the business logic.
+
+The model DB.pm will have the following content:
+
+    package WebApp::Model::DB;
+    use strict;
+    use warnings;
+    use base 'Catalyst::Model::Adaptor';
+    1;
+
+This model has the same content as it had on step 3 but only its configuration will be different because this time the standalone library doesn't need to know the path to a text file database but the DSN to a relational database.
+
+Here is the new configuration for the model DB.pm:
+
+    <Model::DB>
+        class StandaloneApp2
+        <args>
+            dsn dbi:SQLite:__path_to(data,database_file2.db)__
+        </args>
+    </Model::DB>
+
+This configuration is similar to the one we used on step 3, because both of them use "class" and "args" keys. This is because the models that inherit from Catalyst::Model::Adaptor use them.
+
+The standalone library will be named StandaloneApp2.pm and it will have the same interface as the old StandaloneApp1.pm, so it will also contain the subroutines add/edit/delete/retrieve, but instead of working with a text database, these subroutines will access a relational database.
+
+Usually when we access a relational database we use the Perl module L<DBI|https://metacpan.org/module/DBI>, but if we will use DBI only, we wouldn't have a safe database connection because if the connection to the database drops, DBI won't make the reconnection so we would need to write code that checks if the connection is working, and reconnect if the connection died.
+
+Fortunately, there is a better option. We can use the module L<DBIx::Connector|https://metacpan.org/module/DBIx::Connector> which offers a secure database connection, because it reconnects automaticly if the connection has dropped.
+
+In our previous step we used the module Catalyst::Model::DBI as a base for our model, but this module also uses DBIx::Connector transparently. Now that we need to create our standalone library we would need to explicitly use the module DBIx::Connector, but fortunately its interface is very simple.
+
+So here it is the content of the new standalone library lib/StandaloneApp2.pm:
+
+    package StandaloneApp2;
+
+    use strict;
+    use warnings;
+    use DBIx::Connector;
+
+    sub new {
+        my ( $class, $params ) = @_;
+        my $self = $params;
+
+        my $dsn = $self->{dsn};
+        my $user = $self->{user};
+        my $password = $self->{password};
+
+        my $conn = DBIx::Connector->new( $dsn, $user, $password );
+        $self->{conn} = $conn;
+
+        return bless $self, $class;
+    }
+
+    sub dbh {
+        my ( $self ) = @_;
+        return $self->{conn}->dbh;
+    }
+
+    sub add {
+        my ( $self, $params ) = @_;
+
+        my $first_name = $params->{first_name};
+        my $last_name = $params->{last_name};
+        my $email = $params->{email};
+
+        my $dbh = $self->dbh;
+
+        my $p = $dbh->prepare( "insert into persons(first_name, last_name, email) values(?,?,?)" );
+
+            $p->execute( $first_name, $last_name, $email );
+    }
+
+    sub edit {
+        my ( $self, $wanted_id, $params ) = @_;
+
+        my $first_name = $params->{first_name};
+        my $last_name = $params->{last_name};
+        my $email = $params->{email};
+
+        my $dbh = $self->dbh;
+
+        my $p = $dbh->prepare( "update persons set first_name=?, last_name=?, email=? where id=?" );
+
+        $p->execute( $first_name, $last_name, $email, $wanted_id );
+    }
+
+    sub delete {
+        my ( $self, $wanted_id ) = @_;
+
+        my $dbh = $self->dbh;
+
+        my $p = $dbh->prepare( "delete from persons where id=?" );
+        $p->execute( $wanted_id );
+    }
+
+    sub retrieve {
+        my ( $self, $wanted_id ) = @_;
+
+        my $dbh = $self->dbh;
+
+        my $members;
+
+        if ( $wanted_id ) {
+            $members = $dbh->selectall_arrayref( "select * from persons where id=? order by id",
+              { Slice => {} }, $wanted_id );
+        }
+        else {
+            $members = $dbh->selectall_arrayref( "select * from persons order by id",
+              { Slice => {} } );
+        }
+
+        return $members;
+    }
+
+    1;
+
+
+As you might have seen, the subroutines add/edit/delete/retrieve were moved here from the model DB.pm without any change.
+And now the model DB doesn't inherit from Catalyst::Model::DBI but from Catalyst::Model::Adaptor.
+
+So the new things are in the new() constructor and dbh() method of StandaloneApp2.pm.
+
+        my $dsn = $self->{dsn};
+
+The line above gets the DSN which is needed for database connection. It is configured in the config file.
+
+        my $user = $self->{user};
+        my $password = $self->{password};
+
+The lines above get the username and password needed to connect to the database if the database accept a user and a password. In our case the SQLite database doesn't need a username and password so we could have skipped using these variables, but it is better to write a more complete standalone library that could also work with other databases like MySQL, PostgreSQL or others.
+
+If we do this, we can use this standalone library to access a MySQL database by changing the DSN in the configuration file with something like:
+
+    dsn dbi:mysql:test
+    user the_username
+    password the_password
+
+We would just need to have a MySQL database named "test" and the table "persons" with the same columns created as in the SQLite database and our application will work using MySQL.
+
+        my $conn = DBIx::Connector->new( $dsn, $user, $password );
+
+This line creates the DBIx::Connector object which maintains the connection to the database.
+
+        $self->{conn} = $conn;
+
+This line stores the DBIx::Connector object in the $self hashref in order to be accessed later in all the methods of this standalone library.
+
+The dbh() method is just an accessor for the database handle. Without this method we need to access the database handle using $self->{conn}->dbh. This is not a problem, but the add/edit/delete/retrieve subroutines we moved from previous model already use $self->dbh because that model inherits from Catalyst::Model::DBI which offers the dbh() accessor. So it is more easy to offer this accessor in our standalone library too and let all the other subroutines unmodified.
+
+So, in this step we created the new standalone library StandaloneApp2.pm, moved all the subroutines from the model DB.pm in it, change the model DB.pm to inherit from Catalyst::Model::Adaptor, and changed the settings for this model in the configuration file.
+
+Our application does the same thing as before, but it has an important advantage, the fact that it has a standalone library which can be used in other applications and because now it is easier to test it.
+
+As we are going to do on each step, we will test the application using the same actions:
+
+Run again the development server:
+
+    perl script/webapp_server.pl
+
+And then access it at the following URL:
+
+    http://localhost:3000/manage
+
+Click on the "Add new member". It will open the page with the add form.
+Add some data in the form and submit it. It will add that record in the database and it will redirect to the page with the persons.
+Click on the name of the person. It will open the edit form. Change some values and submit that form. It will make the change in the database and it will redirect to the page with the list of persons.
+Click on "Delete member" link for the person you want. It will delete that record from the database and it will redirect to the page with the list of persons.
+
+Now that we have a working application which uses a relational database which is accessed with a standalone library that can be used in other applications, we will create again those command line scripts that can add/edit/delete/retrieve the records to/from the database. This time we will store these scripts in the directory standalone_app2_script.
+
+These scripts will be very similar with the scripts we created on step 3. They are not perfectly identical because now the standalone module is named StandaloneApp2 and not StandaloneApp1 as before, and because the constructor of this module doesn't receive a path to a text file database but a DSN to connect to a relational database.
+
+So in all these scripts there will be only 3 lines that differ:
+
+    use StandaloneApp2;
+
+    my $dsn = $config->{'Model::DB'}{args}{dsn};
+
+    my $app = StandaloneApp2->new( { dsn => $dsn } );
+
+The rest of the code in all these scripts is the same as it was on the step 3.
+
+We can also use these scripts as a test of the standalone application using commands like:
+
+    perl add.pl --first-name John --last-name Smith --email john at smith.com
+
+    perl edit.pl --id 1 --first-name George --last-name Smith --email j at smith.com
+
+    perl retrieve.pl --id 1
+
+    perl delete.pl --id 1
+
+We also created a test file named t/04standalone_app2.t which tests if the module StandaloneApp2.pm works fine.
+
+You can run all the tests using the following command in the main directory of the web application:
+
+    prove -l t
+
+The results should be:
+
+    All tests successful.
+    Files=4, Tests=44,  3 wallclock secs ( 0.01 usr +  0.00 sys =  0.01 CPU)
+    Result: PASS 
+
+=head2 Author:
+
+Octavian Rasnita <orasnita at gmail.com>

Added: trunk/examples/CatalystAdvent/root/2012/14.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2012/14.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2012/14.pod	2012-12-05 17:26:06 UTC (rev 14415)
@@ -0,0 +1,337 @@
+=head1 Catalyst in 9 steps - step 8: SQLite DB accessed with DBIC in the controller, CLI scripts
+
+Note: You can download the archive with the application created in this eighth step from:
+
+=begin xhtml
+
+<a href="/calendar/static/2012/WebApp-0.08.tar.gz">WebApp-0.08.tar.gz</a>
+
+=end xhtml
+
+We now have an application that can use a relational database. It uses a standalone library which can be also used easily in other programs and we have also made some CLI scripts that can use it. It does the HTML formatting in a view which uses Template-Toolkit templates.
+
+This application is very simple though, and it doesn't have too many SQL queries and the SQL code it uses is very basic. As we said, if we would change the DSN to make it use a MySQL database instead of SQLite, this application would work fine.
+
+But in other applications we might have much more complex queries and we might need to do a lot of string joining to compose the SQL code, which will lead to ugly source code which would be harder to maintain.
+The simple SQL queries we used are compatible with more database management systems and we are able to change the database types very easy. But if we will need to use complex queries, some of them will work with just a single database type, so that application would be much less flexible.
+
+For example, if we'd want to use a pretty simple SQL query like "select * from person limit 10", it will work with SQLite and MySQL databases, but it won't work with Oracle or MS SQL.
+
+The solution that would make our source code much elegant and easier to maintain and which would make the application compatible with more database types, is to use an L<ORM|http://en.wikipedia.org/wiki/Object-relational_mapping> (Object Relational Mapping).
+An ORM used in a Perl application is a Perl package of modules that can access a relational database using Perl objects, methods and data structures, and not simple SQL strings.
+
+The most used ORM in Perl which is also the preferred ORM in Catalyst applications is L<DBIx::Class|https://metacpan.org/module/DBIx::Class>, also known as DBIC.
+
+DBIC is a pretty complex package and it takes some time to learn it, but it increases the productivity very much. In this article we will improve our application to make it access the SQLite database using it. We won't give too many details about this ORM, but we will show how it can be used in a Catalyst app.
+
+You can read more about DBIC by reading the POD documentation of the following modules:
+
+    L<DBIx::Class|https://metacpan.org/module/DBIx::Class>
+    L<DBIx::Class::Manual::Intro|https://metacpan.org/module/DBIx::Class::Manual::Intro>
+    L<DBIx::Class::Manual::Example|https://metacpan.org/module/DBIx::Class::Manual::Example>
+    L<DBIx::Class::Manual::Cookbook|https://metacpan.org/module/DBIx::Class::Manual::Cookbook>
+    L<DBIx::Class::Manual::FAQ|https://metacpan.org/module/DBIx::Class::Manual::FAQ>
+    L<DBIx::Class::Manual::Joining|https://metacpan.org/module/DBIx::Class::Manual::Joining>
+
+Because our application is very simple and so are the SQL queries we used, it won't be very clear what's the advantage of using DBIC, but in more complex apps, the productivity improvement will be huge.
+
+In order to use DBIC, we will need to create a so called Result class for each table in the database. A Result class is a Perl module that contains information about the table, like the name of the table, the names of the columns, the primary key and other information.
+
+If we need to use DBIC to access a database that has many tables, it is very time consuming and error prone to create all those Perl modules manually, but fortunately there are methods to run a command that studies the internal structure of the database and create those modules automaticly. We can also use that method if we made some structural changes in the database and need to re-generate those Perl modules and it will work even after we already made some manual changes in them.
+
+To access a database using DBIC, in Catalyst apps we can create a model using a command line similar to the ones presented at the end of step 6, but this time we will create a model which inherits from Catalyst::Model::DBIC::Schema. Because this model will require more details, the command line will be pretty long, and because we might need it to use it again after we made some structural changes in the database, we can store it in a batch file.
+
+We will first delete the existing model DB.pm and then we will create a new model named DB.pm using the following command in the main directory of the application:
+
+    perl script/webapp_create.pl model DB DBIC::Schema StandaloneApp3 \
+    create=static dbi:SQLite:data/database_file2.db
+
+Here it is what means each component of this command:
+
+    perl script/webapp_create.pl model
+
+This means that we will create a model (and not a controller or view).
+
+    DB
+
+This is the name of the model we will create.
+
+    DBIC::Schema
+
+This is the type of the model, or with other words it means that this model will inherit from Catalyst::Model::DBIC::Schema
+
+    StandaloneApp3
+
+This will be the name of the standalone module that will be accessed with the model DB.pm. As we said, it is usually recommended to place these modules not directly under the lib directory, but under the directory with the name of the Catalyst application, in our case under lib/WebApp, so by recommendation this standalone module should have been named WebApp::StandaloneApp3. We named it just StandaloneApp3 in order to be created automaticly in the lib directory, because we also created there the previous standalone libraries StandaloneApp1 and StandaloneApp2.
+
+    create=static
+
+We need to use this command line parameter always in order to have the Result class modules generated and saved on the disk.
+
+    dbi:SQLite:data/database_file2.db
+
+This last parameter contains the DSN we need to access the database.
+
+After these parameters we can also add the username and the password as 2 distinct parameters in case the database we use needs them.
+
+This command will create the following files on the disk:
+
+    lib/StandaloneApp3.pm - the schema module of the standalone library
+    lib/StandaloneApp3/Result/Person.pm - the Result class for the table person
+    lib/WebApp/Model/DB.pm - the model that will access the standalone library
+    t/model_DB.t - the base of the test file for the model DB
+
+The model DB.pm will have the following content (plus some POD documentation):
+
+    package WebApp::Model::DB;
+    use strict;
+    use base 'Catalyst::Model::DBIC::Schema';
+    1;
+
+The Catalyst helper command we just used to create this model also added in it the configuration data we provided at command line, but we will delete this code from the DB.pm model and we will add it in the configuration file webapp.conf, because we might need this configuration data in other applications that might need to access our database using the DBIC schema StandaloneApp3.pm.
+
+In the configuration file this will appear as:
+
+    <Model::DB>
+        schema_class StandaloneApp3
+        <connect_info>
+            dsn dbi:SQLite:__path_to(data,database_file2.db)__
+        </connect_info>
+    </Model::DB>
+
+After we created the model and generated the DBIC classes and after we added the configuration to the config file, we will modify the controller Manage to access the database using DBIC. The controller will have the following content:
+
+    package WebApp::Controller::Manage;
+    use strict;
+    use warnings;
+    use base 'Catalyst::Controller';
+
+    sub add : Local {
+        my ( $self, $c ) = @_;
+
+        if ( delete $c->request->params->{submit} ) {
+            $c->model( 'DB::Person' )->create( $c->request->params );
+            $c->res->redirect( "/manage" );
+        }
+    }
+
+    sub edit : Local {
+        my ( $self, $c, $wanted_id ) = @_;
+
+        if ( delete $c->request->params->{submit} ) {
+            $c->model( 'DB::Person' )->find( $wanted_id )->update( $c->request->params );
+            $c->res->redirect( "/manage" );
+        }
+        else {
+            my $members = $c->model( 'DB::Person' )->find( $wanted_id );
+
+            $c->stash(
+                wanted_id => $wanted_id,
+                first_name => $members->first_name,
+                last_name => $members->last_name,
+                email => $members->email,
+            );
+        }
+    }
+
+    sub delete : Local {
+        my ( $self, $c, $wanted_id ) = @_;
+        $c->model( 'DB::Person' )->find( $wanted_id )->delete;
+        $c->res->redirect( "/manage" );
+    }
+
+    sub index :Path :Args(0) {
+        my ( $self, $c ) = @_;
+        my @members = $c->model( 'DB::Person' )->search( undef, { order_by => 'id' } )->all;
+        $c->stash( members => \@members );
+    }
+
+    1;
+
+
+In the add() subroutine only the following 2 lines were modified:
+
+        if ( delete $c->request->params->{submit} ) {
+            $c->model( 'DB::Person' )->create( $c->request->params );
+
+In the first line we added the delete function. The hashref $c->request->params contains the keys first_name, last_name, email and commit.
+We will send the whole hashref as a parameter to the DBIC method create() to create a new record in the database with the values that correspond to these keys.
+But in the database table there is no column named "commit", and if we would be sending it, we would receive an error, so we should delete it. The delete() function returns true if the hashref contained a key named "submit" so it has the same effect as when we just checked if there is a key named "submit" in that hash. With other words, if the form was submitted, the hashref contains a "commit" key, so the delete returns true.
+
+The second line is the way used by DBIC to create a new record with the values from the hashref sent as a parameter.
+The keys from this hash should correspond to the column names of the database table.
+
+This line is a shortened version of the longer line:
+
+            $c->model( 'DB' )->resultset( 'Person' )->create( $c->request->params );
+
+DB is the name of the model and Person is the Result class that corresponds to the "persons" table. DBIC by default singularizes the names of the tables when it generates the corresponding Result classes.
+
+In the edit() subroutine there are a few more lines which were modified:
+
+        if ( delete $c->request->params->{submit} ) {
+            $c->model( 'DB::Person' )->find( $wanted_id )->update( $c->request->params );
+
+The second line uses the model DB and the Result class Person. It first searches for the record with the primary key $wanted_id and then updates that record with the values from $c->request->params hashref.
+
+            my $members = $c->model( 'DB::Person' )->find( $wanted_id );
+
+The line above also used the model DB and the Result class Person and searched for the record with the primary key $wanted_id.
+
+                first_name => $members->first_name,
+                last_name => $members->last_name,
+                email => $members->email,
+
+These lines add the variables above to the stash in order to be then used for rendering the template edit.tt.
+You can see that the names of the columns are accessors of the object $members.
+
+In the delete() subroutine there is only a single line modified:
+
+        $c->model( 'DB::Person' )->find( $wanted_id )->delete;
+
+This line also uses the model DB and the Result class Person. It searches for the record with the primary key $wanted_id and it deletes that record.
+
+In the subroutine index() both lines of code were modified:
+
+        my @members = $c->model( 'DB::Person' )->search( undef, { order_by => 'id' } )->all;
+        $c->stash( members => \@members );
+
+The first line gets all the records from the database, so it doesn't use the find() method that gets only a single record, but the search() method which is more complex.
+This time we don't need to specify any search criteria in the first parameter of the method, so it is undef, but in the second parameter we specify that we want the found records to be ordered by their ID.
+
+The search() method returns a ResultSet object that can be iterate using the next() iterator with a syntax like:
+
+    while ( my $m = $members->next ) {
+        print $m->first_name;        #and also $m->last_name, $m->email
+    }
+
+In the index.tt template we don't use WHILE but FOREACH, so we need to add an arrayref in the stash as we did until now. In order to return an array of records from the ResultSet given by search() we needed to use the all() method.
+In the second line we added to the stash a reference to the @members array.
+
+And that's all. This time we don't need to create a standalone module manually as we did before, because that standalone module is now a DBIC schema and it was created automaticly together with the Result classes and the Catalyst model DB when we used that long command.
+
+For using DBIC in our application we just needed to run the Catalyst helper script (that long command) and change the Manage controller. (We have also moved the configuration code from the model DB to the config file, but the application can also work at this stage without that movement.)
+
+So we moved from an application that uses SQL queries to access the database to an application that uses DBIC without too much effort, and the new application will work with the same database, the same view, same template files as before.
+
+As we are going to do on each step, we will test the application using the same actions:
+
+Run again the development server:
+
+    perl script/webapp_server.pl
+
+And then access it at the following URL:
+
+    http://localhost:3000/manage
+
+Click on the "Add new member". It will open the page with the add form.
+Add some data in the form and submit it. It will add that record in the database and it will redirect to the page with the persons.
+Click on the name of the person. It will open the edit form. Change some values and submit that form. It will make the change in the database and it will redirect to the page with the list of persons.
+Click on "Delete member" link for the person you want. It will delete that record from the database and it will redirect to the page with the list of persons.
+
+
+Note: Even though the standalone library StandaloneApp3.pm is a different library than StandaloneApp2.pm, both libraries access the same database file data/database_file2.db, one using SQL and the other using DBIC.
+
+The Catalyst helper command created a standalone library for us without writing any code, and we can also use it in other programs to access the database of our application.
+
+We will create the scripts add.pl, edit.pl, delete.pl and retrieve.pl and we will place them in the directory standalone_app3_script. These scripts will be very similar to the scripts from the directory standalone_app2_script, so we will explain only the lines which are different.
+
+    # add.pl
+
+    use StandaloneApp3;
+
+    my $dsn = $config->{'Model::DB'}{connect_info}{dsn};
+
+This line is similar but the Catalyst helper created a configuration with a different structure, so we use "connect_info" key instead of "args".
+
+    my $app = StandaloneApp3->connect( $dsn );
+
+This line initializes the DBIC schema object using a similar syntax with the one used by the module DBI.
+
+    $app->resultset( 'Person' )->create( { first_name => $first_name,
+      last_name => $last_name, email => $email } );
+
+This line uses the Result class Person and creates a new record in the database with the values from the hashref given as a parameter to the create() method.
+The keys should be the names of the columns of the table persons.
+
+    # edit.pm
+
+    $app->resultset( 'Person' )->find( $id )->update( { first_name => $first_name,
+      last_name => $last_name, email => $email } );
+
+This line uses the Person Result class, gets the record with the primary key $id and updates it in the database with the new values given.
+The other changed lines that appear in this script were explained above for the script add.pl.
+
+    # delete.pl
+
+    $app->resultset( 'Person' )->find( $id )->delete;
+
+This line uses the Person Result class, gets the record with the primary key $id and deletes it.
+
+    #retrieve.pl
+
+    my $members = $app->resultset( 'Person' )->search( undef, { order_by => 'id' } );
+
+This line gets all the records from the table persons and orders them by the column "id".
+
+    $members = $members->search( { id => $id } ) if $id;
+
+If the command line parameter --id was used, so if the $id variable is true, this line refines the search by adding a new condition that the ID of the record should be the given ID. At this point, no search in the database is made yet, so other conditions and options can be added.
+
+    my @members = $members->all;
+
+This line executes the method all() which returns an array with all the records from the ResultSet $members.
+
+    for my $m ( @members ) {
+        print $m->id, ', ', $m->first_name, ', ', $m->last_name, ', ', $m->email, "\n";
+    }
+
+This code get each record from the @members array as an object. For returning the values of some columns of the record we can use the column names as methods of this object.
+
+We can also use these scripts as a test of the standalone library (the DBIC schema) using commands like:
+
+    perl add.pl --first-name John --last-name Smith --email john at smith.com
+
+    perl edit.pl --id 1 --first-name George --last-name Smith --email j at smith.com
+
+    perl retrieve.pl --id 1
+
+    perl delete.pl --id 1
+
+In order to be also able to use the old CLI scripts from the directory standalone_app2_script that use the old library StandaloneApp2.pm (which accesses the database using plain SQL), we changed the line that gets the DSN from the configuration file, because we modified that file.
+
+That line in the old CLI scripts was:
+
+    my $dsn = $config->{'Model::DB'}{args}{dsn};
+
+and now it is:
+
+    my $dsn = $config->{'Model::DB'}{connect_info}{dsn};
+
+We've seen that in previous steps when we were using a plain text database or when we were accessing the current SQLite database with plain SQL queries we needed to manually create the subroutines add(), edit(), delete() and retrieve() in the standalone library. Now that we use DBIC, it was pretty simple to skip creating those subroutines and use directly the methods offered by this ORM. In our example we have used the methods find() to find a record with a certain primary key, search() to search for one or more records with certain conditions and options, update() to update the found record with new data, delete() to delete the found record or records, next() to iterate the list of results from a ResultSet object and get each record, all() to create an array with records, but DBIC offer other methods for doing more complex things in an easier way.
+
+We have also created a test file t/05standalone_app3.t which tests if the DBIC schema works fine.
+
+In order to access the database, these test files, as well as the CLI scripts get the DSN from the configuration file made under the key Module::DB. Now that we changed this configuration, we should also change it in the old test file 04standalone_app2.t which tests if the database can be accessed using plain SQL because otherwise it won't be able to find the database.
+
+So in that test file we must changed the line:
+
+    my $dsn = "$config->{'Model::DB'}{args}{dsn}_test";
+
+with:
+
+    my $dsn = "$config->{'Model::DB'}{connect_info}{dsn}_test";
+
+And then we can run all the tests with:
+
+    prove -l t
+
+And it should finish with the results:
+
+    All tests successful.
+    Files=6, Tests=88,  4 wallclock secs ( 0.05 usr +  0.01 sys =  0.06 CPU)
+    Result: PASS 
+
+=head2 Author:
+
+Octavian Rasnita <orasnita at gmail.com>

Added: trunk/examples/CatalystAdvent/root/2012/15.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2012/15.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2012/15.pod	2012-12-05 17:26:06 UTC (rev 14415)
@@ -0,0 +1,333 @@
+=head1 Catalyst in 9 steps - step 9: SQLite DB accessed with DBIC in ResultSet classes, CLI scripts
+
+Note: You can download the archive with the application created in this ninth step from:
+
+=begin xhtml
+
+<a href="/calendar/static/2012/WebApp-0.09.tar.gz">WebApp-0.09.tar.gz</a>
+
+=end xhtml
+
+The application we made is a very simple one that just adds/edits/deletes/retrieves very small records so the advantages of the previous step are not obvious because for very simple SQL queries the ORMs can't improve the code too much. In the previous step we used business logic in the controller, because we accessed there the ORM directly, which theoreticly is not recommended.
+
+Theoreticly it is better to create a standalone library which offers custom methods for what it needs to do, and only that library then use whatever technology we decide in a certain moment.
+
+For example, for adding a record in the database we should be able to just initialize an object and use a common Perl method that we can use with exactly the same interface everywhere, in the Catalyst controllers, in command line programs, in test scripts. Something like:
+
+    $object->add( first_name => '...', last_name => '...', email => '...' );
+
+And then in our standalone library make the add() subroutine to do what we want to do. Maybe sometimes we want it to add the records in a text file, other times we want to add the records in a relational database using plain SQL queries, other times we want to use DBIC, maybe once we will want to create another standalone library with the same interface that will use another ORM... but all those versions of the standalone library should use the same interface, so the add() subroutine should accept the same parameters.
+
+The advantage would be that after changing the standalone library with a new one we wouldn't need to change anything else because all the controllers, CLI scripts, test files will send the same parameters and the new standalone library would work.
+
+But this is theory, which is not always very practical.
+
+If we use a lower level way to access a database, like when we use a plain text file as a database or when we access a relational database using SQL queries, we need to write some more custom code manually, and in that code we can easily return the result as any data structure we want.
+When we use a higher level way to access a database, like when we use an ORM, that ORM returns the data as objects which already offer many features and usually it is not very helpful to take off the data from those objects and transform it in a common Perl data structure just for using the same interface we used before.
+
+The objects which are generated by ORMs can be also used in other higher level modules like form processors for example, and those modules can increase the productivity even more.
+
+Another reason to change the interface and use those objects returned by the ORM is that many times the biggest part of the application is the one that does the business logic. So when we want to change the database from using a plain text to use a non-sql database, or from such a database to a relational database, or if we want to change the technology and start using an ORM, usually we actually want to change the entire application, not to just change the technology that powers the business logic and let the rest unchanged. So if we really want to change the entire application it doesn't matter too much if we will also change the interface used to access the database, especially that the new change would be probably an improvement.
+
+In our small application that uses just very simple DBIC methods to access the database there is almost no reason to improve the way we use DBIC. Even though we use DBIC methods directly in the controller, and we would need to copy that code in other controllers if we would need it there, DBIC offers a very short and elegant solution.
+
+For example, if we need to get an object with the record that has the primary key $id we need to write the following DBIC code:
+
+    my $record = $c->model( 'DB::Person' )->find( $id );
+
+If we would create a standalone library and use a model that inherits from L<Catalyst::Model::Adaptor|https://metacpan.org/module/Catalyst::Model::Adaptor> to use that library, we would need to use instead a code like:
+
+    my $record = $c->model( 'DB' )->find_person( $id );
+
+But in addition we would need to manually create that standalone library and add the custom method find_person() using a code like:
+
+    sub find_person {
+        my ( $self, $id ) = @_;
+        return $self->schema->resultset( 'Person' )->find( $id );
+    }
+
+So this would mean much more code which wouldn't be more clear than the interface offered directly by DBIC.
+And the result will still be a DBIC row object and not a common Perl data type that would be compatible with a previous version of the application that doesn't use DBIC. In order to return a common Perl data structure and not a DBIC object, some more code is needed and some features offered by DBIC will be lost.
+
+So for the moment usually the best idea is to use directly the objects returned by DBIC.
+
+OK, but what about the larger applications that use a much complicated business logic and much more complex DBIC commands to add/edit/delete/retrieve the data from the database?
+
+Well, in case of those applications it is not a good idea to keep the entire business logic in the controllers, because if we would need to use the same code in more controllers, we would need to copy too much code and we will also need to maintain it in more places. We can add the code we want in the modules generated automaticly by that long Catalyst helper command.
+
+Let's add some code as an example.
+
+In our database table the first name and the last name of the persons are separated in 2 distinct columns. We've seen that in our template index.tt there is a line that display these 2 columns joined:
+
+            <td><a href="/manage/edit/[% m.id %]">[% m.last_name %], [% m.first_name %]</a></td>
+
+These 2 Template-Toolkit directives join the last name and the first name:
+
+    [% m.last_name %], [% m.first_name %]
+
+But let's say that we want to operate on the record, also called as DBIC row and return from the database a column named "last_first" in which the last name and the first name are already joined and we will display that new column in the template using just:
+
+    [% m.last_first %]
+
+Of course, this is just a simple example, but many other things which are even more helpful can be done.
+
+We should get an additional column named "last_first" which contains the last name and first name joined with a comma and a space.
+
+If we use an SQLite database, the SQL command for getting that additional command is:
+
+    select id, first_name, last_name, email,
+      last_name||', '||first_name as last_first from persons;
+
+If we use a MySQL database, the SQL command for getting that additional command is:
+
+    select id, first_name, last_name, email,
+      concat(last_name, ', ', first_name) as last_first from persons;
+
+In other database types it could have other forms. If we use DBIC, our source code will be compatible with all supported databases.
+
+We will add the code in the file lib/StandaloneApp3/Result/Person.pm below the text generated automaticly "# You can replace this text with custom code or comments, and it will be preserved on regeneration":
+
+    sub last_first {
+        my ( $self ) = @_;
+        return $self->last_name . ', ' . $self->first_name;
+    }
+
+That's all. This subroutine with the name last_first from the Result class Person.pm will become a column with the name last_first which will get and concatenate the 2 columns and return the result. This column will be displayed by the index.tt template using:
+
+    [% m.last_first %]
+
+OK, we've seen how we can operate on a record, also know as a DBIC row object, but how can we operate on a whole table, or better said to ResultSet (collection of row objects)?
+
+We need to operate on a ResultSet when we want to add a new record in the table, or when we need to select something from a table or want to search in related tables.
+
+Instead of adding this code in the controllers, and duplicate it in all controllers where we need it, we can add it in the ResultSet modules and then access it with a single method call.
+
+But where are those ResultSet modules?
+
+When we used that long Catalyst helper command that created the model DB.pm and the DBIC schema StandaloneApp3.pm, it also created the Result class Person.pm in the directory lib/StandaloneApp3/Result. The helper script doesn't create ResultSet classes, but only Result classes so we need to create them manually.
+
+We need to create the directory lib/StandaloneApp3/ResultSet and in that directory we will create Perl modules that have the same name as the modules from the Result directory. Of course, we don't need to create ResultSet modules for all the Result modules we have.
+
+In case of our application we have a single Result class named Result/Person.pm so we will create a ResultSet class ResultSet/Person.pm that will have the following content:
+
+    package StandaloneApp3::ResultSet::Person;
+
+    use strict;
+    use warnings;
+    use base 'DBIx::Class::ResultSet';
+
+    sub add {
+        my ( $self, $params ) = @_;
+        $self->create( $params );
+    }
+
+    sub edit {
+        my ( $self, $id, $params ) = @_;
+        $self->find( $id )->update( $params );
+    }
+
+    sub delete {
+        my ( $self, $id ) = @_;
+        $self->find( $id )->delete;
+    }
+
+    sub retrieve {
+        my ( $self, $id ) = @_;
+        my $members = $self->search( undef, { order_by => 'id' } );
+        $members = $members->search( { id => $id } ) if $id;
+        my @members = $members->all;
+        return \@members;
+    }
+
+    1;
+
+As we can see the code is very simple, but of course, the L<DBIC|https://metacpan.org/module/DBIx::Class> documentation must be read in order to understand it.
+
+Note that the ResultSet classes inherit from L<DBIx::Class::ResultSet|https://metacpan.org/module/DBIx::Class::ResultSet>.
+
+Now if we want to access the database from the controller or from command line scripts, or in test scripts, we don't need to use the methods provided by DBIC directly, because it would be more easy to use these custom methods we created. In real life applications the business code uses more lines of code. It will be replaced by just a single line of code that calls a single method or chain of methods from our ResultSet module. In our application which is very simple we will not see this improvement because the business code is already small.
+
+OK, let's modify the controller to use these custom methods instead of the methods offered by DBIC. In order to be more clear which is the diffrence, we will show below the lines that where changed and we will show the line before and after this change.
+
+In the add() subroutine:
+
+previous code:
+
+            $c->model( 'DB::Person' )->create( $c->request->params );
+
+current code:
+
+            $c->model( 'DB::Person' )->add( $c->request->params );
+
+In this case we changed just the name of the method used from create() to add() so it is not an advantage. In other applications, instead this previous code that's so small, we might have a bigger code, but the current code would still be just a single line or fewer lines.
+
+In the edit() subroutine:
+
+previous code:
+
+            $c->model( 'DB::Person' )->find( $wanted_id )->update( $c->request->params );
+
+current code:
+
+            $c->model( 'DB::Person' )->edit( $wanted_id, $c->request->params );
+
+This time instead of using a chain of 2 method calls to find() and update() which could have been put in 2 lines of code, we use just a single subroutine named edit().
+
+In the edit() subroutine we also have a line that calls a DBIC method to get the record with the specified ID:
+
+            my $members = $c->model( 'DB::Person' )->find( $wanted_id );
+
+This line of code just calls a single subroutine with a single parameter so we couldn't improve it by creating our own custom method in the Person.pm ResultSet class so we leave it as it is.
+
+In the delete() subroutine:
+
+previous code:
+
+        $c->model( 'DB::Person' )->find( $wanted_id )->delete;
+
+current code:
+
+        $c->model( 'DB::Person' )->del( $wanted_id );
+
+The previous code was calling 2 DBIC methods find() and delete() and the current code calls just our custom del() method we created. We name our custom method del() and not delete() to not conflict with the delete() method which is provided by DBIC.
+
+In the index() subroutine:
+
+previous code:
+
+        my @members = $c->model( 'DB::Person' )->search( undef, { order_by => 'id' } );
+        $c->stash( members => \@members );
+
+current code:
+
+        my $members = $c->model( 'DB::Person' )->retrieve;
+        $c->stash( members => $members );
+
+In the previous code we called the DBIC method search() with a long parameter, but the current code just calls the custom method retrieve() without any parameters and that method also returns an arrayref we can add directly to the stash.
+
+But anyway, I say again that this code is too small for beeing obvious that it is better to add custom methods to Resultset classes.
+
+So, in addition to what we done on the previous step, on this last step we created the lib/StandaloneApp3/ResultSet/Person.pm module and we modified the controller to use the methods we defined in this module. We also shown how to add a custom generated column in lib/StandaloneApp3/Result/Person.pm and also modified the template index.tt to display this new column.
+
+As we did on each step, we will test the application using the same actions:
+
+Run again the development server:
+
+    perl script/webapp_server.pl
+
+And then access it at the following URL:
+
+    http://localhost:3000/manage
+
+Click on the "Add new member". It will open the page with the add form.
+Add some data in the form and submit it. It will add that record in the database and it will redirect to the page with the persons.
+Click on the name of the person. It will open the edit form. Change some values and submit that form. It will make the change in the database and it will redirect to the page with the list of persons.
+Click on "Delete member" link for the person you want. It will delete that record from the database and it will redirect to the page with the list of persons.
+
+
+Now that we created those custom methods in the ResultSet class and we are using them in the Manage controller, we can do the same changes in the command line scripts, and make them to also use those methods.
+
+Here are the lines that were changed in the CLI scripts:
+
+    # add.pl:
+
+previous code:
+
+    $app->resultset( 'Person' )->create( { first_name => $first_name,
+      last_name => $last_name, email => $email } );
+
+current code:
+
+    $app->resultset( 'Person' )->add( { first_name => $first_name,
+      last_name => $last_name, email => $email } );
+
+    # edit.pl:
+
+previous code:
+
+    $app->resultset( 'Person' )->find( $id )->update( { first_name => $first_name,
+      last_name => $last_name, email => $email } );
+
+Current code:
+
+    $app->resultset( 'Person' )->edit( $id, { first_name => $first_name,
+      last_name => $last_name, email => $email } );
+
+    # delete.pl:
+
+previous code:
+
+    $app->resultset( 'Person' )->find( $id )->delete;
+
+current code:
+
+    $app->resultset( 'Person' )->del( $id );
+
+    # retrieve.pl:
+
+previous code:
+
+    my $members = $app->resultset( 'Person' )->search( undef, { order_by => 'id' } );
+    $members = $members->search( { id => $id } ) if $id;
+    my @members = $members->all;
+
+    for my $m ( @members ) {
+        print $m->id, ', ', $m->first_name, ', ', $m->last_name, ', ', $m->email, "\n";
+    }
+
+current code:
+
+    my $members = $app->resultset( 'Person' )->retrieve( $id );
+
+    for my $m ( @$members ) {
+        print $m->id, ', ', $m->first_name, ', ', $m->last_name, ', ', $m->email, "\n";
+    }
+
+We can also use these scripts as a test of the standalone library (the DBIC schema) using commands like:
+
+    perl add.pl --first-name John --last-name Smith --email john at smith.com
+
+    perl edit.pl --id 1 --first-name George --last-name Smith --email j at smith.com
+
+    perl retrieve.pl --id 1
+
+    perl delete.pl --id 1
+
+We have also created a test file t/06standalone_app3.t which tests if the DBIC schema works fine when we use the ResultSet methods we defined.
+
+We can run all the tests with:
+
+    prove -l t
+
+And it should finish with the results:
+
+    All tests successful.
+    Files=7, Tests=131,  9 wallclock secs ( 0.13 usr +  0.02 sys =  0.14 CPU)
+    Result: PASS 
+
+
+This was a short step by step tutorial for Catalyst which started with an application that use a plain text database and which has just a controller, but no model and no view.
+
+Then we've seen that the application is easier to develop if we move the business code to the model because we can access the business code from more controllers.
+
+In the third step we've seen that we can move the business code further, outside of the Catalyst app, in a standalone library that can be accessed not only from the Catalyst app by using a light model, but from other applications also.
+
+In the next step we've presented the advantages of doing the formatting using a view, and not the controller, and we created a view which uses Template-Toolkit.
+
+On the fifth step we decided to abandon the text file database and to use an SQLite relational database instead which is more powerful, and we change the application to access this database with plain SQL commands from the controller.
+
+In the next step we moved the business code from the controller to the model for the same reasons... for beeing able to use that code in other controllers easier.
+
+In the seventh step we moved again the business code from the fat model to a new standalone library in order to be able to use again that code in other applications than the web app.
+
+In the eight step we presented the disadvantages of using plain SQL queries and the advantage of using an ORM, and we modified the controller to access the same SQLite database using DBIC.
+
+In the current step we have shown how we can create custom methods in the DBIC Result and ResultSet classes in order to be able to use them in more controllers, test scripts and other applications.
+
+The application can be improved further by adding more configuration and specify for example that the templates, config files, database use UTF-8, make it use a form processor, use internationalization, use REST and so on. I didn't add these things to the current application in order to make it appear as easy to understand as possible.
+
+I also made all the modules which were manually created to not use Moose in order to be easier to understand by beginners.
+
+=head1 Author:
+
+Octavian Rasnita <orasnita at gmail.com>

Added: trunk/examples/CatalystAdvent/root/2012/6.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2012/6.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2012/6.pod	2012-12-05 17:26:06 UTC (rev 14415)
@@ -0,0 +1,181 @@
+=head1 Catalyst Advent 2012 Introduction
+
+=head1 OVERVIEW
+
+A bit about the contents of the Advent Calendar and some general thoughts on 
+the l<Catalyst> project.
+
+=head1 Didn't Advent Start already?
+
+Yes, we are getting a bit of a late start (although since Advent officially
+started on December 2nd, perhaps a bit less late).
+
+=head1 Didn't we way we'd retire Catalyst Advent?
+
+Well, in my mind that was more of a kick in the pants to get more people
+involved, since last year the burden of work was carried by far too few people.
+
+=head1 So, what's the 'Year End Review' of Catalyst
+
+So here's my thoughts on the state of Catalyst.  Catalyst continues to be one
+of the more popular choices for Modern Perl programmers who are building web
+sites / applications.  The code base is pretty mature and we have quite a bit
+(probably not enough, but a lot for a Perl project) documentation and examples
+floating around.
+
+The community has a number of pretty enthusiastic members, and the number of
+people hanging out on the various Catalyst IRC channels continues to be much
+higher than any other modern web framework.  So that is the good stuff.
+
+On the other hand Catalyst is facing significant challenges if we want it to
+continue to be fresh and relevent as time goes on.  I can see both internal
+and external challenges for the project.  Internally, we have an aging codebase
+with a lot of backward compatibility hacks, and probably some things in core
+that don't belong in core (and some things outside core that should be core).
+Although the codebase was converted to L<Moose> a number of years ago, many
+of the more important parts of the Catalyst ecosystem have not made the switch
+and in addition although Catalyst core uses Moose a lot of the code could be 
+significantly cleaned up to make better use of existing Moose features.  Same
+thing for the L<Plack> integration work that was completed more than a year ago.
+There's a lot of overlap between some of the popular Catalyst plugins and
+features that come with L<Plack> and there is no clarity as to how to address
+this.
+
+In addition our attempts to replace Catalyst's service lookup features with
+the better supported and more featureful L<Bread::Board> has stalled due to
+the tremendous difficult in managing this transition without breaking so much
+of Catalyst as to render the project without merit.
+
+Again, although many people hang out on the various Catalyst IRC channels,
+it seems there's a very small number of people that can actually contribute
+to the project. There's a number of reasons for this, part of it is how the
+project is currently managed, and part of it is probably the complexity of the
+codebase, and part of it is likely the complexity of the various design patterns
+that Catalyst uses (Model - View - Controller and Chain of Command plus a bit
+of Mediator and some others).
+
+Externally there's a lot of challenge to the idea that a theoretically heavy (
+what I mean by this is that the design patterns that Catalyst is build on all
+have many papers and academic discussions around them) and full stack solution
+is relevent today.  There's a lot of cutting edge web development that is taking
+place exclusively on clients (such as in Javascript on the web browser, or in
+C# and Java for various smart phone clients) and one could make the argument
+that back end applications will focus more on providing well structured web
+services, probably in JSON or similar popular standards, and following a REST
+paradigm.  Many of the popular mini/micro web frameworks cater to this idea,
+and they tend to focus on minimal code / maximum feature delivery.
+
+In such a world, can Catalyst continue to be a good choice for the Modern
+Perl programmer?
+
+I personally believe so, although it is clear the use cases for server side
+development is going to change and expand.  The fundemental design patterns
+that Catalyst strives to deliver are in my mind still very relevent, and I
+think they lead you to delivering better code that is easier to test, deploy
+and change as requirements change.  It is not clear to me that some of the
+popular micro-frameworks achieve this, although I do think that Catalyst could
+take a page from the micro-framework book and lose some of the bulk and boiler
+plate needed to get started with a meaningful application.
+
+In additional to Catalyst growing and changing with these evolved needs, I
+think our community needs to mature as well.  I think we need to have more
+hands on engagement with the various intellectual ideas upon which Catalyst
+is built, for example.  As people use Catalyst and grapple with issues
+around MVC and other topics, it would be great for us to talk about that stuff
+and not just code cookbooks.  Perl programmers have always been practical, but
+we should always remember that Larry Wall make Perl practical but also in
+line with a consistent philisophy about language and how people use languages
+to communicate and achieve goals.
+
+=head1 Wait, you aren't going to actually show us some code?
+
+Ok, here's my 'stupid pet trick' so that you didn't read all this in vain.
+Quite often when you are first working on a Catalyst application using
+DBIx::Class you are messing around a lot with the Schema and it is a big
+pain to keep rebuilding a fresh database.  Sometimes I use L<Test::DBIx::Class>
+to automatically create and deploy a schema on each application restart.
+
+    package MyApp::Web::Model::DBIC;
+
+    use Moose;
+    use Test::DBIx::Class
+      -schema_class => 'MyApp::Schema';
+
+    extends 'Catalyst::Model::DBIC::Schema';
+ 
+    __PACKAGE__->config(
+      schema_class => 'MyApp::Schema',
+      connect_info => [
+        sub {Schema()->storage->dbh},
+        { on_connect_call => sub { Schema()->install_fixtures } },
+      ]);
+
+Please note with the above you will lose all your data for each restart of the
+application, unless you save or dump it in some manner.  Also, note that
+L<Test::DBIx::Class> imports C<Schema> as well as a few other things into your
+namespace, so you might want to review the documentation on that to know what
+you are getting into.
+
+You'll need to create the method C<install_fixtures> in C<MyApp::Schema> if you
+want to install some seed or development data.
+
+Another useful variation of this trick is to move this connect information into
+a stand alone configuration file (such as C<myapp_web_test.pl>) for when you are
+running automated tests against your application.  For example:
+
+    ## share/etc/myapp_web_test.pl
+
+    use Test::DBIx::Class
+      -schema_class=>'MusicBase::Schema',
+      -traits=>['Testmysqld'];
+
+  {
+    'Model::DBIC' => {
+      schema_class => 'MyApp::Schema',
+      connect_info => [
+        sub {Schema()->storage->dbh},
+        { on_connect_call => sub { Schema()->install_fixtures } },
+      ],
+    },
+  };
+
+Then if you start your Catalyst application pointing to this testing configuration
+file, you'll get a fresh schema and data for each set of tests that get run.
+
+In this example I am also using the C<Testmysqld> trait so that we run our
+automated tests against MySQL instead of the default SQLite.  This is a good
+idea to be running your tests against the same database type as your intended
+production database, and the Testmysqld trait for L<Test::DBIx::Clas> makes it
+pretty easy to use MySQL (it only needs to be installed (not running) and
+find-able in $PATH).  You can even use the mysql replication testing trait to
+make sure your code runs well in a replicated cluster.
+
+=head1 So What is Next?
+
+This year we are going to kick off Advent with a nine (NINE!!!) article series
+by Octavian who is going to walk us through one approach to building up an 
+application over time.  Although not all the code is the most modern in approach
+(not everyone can use the most recent version of Catalyst on the job, so I think
+it is great to have such an extended example that would work well across many
+Catalyst versions) there is a clear thinking pattern that runs through it all
+and there is a lot of very interesting discussion of some of the design patterns
+I mentioned above.  So be sure to read all the words and not just skip right to
+the code!
+
+Following we will have our standard grab bags of cookbooks articles and various
+musings on Catalyst.
+
+=head1 Summary
+
+Catalyst is a project in transition, and I think the 2012 Advent calendar will
+reflect that.  We will have some old and new voices mixing both legacy and
+modern approaches to using the framework.  My hope is that it will provoke us
+to think more deeply about the future evolution of Catalyst such that it will
+continue to be the first, best choice for Modern Perl programmers building the
+next generation of web applications.
+
+=head1 Author
+
+John Napiorkowski <jjnapiork at cpan.com> jnap on IRC
+
+=cut

Added: trunk/examples/CatalystAdvent/root/2012/7.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2012/7.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2012/7.pod	2012-12-05 17:26:06 UTC (rev 14415)
@@ -0,0 +1,439 @@
+=head1 Catalyst in 9 steps - step 1: Just a controller using a plain text file database
+
+Note: You can download the archive with the application created in this first step from:
+
+=begin xhtml
+
+<a href="/calendar/static/2012/WebApp-0.01.tar.gz">WebApp-0.01.tar.gz</a>
+
+=end xhtml
+
+Probably everybody remembers the first steps made in the childhood and the falls on belly that followed them, right? Oh, nobody remembers? Well, me neither, but there were surely many falls and they were very normal. The same thing happens when starting to learn a complex enough piece of software like Catalyst.
+
+We learn to walk by continuously improving our skills and knowledge because this is the easiest way. It would be much harder or impossible if an adult would teach us a lot of theory about the process of walking, without letting us to walk and fall.
+Why? Because at that age is easier to try to walk and fall than to understand what the adults tell us.
+
+Most L<Catalyst|https://metacpan.org/module/Catalyst> tutorials try to teach the beginners the "right way" directly, try to teach the best practices which usually involve other high-level Perl modules like templating systems, ORMs, form processors etc.
+The result is that there are many Perl programmers that consider Catalyst a web framework which is hard to use and which involves a lot of time for learning it even for doing a simple test web app.
+
+In this article I will try a different approach. I will try to create a simple Catalyst app which doesn't use the best practices and recommendations at the beginning, and then when the app is working and everything is clear we will move on by improving it step by step.
+This way should make clear that Catalyst framework is very simple to use and it should also make clear that a Catalyst app doesn't require using DBIx::Class, Template-Toolkit, HTML::FormFu or other similar modules.
+
+Note: This article is not a substitute for Catalyst docs. It just tries to cover the child steps which might be missing from Catalyst docs, but it is necessary to read Catalyst POD documentation in order to understand how URL dispatching works, or what is the stash, or how to redirect to a certain URL and so on.
+
+OK, let's begin. We will make a very simple application which doesn't really matter what it does, because we need it only to show how Catalyst works, not how to create such an application professionally.
+This application will be able to display a table with persons and it will also allow adding, editting and deleting the records in the database that holds these records.
+The database will hold 4 fields for each record saved: id, first name, last name and email.
+Not only that we won't use an ORM like DBIx::Class to access the database, but we won't even use a relational database (at the beginning). We will use a single text file as a database. Nothing more. Each row in the text file will be a record, and the fields of the same record will be separated by vertical bars (|). It won't be a strong database, it won't allow adding text that contain a vertical bar or end of line chars, and it will surely have other problems, but as I said, it is not important what it does, but how we can make Catalyst to use it and later how to improve it.
+
+We will begin by creating the base Catalyst project named WebApp, using the following command:
+
+    catalyst.pl WebApp
+
+(Or catalyst WebApp if you do it under Windows.)
+
+Then we change the current directory to the newly created "WebApp" directory using:
+
+    cd WebApp
+
+Then we run the application using the development web server provided by Catalyst just as a test, to see if it works:
+
+    perl script/webapp_server.pl
+
+And then we access it in the browser at the following URL:
+
+    http://localhost:3000/
+
+Yep, the welcome page of this app shows as it should.
+
+After this step, the application contains more files and folders, but for the moment we will be interested just in a few of them.
+In the main directory of the application there are the directories lib, root, script, t, and a few files.
+In this directory we will create another directory named "data" in which the application will store its database. This directory can be put anywhere, not necessarily under the WebApp directory.
+
+The "lib" directory contains all the modules used by this application.
+In this directory, the module WebApp.pm is the main module of the application.
+
+Alongside the module WebApp.pm there is a directory named WebApp which contains the directories Controller, Model and View.
+In the Controller directory there is a single controller module created by default, named Root.pm.
+
+We can add our code in that controller, or we can create another one. It would be a little easier to use the existing Root controller, but because in most applications you will need to create separate controllers for different parts of the application, we will also create another controller for doing what we want, especially that it is very simple to do it.
+
+In the Controller directory We will create a new module named Manage.pm that will have the content below.
+This controller will contain 4 subroutines:
+
+    - add() will be executed when we access the URL /manage/add
+    - edit() will be executed when we access the URL /manage/edit
+    - delete() will be executed when we access the URL /manage/delete
+    - index() will be executed when we access the URL /manage
+
+
+    package WebApp::Controller::Manage;
+    use strict;
+    use warnings;
+    use base 'Catalyst::Controller';
+    use File::Slurp ':all';
+
+    sub add : Local {
+        my ( $self, $c ) = @_;
+
+        if ( $c->request->params->{submit} ) {
+            my $first_name = $c->request->params->{first_name};
+            my $last_name = $c->request->params->{last_name};
+            my $email = $c->request->params->{email};
+
+            my ( @lines, $id );
+
+            my $database_file = 'd:/web/step1/WebApp/data/database_file.txt';
+            @lines = read_file( $database_file ) if -f $database_file;
+            ( $id ) = $lines[-1] =~ /^(\d+)\|/ if $lines[-1];
+            $id = $id ? $id + 1 : 1;
+
+            write_file $database_file, { append => 1 },
+              "$id|$first_name|$last_name|$email\n";
+
+            $c->res->redirect( "/manage" );
+        }
+        else {
+            my $body = qq~<html><head><title>Add person</title></head><body>
+                <form action="/manage/add" method="post">
+                First name: <input type="text" name="first_name" /><br />
+                Last name: <input type="text" name="last_name" /><br />
+                Email: <input type="text" name="email" /><br />
+                <input type="submit" name="submit" value="Save" />
+                </form></body></html>~;
+
+            $c->response->body( $body );
+        }
+
+    }
+
+    sub edit : Local {
+        my ( $self, $c, $wanted_id ) = @_;
+
+        my $database_file = 'd:/web/step1/WebApp/data/database_file.txt';
+
+        if ( $c->request->params->{submit} ) {
+            my $first_name = $c->request->params->{first_name};
+            my $last_name = $c->request->params->{last_name};
+            my $email = $c->request->params->{email};
+
+            edit_file_lines { s/^$wanted_id\|.*$/$wanted_id|$first_name|$last_name|$email/ } $database_file;
+            $c->res->redirect( "/manage" );
+        }
+        else {
+            my @raw_lines = read_file $database_file;
+            chomp @raw_lines;
+
+            my ( $id, $first_name, $last_name, $email );
+
+            for my $raw_line ( @raw_lines ) {
+                my ( $test_id ) = split( /\|/, $raw_line );
+                next if $wanted_id && $wanted_id != $test_id;
+                ( $id, $first_name, $last_name, $email ) = split( /\|/, $raw_line );
+            }
+
+            my $body = qq~<html><head><title>Edit person</title></head><body>
+                <form action="/manage/edit/$id" method="post">
+                First name: <input type="text" name="first_name" value="$first_name" /><br />
+                Last name: <input type="text" name="last_name" value="$last_name" /><br />
+                Email: <input type="text" name="email" value="$email" /><br />
+                <input type="submit" name="submit" value="Save" />
+                </form></body></html>~;
+
+            $c->response->body( $body );
+        }
+
+    }
+
+    sub delete : Local {
+        my ( $self, $c, $wanted_id ) = @_;
+
+        my $database_file = 'd:/web/step1/WebApp/data/database_file.txt';
+        edit_file_lines { $_ = '' if /^$wanted_id\|/ } $database_file;
+        $c->res->redirect( "/manage" );
+    }
+
+    sub index :Path :Args(0) {
+        my ( $self, $c ) = @_;
+
+        my $body = qq~<html><head><title>Persons list</title></head><body>
+            <a href="/manage/add">Add new person</a><br /><table>~;
+
+        my $database_file = 'd:/web/step1/WebApp/data/database_file.txt';
+        my @raw_lines;
+        @raw_lines = read_file $database_file if -f $database_file;
+        chomp @raw_lines;
+
+        for my $raw_line ( @raw_lines ) {
+                my ( $id, $first_name, $last_name, $email ) = split( /\|/, $raw_line );
+
+            $body .= qq~<tr>
+                <th>$id</th>
+                <td><a href="/manage/edit/$id">$last_name, $first_name</a></td>
+                <td>$email</td>
+                <td><a href="/manage/delete/$id">delete person</a></td>
+            </tr>\n~;
+        }
+
+        $body .= "</table></body></html>";
+
+        $c->response->body( $body );
+    }
+
+    1;
+
+
+Here are some comments for the content of the Manage.pm module:
+
+    use base 'Catalyst::Controller';
+
+All the controller modules under the Controller directory inherit the class L<Catalyst::Controller|https://metacpan.org/module/Catalyst::Controller> so we are using it as a base.
+
+    use File::Slurp ':all';
+
+This line will load the module L<File::Slurp|https://metacpan.org/module/File::Slurp> and it will export all its subroutines, not only those subroutines exported by default, because we will need the subroutine edit_file_lines() which is not exported by default.
+
+    sub add : Local {
+        my ( $self, $c ) = @_;
+
+This code starts the add() subroutine, the one that will be executed when the URL /manage/add will be accessed.
+
+This subroutine will do 2 things:
+
+    1. It will display the form which will be used for adding a new person in the database.
+    2. It will add the data posted by the user if the form was submitted and then it will redirect to the page which displays the list of persons.
+
+        if ( $c->request->params->{submit} ) {
+
+This line will check if the page was requested with a parameter named "submit" (which is the name of the submit button in the add form), and if it is a true value, it will add the submitted data in the database.
+
+You can read more about $c->request method in the POD documentation of L<Catalyst::Request|https://metacpan.org/module/Catalyst::Request>.
+
+            my $first_name = $c->request->params->{first_name};
+            my $last_name = $c->request->params->{last_name};
+            my $email = $c->request->params->{email};
+
+These lines of code get the first name, last name and email variables from the posted data.
+
+            my $database_file = 'd:/web/step1/WebApp/data/database_file.txt';
+
+This line of code gets the full path to the database file. You need to change this line to match the full path to the database file on your computer. You can store it wherever you want.
+Because in all the subroutines from this controller module we need to access the same database file, we will see that this line appears in all the subroutines and we won't talk about it anymore.
+
+            @lines = read_file( $database_file ) if -f $database_file;
+
+This code uses the subroutine read_file() which is exported by the module File::Slurp. It reads the database file if it exists and it stores all the lines of that file in the array @lines.
+We need to read the database file because we need to get the last ID in it. The current ID will be the last stored ID + 1.
+
+            ( $id ) = $lines[-1] =~ /^(\d+)\|/ if $lines[-1];
+
+This line gets the ID from the last line of the file.
+From the last line of the file ($lines[-1]), it gets the first number (\d+ which is found between the beginning of the line (^) and the first vertical bar (|).
+
+            $id = $id ? $id + 1 : 1;
+
+If an ID was found so there is something in the database, the new ID is incremented with 1, otherwise the new id is the number 1.
+
+            write_file $database_file, { append => 1 },
+              "$id|$first_name|$last_name|$email\n";
+
+This code uses the subroutine write_file() which is exported by the module File::Slurp. It adds the new line at the end of the file.
+
+            $c->res->redirect( "/manage" );
+
+After the new person was added to the database file, this line redirects the browser to the url /manage which displays the page with the list of persons in the database.
+
+        else {
+            my $body = qq~<html><head><title>Add member</title></head><body>
+                <form action="/manage/add" method="post">
+                First name: <input type="text" name="first_name" /><br />
+                Last name: <input type="text" name="last_name" /><br />
+                Email: <input type="text" name="email" /><br />
+                <input type="submit" name="submit" value="Save" />
+                </form></body></html>~;
+
+            $c->response->body( $body );
+        }
+
+If the variable $c->request->params->{submit} is false, it means that the form was not submitted, so this alternative code is executed.
+It just generates the body of the page which contains the form which will be used for adding a new person in the database.
+
+The last line from this block tells Catalyst that the body of the current page that should be returned to the browser is the $body variable.
+
+You can read more about the $c->response method in the POD documentation of L<Catalyst::Response|https://metacpan.org/module/Catalyst::Response>.
+
+So, in a fewer words, this subroutines checks if the form was submitted, and if this is true it adds the new person in the database, otherwise it displays the form.
+
+    sub edit : Local {
+        my ( $self, $c, $wanted_id ) = @_;
+
+This code starts the edit() subroutine which will be used for editting the record with $wanted_id ID in the database.
+This subroutine will be accessed at the URLs like /manage/edit/1 where 1 is the ID of the record we want to modify.
+
+        if ( $c->request->params->{submit} ) {
+            my $first_name = $c->request->params->{first_name};
+            my $last_name = $c->request->params->{last_name};
+            my $email = $c->request->params->{email};
+
+            edit_file_lines { s/^$wanted_id\|.*$/$wanted_id|$first_name|$last_name|$email/ }    $database_file;
+            $c->res->redirect( "/manage" );
+        }
+
+This code is similar to the one in the add() subroutine, but it is more simple, because we don't need to get the latest ID stored in the database.
+Instead of adding a new line to the database file, this code modifies the line which starts with the wanted ID, using the subroutine edit_file_lines() which is exported by File::Slurp.
+
+        else {
+            my @raw_lines = read_file $database_file;
+            chomp @raw_lines;
+
+            my ( $id, $first_name, $last_name, $email );
+
+            for my $raw_line ( @raw_lines ) {
+                my ( $test_id ) = split( /\|/, $raw_line );
+                next if $wanted_id && $wanted_id != $test_id;
+                ( $id, $first_name, $last_name, $email ) = split( /\|/, $raw_line );
+            }
+
+            my $body = qq~<html><head><title>Edit member</title></head><body>
+                <form action="/manage/edit/$id" method="post">
+                First name: <input type="text" name="first_name" value="$first_name" /><br />
+                Last name: <input type="text" name="last_name" value="$last_name" /><br />
+                Email: <input type="text" name="email" value="$email" /><br />
+                <input type="submit" name="submit" value="Save" />
+                </form></body></html>~;
+
+            $c->response->body( $body );
+        }
+
+The code above is similar with the one in the add() subroutine, but this time it is not just printing a static empty form. It reads the lines from the database file, skips all the lines that don't start with the wanted ID, and from the line which starts with the wanted ID it gets the values which will be displayed in the form.
+
+So, with fewer words, this subroutine checks if the form was submitted, and if this is true, it replaces the line in the database that starts with the wanted ID with newer values posted. If the form was not submitted, it gets the existing values from the database for the record with the wanted ID and display a form filled with those values.
+
+    sub delete : Local {
+        my ( $self, $c, $wanted_id ) = @_;
+
+This code starts the subroutine delete() which will be used for deleting the record in the database which has the wanted ID.
+It will be executed when the URLs like /manage/delete/1 will be accessed (where 1 is the ID of the record we want to delete).
+
+        my $database_file = $c->config->{database_file};
+        edit_file_lines { $_ = '' if /^$wanted_id\|/ } $database_file;
+        $c->res->redirect( "/manage" );
+    }
+
+This code deletes from the database file the line which starts with the wanted ID, using the subroutine edit_file_lines() which is exported by File::Slurp.
+
+    sub index :Path :Args(0) {
+        my ( $self, $c ) = @_;
+
+This code starts the index() subroutine, the subroutine which will display the list of persons in our database.
+The attributes :Path :Args(0) makes Catalyst to execute this subroutine when the URL /manage is accessed.
+
+        my $body = qq~<html><head><title>Members list</title></head><body>
+            <a href="/manage/add">Add new member</a><br /><table>~;
+
+This code adds to the $body variable the start of the HTML document, a link and the start of a table.
+The link points to /manage/add, the page with the form which will be used for adding new persons in the database.
+The table will display the list of persons in our database.
+
+        my @raw_lines;
+        @raw_lines = read_file $database_file if -f $database_file;
+        chomp @raw_lines;
+
+This code reads the database file if it exists and it stores all the lines of that file in the array @raw_lines. The second line deletes the end of line char from all the elements of this array.
+
+        for my $raw_line ( @raw_lines ) {
+            my ( $id, $first_name, $last_name, $email ) = split( /\|/, $raw_line );
+
+            $body .= qq~<tr>
+                <th>$id</th>
+                <td><a href="/manage/edit/$id">$last_name, $first_name</a></td>
+                <td>$email</td>
+                <td><a href="/manage/delete/$id">delete member</a></td>
+            </tr>\n~;
+        }
+
+This code splits every element of the array using the "|" char as separator and gets the fields id, first_name, last_name and email for every line of the database file. Then it creates an HTML table row with these values and it adds it to the $body variable which holds the content of the current page.
+
+        $body .= "</table></body></html>";
+        $c->response->body( $body );
+
+This code just adds to the content of the page the end of the table and the end of the HTML document. The last line tells Catalyst that the body of the current page that should be returned to the browser is the $body variable.
+
+In this Catalyst application you needed to create just a single module manually and the directory "data" which will hold the database file.
+
+This Catalyst app doesn't use any L<relational database|http://en.wikipedia.org/wiki/Relational_database>, any L<ORM|http://en.wikipedia.org/wiki/Object-relational_mapping>, L<form processor|http://en.wikipedia.org/wiki/Forms_processing>, L<templating system|http://en.wikipedia.org/wiki/Web_template_system> and the module above doesn't even use <Moose|https://metacpan.org/module/Moose>.
+
+As we are going to do on each step, we will test the application using the same actions:
+
+Run again the development server:
+
+    perl script/webapp_server.pl
+
+And then access it at the following URL:
+
+    http://localhost:3000/manage
+
+Click on the "Add new member". It will open the page with the add form.
+Add some data in the form and submit it. It will add that record in the database and it will redirect to the page with the persons.
+Click on the name of the person. It will open the edit form. Change some values and submit that form. It will make the change in the database and it will redirect to the page with the list of persons.
+Click on "Delete member" link for the person you want. It will delete that record from the database and it will redirect to the page with the list of persons.
+
+Before passing to the next step we could improve our application a little.
+You've seen that in all the subroutines of this controller we needed to hard code the path to the database file, which is not nice, especially that this path could be different on different computers. We can solve this problem by adding this path to the controller's configuration, in a single place, and then use that configuration value everywhere we need it in this controller.
+
+In order to let the controller Manage.pm unmodified for you to see it, we will copy it and create an identic controller named Manage2.pm and then we're make a few changes in it.
+
+As the first change, add the "2" in the name of the module:
+
+    package WebApp::Controller::Manage2;
+
+Change all the links to point to /manage2:
+
+    <a href="/manage/add"> will become <a href="/manage2/add">.
+    <a href="/manage/edit/$id"> will become <a href="/manage2/edit/$id">.
+    <a href="/manage/delete/$id"> will become <a href="/manage2/delete/$id">.
+
+Change all the form actions to point to /manage2:
+
+    <form action="/manage/add" will become <form action="/manage2/add".
+    <form action="/manage/edit/$id" will become <form action="/manage2/edit/$id".
+
+Change the redirection to /manage2 in all 3 places:
+
+    $c->res->redirect( "/manage2" );
+
+Now the controller Manage2.pm will work exactly like the controller Managed. To improve it, we will do the following 2 changes in it:
+
+Before the first subroutine, add the following code:
+
+    __PACKAGE__->config(
+        database_file => 'd:/web/step2/WebApp/data/database_file.txt',
+    );
+
+And then in all the subroutines change the line that initializes the variable $database_file with the following line:
+
+        my $database_file = $self->{database_file};
+
+The values of the configuration become keys in the $self hash and you can access directly using the line above.
+
+Now if you'll need the path to the database file in other places in this controller you will be able to access it without typing that full path again, and if you'll want to change that path you will need to change it just in a single place.
+
+As we are going to do on each step, we will test the application using the same actions:
+
+Run again the development server:
+
+    perl script/webapp_server.pl
+
+And then access it at the following URL:
+(note that the URL ends with manage2, not manage as in our first test.)
+
+    http://localhost:3000/manage2
+
+Click on the "Add new member". It will open the page with the add form.
+Add some data in the form and submit it. It will add that record in the database and it will redirect to the page with the persons.
+Click on the name of the person. It will open the edit form. Change some values and submit that form. It will make the change in the database and it will redirect to the page with the list of persons.
+Click on "Delete member" link for the person you want. It will delete that record from the database and it will redirect to the page with the list of persons.
+
+=head2 Author:
+
+Octavian Rasnita <orasnita at gmail.com>

Added: trunk/examples/CatalystAdvent/root/2012/8.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2012/8.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2012/8.pod	2012-12-05 17:26:06 UTC (rev 14415)
@@ -0,0 +1,266 @@
+=head1 Catalyst in 9 steps - step 2: A Controller and a fat model with a text DB
+
+Note: You can download the archive with the application created in this second step from:
+
+=begin xhtml
+
+<a href="/calendar/static/2012/WebApp-0.02.tar.gz">WebApp-0.02.tar.gz</a>
+
+=end xhtml
+
+L<Catalyst|https://metacpan.org/module/Catalyst> is an L<MVC|http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller> framework (model/view/controller). The model is the interface to the business logic, the view is the part of the app that provides the results in different formats (html, JSON, XML, PDF, CSS...). The controller is the part of the app which... as its name says, controls the web logic.
+
+The recommendation is to use fat models and light controllers. This means that the business logic, the biggest part of the application should be in the models and not in the controllers. The controllers should just get the data from web requests and send it to the appropriate models and get the results from models and send them back to browsers.
+
+Why does it matter this separation?
+
+For code reuse, for an easier testing.
+
+If a Catalyst app wrongly executes the business logic in a controller and then it needs to use a part of that business logic in other controllers, the same code may need to be copied in more places and it would need to be maintained in more places.
+
+If a model does the business logic as it should, that model can be called in more controllers and the business logic code need to be maintained in a single place.
+
+If the parts of the application are separated and independent, they can be easier plugged with each other in much more combinations and they can be easier reused even in other applications.
+
+Even though the recommendation is to use fat models and light controllers in Catalyst apps, many Catalyst tutorials don't do that, but show how to do some things by adding code to the controllers and not to the models, because it is much easier to understand that way.
+
+Ideally, the controllers should not include any business logic code because this is the job of models. Also ideally, they should not include any code used for formatting data, because this is the job of the views.
+
+In our application in its first step we included both business logic and HTML formatting in the controller because it was easier to understand how Catalyst works this way, but now we will need to separate the business logic and add it in a fat Catalyst model.
+
+Ok, but if it works... why should we change it?
+
+You have seen that in our application we have created 2 controllers that do the same thing. We needed to duplicate the entire business logic in both controllers, and if we want to change something in that business logic, to correct an error, or to add some features, we would need to do it in both controllers.
+In real world application probably we wouldn't have 2 controllers that do the same thing, but we will probably have more different controllers that would require the same parts of the business logic.
+
+So the next step would be to create a fat Catalyst model, which is nothing more than a module that inherits from Catalyst::Model which contains all the subroutines we need to add, edit, delete and retrieve the data from our database.
+
+So will create a file named DB.pm in the directory lib/WebApp/Model that will have the following content:
+
+
+    package WebApp::Model::DB;
+    use strict;
+    use warnings;
+    use base 'Catalyst::Model';
+    use File::Slurp ':all';
+
+    __PACKAGE__->config(
+        database_file => 'd:/web/step2/WebApp/data/database_file.txt',
+    );
+
+    sub add {
+        my ( $self, $params ) = @_;
+
+        my $database_file = $self->{database_file};
+
+        my ( @lines, $id );
+        @lines = read_file( $database_file ) if -f $database_file;
+        ( $id ) = $lines[-1] =~ /^(\d+)\|/ if $lines[-1];
+        $id = $id ? $id + 1 : 1;
+
+        write_file $database_file, { append => 1 },
+          "$id|$params->{first_name}|$params->{last_name}|$params->{email}\n";
+    }
+
+    sub edit {
+        my ( $self, $id, $params ) = @_;
+
+        my $database_file = $self->{database_file};
+
+        my $first_name = $params->{first_name};
+        my $last_name = $params->{last_name};
+        my $email = $params->{email};
+
+        edit_file_lines { s/^$id\|.*$/$id|$first_name|$last_name|$email/ } $database_file;
+    }
+
+    sub delete {
+        my ( $self, $wanted_id ) = @_;
+
+        my $database_file = $self->{database_file};
+
+        edit_file_lines { $_ = '' if /^$wanted_id\|/ } $database_file;
+    }
+
+    sub retrieve {
+        my ( $self, $wanted_id ) = @_;
+
+        my $database_file = $self->{database_file};
+
+        my @raw_lines;
+        @raw_lines = read_file $database_file if -f $database_file;
+        chomp @raw_lines;
+
+        my ( @lines, $id, $first_name, $last_name, $email );
+
+        for my $raw_line ( @raw_lines ) {
+            my ( $test_id ) = split( /\|/, $raw_line );
+            next if $wanted_id && $wanted_id != $test_id;
+            ( $id, $first_name, $last_name, $email ) = split( /\|/, $raw_line );
+
+            push( @lines, {
+                id => $id,
+                first_name => $first_name,
+                last_name => $last_name,
+                email => $email,
+            } );
+        }
+
+        return \@lines;
+    }
+
+    1;
+
+
+You've seen that the code of this model is similar to the one in the controller Manage2 that we made in the first step, but it is more simple because it doesn't contain any HTML generation code in it. It doesn't contain any web logic either, because we don't need to test here if a form was submitted or not, or to redirect to a certain URL.
+
+This model also simplifies something else...
+In the edit() subroutine of the controller Manage.pm we needed to retrieve the lines from the database in order to get the record we wanted to edit. In the retrieve() subroutine of the same controller we needed to retrieve all the records for printing them in the page with the list of persons. So we needed to use a similar code in 2 places.
+
+In the model DB.pm we shortened the code  a little because we created a retrieve() subroutine which accepts the optional parameter $wanted_id. If this subroutine is called without parameters, it returns an array with all the records from the database, as we need in the retrieve() subroutine of the controller. If it is called with one parameter - the ID of the record we need, it will return an array with a single element - the record with that ID, as we need in the edit() subroutine of the controller. So we will be able to call the same subroutine in both edit() and retrieve() subroutines of the Manage controller.
+
+After we created this model, we need to simplify the controller Manage.pm, which will have the content below.
+
+Note: We will change the controller Manage.pm and not the improved controller Manage2.pm, because that improvement was to just add the database file path to the controller's configuration, but if we moved the business logic to the DB.pm model, we won't need to access at all the database file name in the controller. And you've probably noticed that the model uses exactly the same code for adding the database file name to its configuration.
+We don't need the controller Manage2.pm anymore so we deleted it.
+
+
+    package WebApp::Controller::Manage;
+    use strict;
+    use warnings;
+    use base 'Catalyst::Controller';
+
+    sub add : Local {
+        my ( $self, $c ) = @_;
+
+        if ( $c->request->params->{submit} ) {
+            $c->model( 'DB' )->add( $c->request->params );
+            $c->res->redirect( "/manage" );
+        }
+        else {
+            my $body = qq~<html><head><title>Add person</title></head><body>
+                <form action="/manage/add" method="post">
+                First name: <input type="text" name="first_name" /><br />
+                Last name: <input type="text" name="last_name" /><br />
+                Email: <input type="text" name="email" /><br />
+                <input type="submit" name="submit" value="Save" />
+                </form></body></html>~;
+
+            $c->response->body( $body );
+        }
+
+    }
+
+    sub edit : Local {
+        my ( $self, $c, $wanted_id ) = @_;
+
+        if ( $c->request->params->{submit} ) {
+            $c->model( 'DB' )->edit( $wanted_id, $c->request->params );
+            $c->res->redirect( "/manage" );
+        }
+        else {
+            my $members = $c->model( 'DB' )->retrieve( $wanted_id );
+            my $first_name = $members->[0]{first_name};
+            my $last_name = $members->[0]{last_name};
+            my $email = $members->[0]{email};
+
+            my $body = qq~<html><head><title>Edit person</title></head><body>
+                <form action="/manage/edit/$wanted_id" method="post">
+                First name: <input type="text" name="first_name" value="$first_name" /><br />
+                Last name: <input type="text" name="last_name" value="$last_name" /><br />
+                Email: <input type="text" name="email" value="$email" /><br />
+                <input type="submit" name="submit" value="Save" />
+                </form></body></html>~;
+
+            $c->response->body( $body );
+        }
+
+    }
+
+    sub delete : Local {
+        my ( $self, $c, $wanted_id ) = @_;
+        $c->model( 'DB' )->delete( $wanted_id );
+        $c->res->redirect( "/manage" );
+    }
+
+    sub index :Path :Args(0) {
+        my ( $self, $c ) = @_;
+
+        my $members = $c->model( 'DB' )->retrieve;
+
+        my $body = qq~<html><head><title>Persons list</title></head><body>
+            <a href="/manage/add">Add new person</a><br /><table>~;
+
+        for my $m ( @$members ) {
+            $body .= qq~<tr>
+                <th>$m->{id}</th>
+                <td><a href="/manage/edit/$m->{id}">$m->{last_name}, $m->{first_name}</a></td>
+                <td>$m->{email}</td>
+                <td><a href="/manage/delete/$m->{id}">delete member</a></td>
+            </tr>\n~;
+        }
+
+        $body .= "</table></body></html>";
+
+        $c->response->body( $body );
+    }
+
+    1;
+
+
+Because the business logic has been moved to the fat model, we don't need to read/write/edit the database file directly in the controller, so we don't need the module File::Slurp anymore in the controller.
+
+In the add() subroutine we replaced a big part of the code with a single line:
+
+            $c->model( 'DB' )->add( $c->request->params );
+
+This line calls the subroutine add() from the model DB with the parameter $c->request->params. This parameter is a hashref with the request parameters.
+
+In the subroutine edit() from this controller we also changed a bigger piece of code with a single line:
+
+            $c->model( 'DB' )->edit( $wanted_id, $c->request->params );
+
+This line calls the edit() subroutine from the model DB with the parameters $wanted_id and $c->request->params.
+
+In the same edit() subroutine from the controller another piece of code is replaced with the following lines:
+
+            my $members = $c->model( 'DB' )->retrieve( $wanted_id );
+            my $first_name = $members->[0]{first_name};
+            my $last_name = $members->[0]{last_name};
+            my $email = $members->[0]{email};
+
+The first line of this code calls the subroutine retrieve() from the model DB with the parameter $wanted_idd and stores the result in the arrayref $members. This arrayref will contain just a single element, the record with the wanted ID.
+The other 3 lines just store the first name, last name and email from this arrayref in scalar variables.
+
+In the subroutine delete() from the controller a part of the code was replaced with the line:
+
+        $c->model( 'DB' )->delete( $wanted_id );
+
+This line calls the delete() subroutine from the DB model with the $wanted_id parameter.
+
+In the subroutine index() from the controller a part of the code was replaced with the line:
+
+        my $members = $c->model( 'DB' )->retrieve;
+
+This line calls the subroutine retrieve() from the DB model just like it did in the edit() subroutine, but this time it calls it without any parameter. The result will be an arrayref that will contain all the records in the database.
+
+In this second step we created a fat Catalyst model that holds the business logic, and we deleted the business logic code from the controller. Now if we need to create other controllers in which we need to add/edit/delete/retrieve records from the database, we will be able to do it each time by using just a single line of code which will look the same in all those controllers without needing to copy a lot of code. We won't need to add the database file path in the configuration of all those controllers either, because we added it to the configuration of the model.
+
+As we are going to do on each step, we will test the application using the same actions:
+
+Run again the development server:
+
+    perl script/webapp_server.pl
+
+And then access it at the following URL:
+
+    http://localhost:3000/manage
+
+Click on the "Add new member". It will open the page with the add form.
+Add some data in the form and submit it. It will add that record in the database and it will redirect to the page with the persons.
+Click on the name of the person. It will open the edit form. Change some values and submit that form. It will make the change in the database and it will redirect to the page with the list of persons.
+Click on "Delete member" link for the person you want. It will delete that record from the database and it will redirect to the page with the list of persons.
+
+=head2 Author:
+
+Octavian Rasnita <orasnita at gmail.com>

Added: trunk/examples/CatalystAdvent/root/2012/9.pod
===================================================================
--- trunk/examples/CatalystAdvent/root/2012/9.pod	                        (rev 0)
+++ trunk/examples/CatalystAdvent/root/2012/9.pod	2012-12-05 17:26:06 UTC (rev 14415)
@@ -0,0 +1,381 @@
+=head1 Catalyst in 9 steps - step 3: A light model, standalone lib with a text DB, CLI scripts
+
+Note: You can download the archive with the application created in this third step from:
+
+=begin xhtml
+
+<a href="/calendar/static/2012/WebApp-0.03.tar.gz">WebApp-0.03.tar.gz</a>
+
+=end xhtml
+
+Let's say that we received a .xls file with data for 100 persons we need to add in our database. What can we do?
+
+We can copy/paste all the fields from all those records manually in the add form using the web application we just created. But this doesn't sound like a nice solution.
+Our application is a simple one with only one table of records and with a single web form for adding data, so another solution would be to either save the .xls file as .csv then replace the commas with vertical bars to create the database file directly, or create a short program that parses the .xls file and submits the data by web. But in real applications there are usually more tables of data involved, with much more data, and there are usually more forms to add it in the database, so in those cases it would be very hard to work this way with such an application because it would require too much manual work.
+
+The perfect solution would be to have a standalone library that would handle the business logic, with subroutines/methods for adding, editting, deleting and retrieving data, because we could use them to add data in the database directly.
+Of course, we can create that library, but it would be very good if it would be possible to use it in Catalyst application also, not only as a standalone application, because in that case we would have the business logic code in a single place.
+
+Fortunately, this is possible.
+
+Catalyst offers the possibility of using light models and light controllers. The Catalyst light models will be just a glue to a totally independent Perl application organized as a library.
+
+If we will create a totally separated library that does the business work, the Catalyst application will be just a web interface, nothing more. Alongside that web interface we could create a command line interface also, and even a desktop GUI interface with WxPerl. We could even create another web interface for our application using other web frameworks and use the business logic from that library just as we use any other package of modules from CPAN.
+
+In order to create that light model we need to install a Catalyst component named L<Catalyst::Model::Adaptor|https://metacpan.org/module/Catalyst::Model::Adaptor>, using one of the following commands:
+
+    cpan Catalyst::Model::Adaptor
+    cpanm Catalyst::Model::Adaptor
+    ppm install Catalyst::Model::Adaptor
+
+Then we need to create the independent application library and a light Catalyst model which makes it available in our Catalyst app.
+
+Let's create the independent library that will hold the business logic for our application.
+
+Because our application is small, we will need to create just a single module. We will name it StandaloneApp1.pm.
+
+If the name of a Catalyst app is for example MyApp, the recommendation is to put any other modules you create in this app under the directory lib/MyApp/, so all the modules of this application will have a name starting with "MyApp::". If you do that and if you'll create say a module named Foo.pm, it will be named MyApp::Foo.
+
+The advantage of doing this is that there won't be name conflicts with other modules you might install from CPAN. If you'll put the module Foo.pm under the lib directory you will access it with the name "Foo", but maybe someday it will appear a module named "Foo" on CPAN and you might want to use it, but then you'll see that you won't be able to access it in your Catalyst app because you have another module with the same name in the lib directory of your app.
+
+If you think that you can give a name that it won't be used by somebody else and you don't fear to pollute with a new name, you can put it directly under the lib directory.
+In our example we will put the standalone application module StandaloneApp1.pm directly under the lib directory, for showing that it is a totally independent application that doesn't have any relation with the Catalyst app.
+
+We will include here just the beginning of this standalone application because after this code, all the rest, all the subroutines are absolutely identical with the subroutines from the fat model Persons.pm we made in our previous step, as you may see by downloading the entire source code of the application at this step.
+
+
+    # lib/StandaloneApp1.pm
+
+    package StandaloneApp1;
+
+    use strict;
+    use warnings;
+    use File::Slurp ':all';
+
+    sub new {
+        my ( $class, $params ) = @_;
+        my $self = $params;
+        return bless $self, $class;
+    }
+
+The standalone library will start with the code above and then it will continue with the subroutines from the model DB that we made in the previous step.
+
+As you may see, this module doesn't inherit from L<Catalyst::Model|https://metacpan.org/module/Catalyst::Model> nor from L<Catalyst::Controller|https://metacpan.org/module/Catalyst::Controller> or from another Catalyst module, so it is a totally independent module which can be copied and used in other applications that doesn't even use Catalyst.
+
+This module has a constructor new() that receives a parameter $params which is a hashref with the arguments sent to this constructor.
+
+Now we will create a light Catalyst model which will be placed at WebApp/Model/DB.pm which will have the following content:
+
+    package WebApp::Model::DB;
+    use strict;
+    use warnings;
+    use base 'Catalyst::Model::Adaptor';
+    1;
+
+That's all the necessary code. If you'll have more such models which inherit from Catalyst::Model::Adaptor, their content will be very similar.
+
+OK, but how does this model know what standalone module it needs to access? And how does it send the necessary parameters, like the database file path in our case to that module?
+
+We can specify the configuration for this model in the main module of this Catalyst app, lib/WebApp.pm, by adding the following code in the config hash:
+
+    'Model::DB' => {
+        class => 'StandaloneApp1',
+        args => {
+            database_file => 'D:/web/step3/WebApp/data/database.txt',
+        },
+    },
+
+The first line of this code associate this block of settings with the model DB.pm.
+The second line specifies that this model is an interface to the class named StandaloneApp1 (in the module lib/StandaloneApp1.pm).
+The hashref associated with the key "args" is sent as parameter to the new() constructor of the StandaloneApp1 class.
+
+Note: The value for the key database_file should be modified to match the full path to the database file on your computer!
+
+The configuration hash from the main module will look like:
+
+    __PACKAGE__->config(
+        name => 'WebApp',
+        # Disable deprecated behavior needed by old applications
+        disable_component_resolution_regex_fallback => 1,
+        enable_catalyst_header => 1, # Send X-Catalyst header
+        'Model::DB' => {
+            class => 'StandaloneApp1',
+            args => {
+                database_file => 'D:/web/step3/WebApp/data/database.txt',
+            },
+        },
+    );
+
+So we created the module lib/StandaloneApp1.pm, the light model DB.pm, and we added those few lines in the configuration hash of the application main module.
+
+Our application may appear to be more complex now, since we don't have just a single controller that does everything, or a controller and a model, but we have a controller, a model and a standalone module and it might not be clear how it works.
+
+The process is very simple though:
+
+When the application starts, the model DB.pm will get its config from the configuration hash of the application based on its name, and from that config it will know which is the standalone class it needs to call and which are the arguments it needs to send to the constructor of that class. It will then construct that class by calling the new() constructor creating an object.
+
+When a request comes from a browser, the Catalyst dispatcher will call the appropriate subroutine in the appropriate controller.
+When in a subroutine from a controller will appear a code like $c->model( 'DB' )->retrieve, the model DB will be called and it will return the created object which will use the retrieve() method.
+
+As we are going to do on each step, we will test the application using the same actions:
+
+Run again the development server:
+
+    perl script/webapp_server.pl
+
+And then access it at the following URL:
+
+    http://localhost:3000/manage
+
+Click on the "Add new member". It will open the page with the add form.
+Add some data in the form and submit it. It will add that record in the database and it will redirect to the page with the persons.
+Click on the name of the person. It will open the edit form. Change some values and submit that form. It will make the change in the database and it will redirect to the page with the list of persons.
+Click on "Delete member" link for the person you want. It will delete that record from the database and it will redirect to the page with the list of persons.
+
+We are able now to create CLI or GUI scripts that can add/edit/delete/retrieve records to/from our database and we will be able to create very easy that script that parse that .xls file and add all its records in the database.
+
+As a test we will create CLI scripts that can access the database of this Catalyst application.
+These scripts will construct the StandaloneApp1 object by calling the new() constructor and give it the path to the database file and then that object will use the add/edit/delete/retrieve methods, calling these methods with the parameters we get from command line.
+
+The problem is that the CLI scripts can't read very easy the path to the database file from the module lib/WebApp.pm module, and it wouldn't be nice to hard code it in those scripts also, because if we will need to change it, we will need to change it in more places.
+
+But there is a better way of adding the settings for a Catalyst app than the one we did above.
+
+Instead of adding the configuration in the main module of the Catalyst app lib/WebApp.pm, we can add those settings in the configuration file webapp.conf. The settings from the configuration file overrides the configuration from the main module of the Catalyst app, so they'll be used by the Catalyst app, and the configuration file can be read much easier by other programs, for example by those CLI scripts we want to create.
+
+For doing this, we will need to delete the code we just added in the main module of the application, and add the following lines in the file webapp.conf, after the configuration which is already present in it:
+
+    <Model::DB>
+        class StandaloneApp1
+        <args>
+            database_file D:/web/step1/WebApp/data/database.txt
+        </args>
+    </Model::DB>
+
+This configuration format is by default the Apache style configuration used in httpd.conf, but if you want, after changing the file extension of this configuration file you will be able to use any other format supported by the module Config::Any, like JSON, YAML, XML, INI or even a Plain data structure.
+
+So let's start creating the command line interface!
+
+This interface will be nothing more than 4 scripts, named add.pl, edit.pl, delete.pl and retrieve.pl which we will place in a directory named standalone_app1_script which we created it in the main directory of the Catalyst app.
+
+We know that the configuration file for this Catalyst application is named webapp.conf and that it is placed in the parent directory of the directory standalone_app1_script, so we could access it using the path ../webapp.conf and read the path of the database file from it. But we also know that the Catalyst application might also have a second configuration file named webapp_local.conff, which will override the configurations in the first config file, so it won't be a nice solution to read and merge these 2 configuration files if both of them exist.
+
+Fortunately, there is the module L<Config::JFDI|https://metacpan.org/module/Config::JFDI> that can be used for reading both configuration files of a Catalyst app, with a very simple interface. The advantage it offers is that we don't need to hard code the path to the database file and we also don't need to hard code the path to the configuration files of the Catalyst app.
+
+Note: There is also the module L<Config::ZOMG|https://metacpan.org/module/Config::ZOMG> that does the same thing as Config::JFDI and its POD documentation says that it works faster, but for the moment it has a small bug, so we will use Config::JFDI.
+
+We will use the module L<Getopt::Long|https://metacpan.org/module/Getopt::Long> for reading the parameters which we will use in the command line when we will run these CLI scripts.
+
+We will also use the module L<FindBin|https://metacpan.org/module/FindBin> in order to get the full path to the current directory and of course, we will use the module StandaloneApp1 which will offer the methods for adding/editting/deleting/retrieving records.
+
+If some of the modules mentioned above are not installed, they need to be installed using one of the commands cpan, cpanm or ppm. If we would need to make a quick and dirty script just for adding a big number of records in the database once and then we won't need that script anymore, the modules mentioned above wouldn't be necessary because we could hard code the path to the database and we wouldn't need to read command line parameters.
+
+Here is the content of these CLI scripts:
+
+    # add.pl:
+
+    use strict;
+    use warnings;
+    use Config::JFDI;
+    use Getopt::Long;
+    use FindBin;
+    use lib "$FindBin::Bin/../lib";
+    use StandaloneApp1;
+
+    my ( $first_name, $last_name, $email );
+
+    GetOptions(
+        'first-name=s' => \$first_name,
+        'last-name=s' => \$last_name,
+        'email=s' => \$email,
+    );
+
+    die "Please use --first-name, --last-name and --email parameters\n"
+      unless $first_name and $last_name and $email;
+
+    my $config = Config::JFDI->new( name => 'WebApp', path => "$FindBin::Bin/.." )->get;
+    my $database_file = $config->{'Model::DB'}{args}{database_file};
+
+    my $app = StandaloneApp1->new( { database_file => $database_file } );
+
+    $app->add( { first_name => $first_name, last_name => $last_name, email => $email } );
+
+
+Here are a few explanations for some parts of the script above:
+
+    GetOptions(
+        'first-name=s' => \$first_name,
+        'last-name=s' => \$last_name,
+        'email=s' => \$email,
+    );
+
+This code is used by Getopt::Long and assigns the command line parameters to Perl variables. The script add.pl will be ran with a command like:
+
+    perl add.pl --first-name John --last-name Smith --email j at smith.com
+
+The command above will add the person John Smith in the database of our application. If you get the names and the email address of the persons from another source than the command line, you don't need this code and you also don't need the module Getopt::Long.
+
+    die "Please use --first-name, --last-name and --email parameters\n"
+      unless $first_name and $last_name and $email;
+
+The command above will interrupt the program if those variables are not all defined.
+
+    my $config = Config::JFDI->new( name => 'WebApp', path => "$FindBin::Bin/.." )->get;
+
+This line uses the module Config::JFDI and reads the configuration file of the Catalyst application (or both configuration files if there are 2) and stores this configuration in the hashref $config;
+In our case we need it just for reading the path to the database file from it.
+
+    my $database_file = $config->{'Model::DB'}{args}{database_file};
+
+The line above reads the path to the database file from the $config hashref. If we hard code the path to the database file, we don't need the last 2 explained lines and we don't need the module Config::JFDI.
+
+    my $app = StandaloneApp1->new( { database_file => $database_file } );
+
+This is the constructor of our standalone module.
+
+    $app->add( { first_name => $first_name, last_name => $last_name, email => $email } );
+
+And this line executed the add() method of the standalone module.
+
+A quick and dirty script that will do the same thing would look like:
+
+    use lib '../lib';
+    use StandaloneApp1;
+
+    my $database_file = 'D:/web/step3/WebApp/data/database.txt';
+    my $app = StandaloneApp1->new( { database_file => $database_file } );
+    $app->add( { first_name => 'John', last_name => 'Smith', email => 'j at smith.com' } );
+
+
+    # edit.pl:
+
+    use strict;
+    use warnings;
+    use Config::JFDI;
+    use Getopt::Long;
+    use FindBin;
+    use lib "$FindBin::Bin/../lib";
+    use StandaloneApp1;
+
+    my ( $id, $first_name, $last_name, $email );
+
+    GetOptions(
+        'id=i' => \$id,
+        'first-name=s' => \$first_name,
+        'last-name=s' => \$last_name,
+        'email=s' => \$email,
+    );
+
+    die "Please use --id, --first-name, --last-name and --email parameters\n"
+      unless $id and $first_name and $last_name and $email;
+
+    my $config = Config::JFDI->new( name => 'WebApp', path => "$FindBin::Bin/.." )->get;
+    my $database_file = $config->{'Model::DB'}{args}{database_file};
+
+    my $app = StandaloneApp1->new( { database_file => $database_file } );
+
+    $app->edit( $id, { first_name => $first_name, last_name => $last_name, email => $email } );
+
+As you can see, this script is very similar to the add.pl, but in addition it gets the ID of the record it needs to modify, and sends 2 parameters to the edit() subroutine of the module StandaloneApp1.pm, the $id and the hashref with the first name, last name and email.
+
+
+    # delete.pl:
+
+    use strict;
+    use warnings;
+    use Config::JFDI;
+    use Getopt::Long;
+    use FindBin;
+    use lib "$FindBin::Bin/../lib";
+    use StandaloneApp1;
+
+    my $id;
+
+    GetOptions(
+        'id=i' => \$id,
+    );
+
+    die "Please use --id parameter\n" unless $id;
+
+    my $config = Config::JFDI->new( name => 'WebApp', path => "$FindBin::Bin/.." )->get;
+    my $database_file = $config->{'Model::DB'}{args}{database_file};
+
+    my $app = StandaloneApp1->new( { database_file => $database_file } );
+
+    $app->delete( $id );
+
+This script gets only the ID parameter of the record we want to delete and sends it to the delete() method.
+
+
+    # retrieve.pl:
+
+    use strict;
+    use warnings;
+    use Config::JFDI;
+    use Getopt::Long;
+    use FindBin;
+    use lib "$FindBin::Bin/../lib";
+    use StandaloneApp1;
+
+    my $id;
+
+    GetOptions(
+        'id=i' => \$id,
+    );
+
+    my $config = Config::JFDI->new( name => 'WebApp', path => "$FindBin::Bin/.." )->get;
+    my $database_file = $config->{'Model::DB'}{args}{database_file};
+
+    my $app = StandaloneApp1->new( { database_file => $database_file } );
+
+    my $members = $app->retrieve( $id );
+
+    for my $m ( @$members ) {
+        print "$m->{id}, $m->{first_name}, $m->{last_name}, $m->{email}\n";
+    }
+
+This script gets the optional ID command line parameter. If no parameters are used, the script will retrieve and print all the persons in the database. If an --id command line parameter is used, only the values of the record with that ID are printed.
+
+So with these command line parameters we can do everything we can do by using the web interface, and sometimes this they might be helpful because we can use them in batch scripts or cron/scheduler jobs.
+Anyway, we made them just to show how helpful can be to have a standalone library that can be used in any program, not only in the web application.
+
+We can also use these scripts as a test of the standalone application using commands like:
+
+    perl add.pl --first-name John --last-name Smith --email john at smith.com
+
+    perl edit.pl --id 1 --first-name George --last-name Smith --email j at smith.com
+
+    perl retrieve.pl --id 1
+
+    perl delete.pl --id 1
+
+We also created a test file named t/04standalone_app1.t which tests if the module StandaloneApp1.pm works fine. It is much easier to test if the business logic, the add/edit/delete/retrieve methods work well in a standalone application than in a web app.
+
+You can run all the tests using the following command in the main directory of the web application:
+
+    prove -l t
+
+The results should be:
+
+    All tests successful.
+    Files=4, Tests=44,  3 wallclock secs ( 0.01 usr +  0.00 sys =  0.01 CPU)
+    Result: PASS 
+
+
+Before passing to the next step we can improve this application a little.
+
+Until now, everywhere we configured the path to the database file, in the controller subroutines, controller's configuration, model's configuration, main module of the application, configuration file of the application, we specified the full path to the database file literally. This is not very nice because this path may be different on different computers.
+Catalyst offers a macro that can be used to provide a path relative to the main directory of the application using a line like the following in the configuration hash of the main module WebApp.pm:
+
+    database_file => "__path_to(data,database_file.txt)__",
+
+or the following line in the webapp.conf config file:
+
+    database_file "__path_to(data,database_file.txt)__"
+
+The macro __path_to()__ will get the path to the main directory of the application and between its ( and ) we can add the list of directories under it, until the wanted directory or file, separated by commas.
+You can read more about the macros that can be used in the POD documentation of L<Catalyst::Plugin::ConfigLoader|https://metacpan.org/module/Catalyst::Plugin::ConfigLoader>.
+
+=head2 Author:
+
+Octavian Rasnita <orasnita at gmail.com>

Added: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.01.tar.gz
===================================================================
(Binary files differ)


Property changes on: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.01.tar.gz
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.02.tar.gz
===================================================================
(Binary files differ)


Property changes on: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.02.tar.gz
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.03.tar.gz
===================================================================
(Binary files differ)


Property changes on: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.03.tar.gz
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.04.tar.gz
===================================================================
(Binary files differ)


Property changes on: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.04.tar.gz
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.05.tar.gz
===================================================================
(Binary files differ)


Property changes on: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.05.tar.gz
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.06.tar.gz
===================================================================
(Binary files differ)


Property changes on: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.06.tar.gz
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.07.tar.gz
===================================================================
(Binary files differ)


Property changes on: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.07.tar.gz
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.08.tar.gz
===================================================================
(Binary files differ)


Property changes on: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.08.tar.gz
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.09.tar.gz
===================================================================
(Binary files differ)


Property changes on: trunk/examples/CatalystAdvent/root/static/2012/WebApp-0.09.tar.gz
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream




More information about the Catalyst-commits mailing list