[Catalyst-commits] r14090 - in trunk/examples/Tutorial: . Final Final/Chapter02 Final/Chapter02/Hello Final/Chapter02/Hello/lib Final/Chapter02/Hello/lib/Hello Final/Chapter02/Hello/lib/Hello/Controller Final/Chapter02/Hello/lib/Hello/Model Final/Chapter02/Hello/lib/Hello/View Final/Chapter02/Hello/root Final/Chapter02/Hello/root/site Final/Chapter02/Hello/root/static Final/Chapter02/Hello/root/static/images Final/Chapter02/Hello/script Final/Chapter02/Hello/t Final/Chapter03 Final/Chapter03/MyApp Final/Chapter03/MyApp/lib Final/Chapter03/MyApp/lib/MyApp Final/Chapter03/MyApp/lib/MyApp/Controller Final/Chapter03/MyApp/lib/MyApp/Model Final/Chapter03/MyApp/lib/MyApp/Schema Final/Chapter03/MyApp/lib/MyApp/Schema/Result Final/Chapter03/MyApp/lib/MyApp/View Final/Chapter03/MyApp/root Final/Chapter03/MyApp/root/src Final/Chapter03/MyApp/root/src/books Final/Chapter03/MyApp/root/static Final/Chapter03/MyApp/root/static/css Final/Chapter03/MyApp/root/static/images Final/Chapter03/MyApp/script Final/Chapter03/MyApp/t Final/Chapter04 Final/Chapter04/MyApp Final/Chapter04/MyApp/lib Final/Chapter04/MyApp/lib/MyApp Final/Chapter04/MyApp/lib/MyApp/Controller Final/Chapter04/MyApp/lib/MyApp/Model Final/Chapter04/MyApp/lib/MyApp/Schema Final/Chapter04/MyApp/lib/MyApp/Schema/Result Final/Chapter04/MyApp/lib/MyApp/Schema/ResultSet Final/Chapter04/MyApp/lib/MyApp/View Final/Chapter04/MyApp/root Final/Chapter04/MyApp/root/src Final/Chapter04/MyApp/root/src/books Final/Chapter04/MyApp/root/static Final/Chapter04/MyApp/root/static/css Final/Chapter04/MyApp/root/static/images Final/Chapter04/MyApp/script Final/Chapter04/MyApp/t Final/Chapter05 Final/Chapter05/MyApp Final/Chapter05/MyApp/lib Final/Chapter05/MyApp/lib/MyApp Final/Chapter05/MyApp/lib/MyApp/Controller Final/Chapter05/MyApp/lib/MyApp/Model Final/Chapter05/MyApp/lib/MyApp/Schema Final/Chapter05/MyApp/lib/MyApp/Schema/Result Final/Chapter05/MyApp/lib/MyApp/Schema/ResultSet Final/Chapter05/MyApp/lib/MyApp/View Final/Chapter05/MyApp/root Final/Chapter05/MyApp/root/src Final/Chapter05/MyApp/root/src/books Final/Chapter05/MyApp/root/static Final/Chapter05/MyApp/root/static/css Final/Chapter05/MyApp/root/static/images Final/Chapter05/MyApp/script Final/Chapter05/MyApp/t Final/Chapter06 Final/Chapter06/MyApp Final/Chapter06/MyApp/lib Final/Chapter06/MyApp/lib/MyApp Final/Chapter06/MyApp/lib/MyApp/Controller Final/Chapter06/MyApp/lib/MyApp/Model Final/Chapter06/MyApp/lib/MyApp/Schema Final/Chapter06/MyApp/lib/MyApp/Schema/Result Final/Chapter06/MyApp/lib/MyApp/Schema/ResultSet Final/Chapter06/MyApp/lib/MyApp/View Final/Chapter06/MyApp/root Final/Chapter06/MyApp/root/src Final/Chapter06/MyApp/root/src/books Final/Chapter06/MyApp/root/static Final/Chapter06/MyApp/root/static/css Final/Chapter06/MyApp/root/static/images Final/Chapter06/MyApp/script Final/Chapter06/MyApp/t Final/Chapter07 Final/Chapter07/MyApp Final/Chapter07/MyApp/lib Final/Chapter07/MyApp/lib/MyApp Final/Chapter07/MyApp/lib/MyApp/Controller Final/Chapter07/MyApp/lib/MyApp/Model Final/Chapter07/MyApp/lib/MyApp/Schema Final/Chapter07/MyApp/lib/MyApp/Schema/Result Final/Chapter07/MyApp/lib/MyApp/Schema/ResultSet Final/Chapter07/MyApp/lib/MyApp/View Final/Chapter07/MyApp/root Final/Chapter07/MyApp/root/src Final/Chapter07/MyApp/root/src/books Final/Chapter07/MyApp/root/static Final/Chapter07/MyApp/root/static/css Final/Chapter07/MyApp/root/static/images Final/Chapter07/MyApp/script Final/Chapter07/MyApp/t Final/Chapter08 Final/Chapter08/MyApp Final/Chapter08/MyApp/lib Final/Chapter08/MyApp/lib/MyApp Final/Chapter08/MyApp/lib/MyApp/Controller Final/Chapter08/MyApp/lib/MyApp/Model Final/Chapter08/MyApp/lib/MyApp/Schema Final/Chapter08/MyApp/lib/MyApp/Schema/Result Final/Chapter08/MyApp/lib/MyApp/Schema/ResultSet Final/Chapter08/MyApp/lib/MyApp/View Final/Chapter08/MyApp/root Final/Chapter08/MyApp/root/src Final/Chapter08/MyApp/root/src/books Final/Chapter08/MyApp/root/static Final/Chapter08/MyApp/root/static/css Final/Chapter08/MyApp/root/static/images Final/Chapter08/MyApp/script Final/Chapter08/MyApp/t Final/Chapter09_FormFu Final/Chapter09_FormFu/MyApp Final/Chapter09_FormFu/MyApp/lib Final/Chapter09_FormFu/MyApp/lib/MyApp Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller Final/Chapter09_FormFu/MyApp/lib/MyApp/Model Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/ResultSet Final/Chapter09_FormFu/MyApp/lib/MyApp/View Final/Chapter09_FormFu/MyApp/root Final/Chapter09_FormFu/MyApp/root/forms Final/Chapter09_FormFu/MyApp/root/forms/books Final/Chapter09_FormFu/MyApp/root/src Final/Chapter09_FormFu/MyApp/root/src/books Final/Chapter09_FormFu/MyApp/root/static Final/Chapter09_FormFu/MyApp/root/static/css Final/Chapter09_FormFu/MyApp/root/static/images Final/Chapter09_FormFu/MyApp/script Final/Chapter09_FormFu/MyApp/t

hkclark at dev.catalyst.perl.org hkclark at dev.catalyst.perl.org
Sat Sep 3 19:35:03 GMT 2011


Author: hkclark
Date: 2011-09-03 19:35:03 +0000 (Sat, 03 Sep 2011)
New Revision: 14090

Added:
   trunk/examples/Tutorial/Final/
   trunk/examples/Tutorial/Final/Chapter02/
   trunk/examples/Tutorial/Final/Chapter02/Hello/
   trunk/examples/Tutorial/Final/Chapter02/Hello/Changes
   trunk/examples/Tutorial/Final/Chapter02/Hello/Makefile.PL
   trunk/examples/Tutorial/Final/Chapter02/Hello/README
   trunk/examples/Tutorial/Final/Chapter02/Hello/hello.conf
   trunk/examples/Tutorial/Final/Chapter02/Hello/hello.psgi
   trunk/examples/Tutorial/Final/Chapter02/Hello/lib/
   trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello.pm
   trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/
   trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/Controller/
   trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/Controller/Root.pm
   trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/Controller/Site.pm
   trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/Model/
   trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/View/
   trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/View/HTML.pm
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/favicon.ico
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/hello.tt
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/site/
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/site/test.tt
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_120x50_built.png
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_120x50_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_120x50_powered.png
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_120x50_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_88x31_built.png
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_88x31_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_88x31_powered.png
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_88x31_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/catalyst_logo.png
   trunk/examples/Tutorial/Final/Chapter02/Hello/script/
   trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_cgi.pl
   trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_create.pl
   trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_fastcgi.pl
   trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_server.pl
   trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_test.pl
   trunk/examples/Tutorial/Final/Chapter02/Hello/t/
   trunk/examples/Tutorial/Final/Chapter02/Hello/t/01app.t
   trunk/examples/Tutorial/Final/Chapter02/Hello/t/02pod.t
   trunk/examples/Tutorial/Final/Chapter02/Hello/t/03podcoverage.t
   trunk/examples/Tutorial/Final/Chapter02/Hello/t/controller_Site.t
   trunk/examples/Tutorial/Final/Chapter02/Hello/t/view_HTML.t
   trunk/examples/Tutorial/Final/Chapter03/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/Changes
   trunk/examples/Tutorial/Final/Chapter03/MyApp/Makefile.PL
   trunk/examples/Tutorial/Final/Chapter03/MyApp/README
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp.pm
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Controller/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Controller/Books.pm
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Controller/Root.pm
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Model/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Model/DB.pm
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema.pm
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/Result/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/Result/Author.pm
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/Result/Book.pm
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/View/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/View/HTML.pm
   trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp.conf
   trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp.db
   trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp.psgi
   trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp01.sql
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/favicon.ico
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/src/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/src/books/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/src/books/list.tt2
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/src/wrapper.tt2
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/css/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/css/main.css
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_120x50_built.png
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_120x50_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_120x50_powered.png
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_120x50_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_88x31_built.png
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_88x31_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_88x31_powered.png
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_88x31_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/catalyst_logo.png
   trunk/examples/Tutorial/Final/Chapter03/MyApp/script/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_cgi.pl
   trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_create.pl
   trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_fastcgi.pl
   trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_server.pl
   trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_test.pl
   trunk/examples/Tutorial/Final/Chapter03/MyApp/t/
   trunk/examples/Tutorial/Final/Chapter03/MyApp/t/01app.t
   trunk/examples/Tutorial/Final/Chapter03/MyApp/t/02pod.t
   trunk/examples/Tutorial/Final/Chapter03/MyApp/t/03podcoverage.t
   trunk/examples/Tutorial/Final/Chapter03/MyApp/t/controller_Books.t
   trunk/examples/Tutorial/Final/Chapter03/MyApp/t/model_DB.t
   trunk/examples/Tutorial/Final/Chapter03/MyApp/t/view_HTML.t
   trunk/examples/Tutorial/Final/Chapter04/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/Changes
   trunk/examples/Tutorial/Final/Chapter04/MyApp/Makefile.PL
   trunk/examples/Tutorial/Final/Chapter04/MyApp/README
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp.pm
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Controller/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Controller/Books.pm
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Controller/Root.pm
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Model/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Model/DB.pm
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema.pm
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/Result/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/Result/Author.pm
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/Result/Book.pm
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/ResultSet/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/ResultSet/Book.pm
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/View/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/View/HTML.pm
   trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp.conf
   trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp.db
   trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp.psgi
   trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp01.sql
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/favicon.ico
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/books/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/books/create_done.tt2
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/books/form_create.tt2
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/books/list.tt2
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/wrapper.tt2
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/css/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/css/main.css
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_120x50_built.png
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_120x50_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_120x50_powered.png
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_120x50_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_88x31_built.png
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_88x31_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_88x31_powered.png
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_88x31_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/catalyst_logo.png
   trunk/examples/Tutorial/Final/Chapter04/MyApp/script/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_cgi.pl
   trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_create.pl
   trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_fastcgi.pl
   trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_server.pl
   trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_test.pl
   trunk/examples/Tutorial/Final/Chapter04/MyApp/t/
   trunk/examples/Tutorial/Final/Chapter04/MyApp/t/01app.t
   trunk/examples/Tutorial/Final/Chapter04/MyApp/t/02pod.t
   trunk/examples/Tutorial/Final/Chapter04/MyApp/t/03podcoverage.t
   trunk/examples/Tutorial/Final/Chapter04/MyApp/t/controller_Books.t
   trunk/examples/Tutorial/Final/Chapter04/MyApp/t/model_DB.t
   trunk/examples/Tutorial/Final/Chapter04/MyApp/t/view_HTML.t
   trunk/examples/Tutorial/Final/Chapter05/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/Changes
   trunk/examples/Tutorial/Final/Chapter05/MyApp/Makefile.PL
   trunk/examples/Tutorial/Final/Chapter05/MyApp/README
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Books.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Login.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Logout.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Root.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Model/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Model/DB.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/Author.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/Book.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/Role.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/User.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/UserRole.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/ResultSet/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/ResultSet/Book.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/View/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/View/HTML.pm
   trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp.conf
   trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp.db
   trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp.psgi
   trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp01.sql
   trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp02.sql
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/favicon.ico
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/books/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/books/create_done.tt2
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/books/form_create.tt2
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/books/list.tt2
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/login.tt2
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/wrapper.tt2
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/css/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/css/main.css
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_120x50_built.png
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_120x50_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_120x50_powered.png
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_120x50_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_88x31_built.png
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_88x31_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_88x31_powered.png
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_88x31_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/catalyst_logo.png
   trunk/examples/Tutorial/Final/Chapter05/MyApp/script/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_cgi.pl
   trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_create.pl
   trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_fastcgi.pl
   trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_server.pl
   trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_test.pl
   trunk/examples/Tutorial/Final/Chapter05/MyApp/set_hashed_passwords.pl
   trunk/examples/Tutorial/Final/Chapter05/MyApp/t/
   trunk/examples/Tutorial/Final/Chapter05/MyApp/t/01app.t
   trunk/examples/Tutorial/Final/Chapter05/MyApp/t/02pod.t
   trunk/examples/Tutorial/Final/Chapter05/MyApp/t/03podcoverage.t
   trunk/examples/Tutorial/Final/Chapter05/MyApp/t/controller_Books.t
   trunk/examples/Tutorial/Final/Chapter05/MyApp/t/controller_Login.t
   trunk/examples/Tutorial/Final/Chapter05/MyApp/t/controller_Logout.t
   trunk/examples/Tutorial/Final/Chapter05/MyApp/t/model_DB.t
   trunk/examples/Tutorial/Final/Chapter05/MyApp/t/view_HTML.t
   trunk/examples/Tutorial/Final/Chapter06/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/Changes
   trunk/examples/Tutorial/Final/Chapter06/MyApp/Makefile.PL
   trunk/examples/Tutorial/Final/Chapter06/MyApp/README
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Books.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Login.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Logout.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Root.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Model/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Model/DB.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/Author.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/Book.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/Role.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/User.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/UserRole.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/ResultSet/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/ResultSet/Book.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/View/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/View/HTML.pm
   trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp.conf
   trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp.db
   trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp.psgi
   trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp01.sql
   trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp02.sql
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/favicon.ico
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/books/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/books/create_done.tt2
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/books/form_create.tt2
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/books/list.tt2
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/error_noperms.tt2
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/login.tt2
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/wrapper.tt2
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/css/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/css/main.css
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_120x50_built.png
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_120x50_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_120x50_powered.png
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_120x50_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_88x31_built.png
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_88x31_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_88x31_powered.png
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_88x31_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/catalyst_logo.png
   trunk/examples/Tutorial/Final/Chapter06/MyApp/script/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_cgi.pl
   trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_create.pl
   trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_fastcgi.pl
   trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_server.pl
   trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_test.pl
   trunk/examples/Tutorial/Final/Chapter06/MyApp/set_hashed_passwords.pl
   trunk/examples/Tutorial/Final/Chapter06/MyApp/t/
   trunk/examples/Tutorial/Final/Chapter06/MyApp/t/01app.t
   trunk/examples/Tutorial/Final/Chapter06/MyApp/t/02pod.t
   trunk/examples/Tutorial/Final/Chapter06/MyApp/t/03podcoverage.t
   trunk/examples/Tutorial/Final/Chapter06/MyApp/t/controller_Books.t
   trunk/examples/Tutorial/Final/Chapter06/MyApp/t/controller_Login.t
   trunk/examples/Tutorial/Final/Chapter06/MyApp/t/controller_Logout.t
   trunk/examples/Tutorial/Final/Chapter06/MyApp/t/model_DB.t
   trunk/examples/Tutorial/Final/Chapter06/MyApp/t/view_HTML.t
   trunk/examples/Tutorial/Final/Chapter07/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/Changes
   trunk/examples/Tutorial/Final/Chapter07/MyApp/Makefile.PL
   trunk/examples/Tutorial/Final/Chapter07/MyApp/README
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Books.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Login.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Logout.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Root.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Model/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Model/DB.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/Author.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/Book.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/Role.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/User.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/UserRole.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/ResultSet/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/ResultSet/Book.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/View/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/View/HTML.pm
   trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp.conf
   trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp.db
   trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp.psgi
   trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp01.sql
   trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp02.sql
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/favicon.ico
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/books/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/books/create_done.tt2
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/books/form_create.tt2
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/books/list.tt2
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/error_noperms.tt2
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/login.tt2
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/wrapper.tt2
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/css/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/css/main.css
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_120x50_built.png
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_120x50_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_120x50_powered.png
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_120x50_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_88x31_built.png
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_88x31_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_88x31_powered.png
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_88x31_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/catalyst_logo.png
   trunk/examples/Tutorial/Final/Chapter07/MyApp/script/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_cgi.pl
   trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_create.pl
   trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_fastcgi.pl
   trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_server.pl
   trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_test.pl
   trunk/examples/Tutorial/Final/Chapter07/MyApp/set_hashed_passwords.pl
   trunk/examples/Tutorial/Final/Chapter07/MyApp/t/
   trunk/examples/Tutorial/Final/Chapter07/MyApp/t/01app.t
   trunk/examples/Tutorial/Final/Chapter07/MyApp/t/02pod.t
   trunk/examples/Tutorial/Final/Chapter07/MyApp/t/03podcoverage.t
   trunk/examples/Tutorial/Final/Chapter07/MyApp/t/controller_Books.t
   trunk/examples/Tutorial/Final/Chapter07/MyApp/t/controller_Login.t
   trunk/examples/Tutorial/Final/Chapter07/MyApp/t/controller_Logout.t
   trunk/examples/Tutorial/Final/Chapter07/MyApp/t/model_DB.t
   trunk/examples/Tutorial/Final/Chapter07/MyApp/t/view_HTML.t
   trunk/examples/Tutorial/Final/Chapter08/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/Changes
   trunk/examples/Tutorial/Final/Chapter08/MyApp/Makefile.PL
   trunk/examples/Tutorial/Final/Chapter08/MyApp/README
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Books.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Login.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Logout.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Root.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Model/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Model/DB.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/Author.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/Book.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/Role.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/User.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/UserRole.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/ResultSet/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/ResultSet/Book.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/View/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/View/HTML.pm
   trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp.conf
   trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp.db
   trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp.psgi
   trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp01.sql
   trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp02.sql
   trunk/examples/Tutorial/Final/Chapter08/MyApp/myappTEST.db
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/favicon.ico
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/books/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/books/create_done.tt2
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/books/form_create.tt2
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/books/list.tt2
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/error_noperms.tt2
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/login.tt2
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/wrapper.tt2
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/css/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/css/main.css
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_120x50_built.png
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_120x50_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_120x50_powered.png
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_120x50_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_88x31_built.png
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_88x31_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_88x31_powered.png
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_88x31_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/catalyst_logo.png
   trunk/examples/Tutorial/Final/Chapter08/MyApp/script/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_cgi.pl
   trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_create.pl
   trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_fastcgi.pl
   trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_server.pl
   trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_test.pl
   trunk/examples/Tutorial/Final/Chapter08/MyApp/set_hashed_passwords.pl
   trunk/examples/Tutorial/Final/Chapter08/MyApp/t/
   trunk/examples/Tutorial/Final/Chapter08/MyApp/t/01app.t
   trunk/examples/Tutorial/Final/Chapter08/MyApp/t/02pod.t
   trunk/examples/Tutorial/Final/Chapter08/MyApp/t/03podcoverage.t
   trunk/examples/Tutorial/Final/Chapter08/MyApp/t/controller_Books.t
   trunk/examples/Tutorial/Final/Chapter08/MyApp/t/controller_Login.t
   trunk/examples/Tutorial/Final/Chapter08/MyApp/t/controller_Logout.t
   trunk/examples/Tutorial/Final/Chapter08/MyApp/t/live_app01.t
   trunk/examples/Tutorial/Final/Chapter08/MyApp/t/model_DB.t
   trunk/examples/Tutorial/Final/Chapter08/MyApp/t/view_HTML.t
   trunk/examples/Tutorial/Final/Chapter09_FormFu/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/Changes
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/Makefile.PL
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/README
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Books.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Login.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Logout.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Root.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Model/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Model/DB.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/Author.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/Book.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/Role.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/User.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/UserRole.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/ResultSet/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/ResultSet/Book.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/View/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/View/HTML.pm
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp.conf
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp.db
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp.psgi
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp01.sql
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp02.sql
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myappTEST.db
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/favicon.ico
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/forms/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/forms/books/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/forms/books/formfu_create.yml
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/create_done.tt2
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/form_create.tt2
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/formfu_create.tt2
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/list.tt2
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/error_noperms.tt2
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/login.tt2
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/wrapper.tt2
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/css/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/css/main.css
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_120x50_built.png
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_120x50_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_120x50_powered.png
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_120x50_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_88x31_built.png
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_88x31_built_shadow.png
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_88x31_powered.png
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_88x31_powered_shadow.png
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/catalyst_logo.png
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_cgi.pl
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_create.pl
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_fastcgi.pl
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_server.pl
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_test.pl
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/set_hashed_passwords.pl
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/01app.t
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/02pod.t
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/03podcoverage.t
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/controller_Books.t
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/controller_Login.t
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/controller_Logout.t
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/live_app01.t
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/model_DB.t
   trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/view_HTML.t
Removed:
   trunk/examples/Tutorial/Hello_Chapter2.tar.gz
   trunk/examples/Tutorial/Hello_Chapter2/
   trunk/examples/Tutorial/MyApp_Chapter3.tar.gz
   trunk/examples/Tutorial/MyApp_Chapter3/
   trunk/examples/Tutorial/MyApp_Chapter4.tar.gz
   trunk/examples/Tutorial/MyApp_Chapter4/
   trunk/examples/Tutorial/MyApp_Chapter5.tar.gz
   trunk/examples/Tutorial/MyApp_Chapter5/
   trunk/examples/Tutorial/MyApp_Chapter6.tar.gz
   trunk/examples/Tutorial/MyApp_Chapter6/
   trunk/examples/Tutorial/MyApp_Chapter7.tar.gz
   trunk/examples/Tutorial/MyApp_Chapter7/
   trunk/examples/Tutorial/MyApp_Chapter8.tar.gz
   trunk/examples/Tutorial/MyApp_Chapter8/
   trunk/examples/Tutorial/MyApp_Chapter9_FormFu.tar.gz
   trunk/examples/Tutorial/MyApp_Chapter9_FormFu/
   trunk/examples/Tutorial/MyApp_Chapter9_FormHandler/
Log:
Update for v5.9002 of Catalyst::Manual


Property changes on: trunk/examples/Tutorial/Final
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter02
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/Changes
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/Changes	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/Changes	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,4 @@
+This file documents the revision history for Perl extension Hello.
+
+0.01  2011-08-30 17:15:18
+        - initial revision, generated by Catalyst

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/Makefile.PL
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/Makefile.PL	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/Makefile.PL	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+# IMPORTANT: if you delete this file your app will not work as
+# expected.  You have been warned.
+use inc::Module::Install;
+use Module::Install::Catalyst; # Complain loudly if you don't have
+                               # Catalyst::Devel installed or haven't said
+                               # 'make dist' to create a standalone tarball.
+
+name 'Hello';
+all_from 'lib/Hello.pm';
+
+requires 'Catalyst::Runtime' => '5.90002';
+requires 'Catalyst::Plugin::ConfigLoader';
+requires 'Catalyst::Plugin::Static::Simple';
+requires 'Catalyst::Action::RenderView';
+requires 'Moose';
+requires 'namespace::autoclean';
+requires 'Config::General'; # This should reflect the config file format you've chosen
+                 # See Catalyst::Plugin::ConfigLoader for supported formats
+test_requires 'Test::More' => '0.88';
+catalyst;
+
+install_script glob('script/*.pl');
+auto_install;
+WriteAll;

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/README
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/README	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/README	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1 @@
+Run script/hello_server.pl to test the application.

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/hello.conf
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/hello.conf	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/hello.conf	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,3 @@
+# rename this file to hello.yml and put a ':' after 'name' if
+# you want to use YAML like in old versions of Catalyst
+name Hello

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/hello.psgi
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/hello.psgi	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/hello.psgi	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+use Hello;
+
+my $app = Hello->apply_default_middlewares(Hello->psgi_app);
+$app;
+


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/lib
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/Controller
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/Controller/Root.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/Controller/Root.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/Controller/Root.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,77 @@
+package Hello::Controller::Root;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller' }
+
+#
+# Sets the actions in this controller to be registered with no prefix
+# so they function identically to actions created in MyApp.pm
+#
+__PACKAGE__->config(namespace => '');
+
+=head1 NAME
+
+Hello::Controller::Root - Root Controller for Hello
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 METHODS
+
+=head2 index
+
+The root page (/)
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    # Hello World
+    $c->response->body( $c->welcome_message );
+}
+
+=head2 default
+
+Standard 404 error page
+
+=cut
+
+sub default :Path {
+    my ( $self, $c ) = @_;
+    $c->response->body( 'Page not found' );
+    $c->response->status(404);
+}
+
+=head2 end
+
+Attempt to render a view, if needed.
+
+=cut
+
+sub end : ActionClass('RenderView') {}
+
+
+sub hello :Global {
+    my ( $self, $c ) = @_;
+
+    $c->response->body("Hello, World!");
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/Controller/Site.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/Controller/Site.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/Controller/Site.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,52 @@
+package Hello::Controller::Site;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+Hello::Controller::Site - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    $c->response->body('Matched Hello::Controller::Site in Site.');
+}
+
+
+sub test :Local {
+    my ( $self, $c ) = @_;
+
+    $c->stash(username => 'John',
+              template => 'site/test.tt');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/Model
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/View
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/View/HTML.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/View/HTML.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello/View/HTML.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,36 @@
+package Hello::View::HTML;
+
+use strict;
+use warnings;
+
+use base 'Catalyst::View::TT';
+
+__PACKAGE__->config(
+    TEMPLATE_EXTENSION => '.tt',
+    render_die => 1,
+);
+
+=head1 NAME
+
+Hello::View::HTML - TT View for Hello
+
+=head1 DESCRIPTION
+
+TT View for Hello.
+
+=head1 SEE ALSO
+
+L<Hello>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/lib/Hello.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,75 @@
+package Hello;
+use Moose;
+use namespace::autoclean;
+
+use Catalyst::Runtime 5.80;
+
+# Set flags and add plugins for the application.
+#
+# Note that ORDERING IS IMPORTANT here as plugins are initialized in order,
+# therefore you almost certainly want to keep ConfigLoader at the head of the
+# list if you're using it.
+#
+#         -Debug: activates the debug mode for very useful log messages
+#   ConfigLoader: will load the configuration from a Config::General file in the
+#                 application's home directory
+# Static::Simple: will serve static files from the application's root
+#                 directory
+
+use Catalyst qw/
+    -Debug
+    ConfigLoader
+    Static::Simple
+/;
+
+extends 'Catalyst';
+
+our $VERSION = '0.01';
+
+# Configure the application.
+#
+# Note that settings in hello.conf (or other external
+# configuration file that you set up manually) take precedence
+# over this when using ConfigLoader. Thus configuration
+# details given here can function as a default configuration,
+# with an external configuration file acting as an override for
+# local deployment.
+
+__PACKAGE__->config(
+    name => 'Hello',
+    # Disable deprecated behavior needed by old applications
+    disable_component_resolution_regex_fallback => 1,
+);
+
+# Start the application
+__PACKAGE__->setup();
+
+
+=head1 NAME
+
+Hello - Catalyst based application
+
+=head1 SYNOPSIS
+
+    script/hello_server.pl
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 SEE ALSO
+
+L<Hello::Controller::Root>, L<Catalyst>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/root
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/root/favicon.ico
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/root/favicon.ico
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/root/hello.tt
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/root/hello.tt	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/root/hello.tt	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,4 @@
+<p>
+    This is a TT view template, called '[% template.name %]'.
+</p>
+


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/root/site
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/root/site/test.tt
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/root/site/test.tt	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/root/site/test.tt	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1 @@
+<p>Hello, [% username %]!</p>


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_120x50_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_120x50_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_120x50_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_120x50_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_120x50_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_120x50_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_120x50_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_120x50_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_88x31_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_88x31_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_88x31_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_88x31_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_88x31_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_88x31_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_88x31_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/btn_88x31_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/catalyst_logo.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/root/static/images/catalyst_logo.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/script
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_cgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_cgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_cgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('Hello', 'CGI');
+
+1;
+
+=head1 NAME
+
+hello_cgi.pl - Catalyst CGI
+
+=head1 SYNOPSIS
+
+See L<Catalyst::Manual>
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as a cgi script.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_create.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_create.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_create.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('Hello', 'Create');
+
+1;
+
+=head1 NAME
+
+hello_create.pl - Create a new Catalyst Component
+
+=head1 SYNOPSIS
+
+hello_create.pl [options] model|view|controller name [helper] [options]
+
+ Options:
+   --force        don't create a .new file where a file to be created exists
+   --mechanize    use Test::WWW::Mechanize::Catalyst for tests if available
+   --help         display this help and exits
+
+ Examples:
+   hello_create.pl controller My::Controller
+   hello_create.pl --mechanize controller My::Controller
+   hello_create.pl view My::View
+   hello_create.pl view HTML TT
+   hello_create.pl model My::Model
+   hello_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\
+   dbi:SQLite:/tmp/my.db
+   hello_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static\
+   [Loader opts like db_schema, naming] dbi:Pg:dbname=foo root 4321
+   [connect_info opts like quote_char, name_sep]
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+   perldoc Catalyst::Helper::Model::DBIC::Schema
+   perldoc Catalyst::Model::DBIC::Schema
+   perldoc Catalyst::View::TT
+
+=head1 DESCRIPTION
+
+Create a new Catalyst Component.
+
+Existing component files are not overwritten.  If any of the component files
+to be created already exist the file will be written with a '.new' suffix.
+This behavior can be suppressed with the C<-force> option.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_fastcgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_fastcgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_fastcgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,48 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('Hello', 'FastCGI');
+
+1;
+
+=head1 NAME
+
+hello_fastcgi.pl - Catalyst FastCGI
+
+=head1 SYNOPSIS
+
+hello_fastcgi.pl [options]
+
+ Options:
+   -? -help      display this help and exits
+   -l --listen   Socket path to listen on
+                 (defaults to standard input)
+                 can be HOST:PORT, :PORT or a
+                 filesystem path
+   -n --nproc    specify number of processes to keep
+                 to serve requests (defaults to 1,
+                 requires -listen)
+   -p --pidfile  specify filename for pid file
+                 (requires -listen)
+   -d --daemon   daemonize (requires -listen)
+   -M --manager  specify alternate process manager
+                 (FCGI::ProcManager sub-class)
+                 or empty string to disable
+   -e --keeperr  send error messages to STDOUT, not
+                 to the webserver
+   --proc_title  Set the process title (is possible)
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as fastcgi.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_server.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_server.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_server.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+BEGIN {
+    $ENV{CATALYST_SCRIPT_GEN} = 40;
+}
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('Hello', 'Server');
+
+1;
+
+=head1 NAME
+
+hello_server.pl - Catalyst Test Server
+
+=head1 SYNOPSIS
+
+hello_server.pl [options]
+
+   -d --debug           force debug mode
+   -f --fork            handle each request in a new process
+                        (defaults to false)
+   -? --help            display this help and exits
+   -h --host            host (defaults to all)
+   -p --port            port (defaults to 3000)
+   -k --keepalive       enable keep-alive connections
+   -r --restart         restart when files get modified
+                        (defaults to false)
+   -rd --restart_delay  delay between file checks
+                        (ignored if you have Linux::Inotify2 installed)
+   -rr --restart_regex  regex match files that trigger
+                        a restart when modified
+                        (defaults to '\.yml$|\.yaml$|\.conf|\.pm$')
+   --restart_directory  the directory to search for
+                        modified files, can be set multiple times
+                        (defaults to '[SCRIPT_DIR]/..')
+   --follow_symlinks    follow symlinks in search directories
+                        (defaults to false. this is a no-op on Win32)
+   --background         run the process in the background
+   --pidfile            specify filename for pid file
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst Testserver for this application.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_test.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_test.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/script/hello_test.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,40 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('Hello', 'Test');
+
+1;
+
+=head1 NAME
+
+hello_test.pl - Catalyst Test
+
+=head1 SYNOPSIS
+
+hello_test.pl [options] uri
+
+ Options:
+   --help    display this help and exits
+
+ Examples:
+   hello_test.pl http://localhost/some_action
+   hello_test.pl /some_action
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst action from the command line.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut


Property changes on: trunk/examples/Tutorial/Final/Chapter02/Hello/t
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/t/01app.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/t/01app.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/t/01app.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+use Catalyst::Test 'Hello';
+
+ok( request('/')->is_success, 'Request should succeed' );
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/t/02pod.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/t/02pod.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/t/02pod.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+eval "use Test::Pod 1.14";
+plan skip_all => 'Test::Pod 1.14 required' if $@;
+
+all_pod_files_ok();

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/t/03podcoverage.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/t/03podcoverage.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/t/03podcoverage.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,14 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@;
+
+eval "use Pod::Coverage 0.20";
+plan skip_all => 'Pod::Coverage 0.20 required' if $@;
+
+all_pod_coverage_ok();

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/t/controller_Site.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/t/controller_Site.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/t/controller_Site.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'Hello';
+use Hello::Controller::Site;
+
+ok( request('/site')->is_success, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter02/Hello/t/view_HTML.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter02/Hello/t/view_HTML.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter02/Hello/t/view_HTML.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+BEGIN { use_ok 'Hello::View::HTML' }
+
+done_testing();


Property changes on: trunk/examples/Tutorial/Final/Chapter03
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/Changes
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/Changes	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/Changes	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,4 @@
+This file documents the revision history for Perl extension MyApp.
+
+0.01  2011-08-30 17:51:40
+        - initial revision, generated by Catalyst

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/Makefile.PL
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/Makefile.PL	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/Makefile.PL	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+# IMPORTANT: if you delete this file your app will not work as
+# expected.  You have been warned.
+use inc::Module::Install;
+use Module::Install::Catalyst; # Complain loudly if you don't have
+                               # Catalyst::Devel installed or haven't said
+                               # 'make dist' to create a standalone tarball.
+
+name 'MyApp';
+all_from 'lib/MyApp.pm';
+
+requires 'Catalyst::Runtime' => '5.90002';
+requires 'Catalyst::Plugin::ConfigLoader';
+requires 'Catalyst::Plugin::Static::Simple';
+requires 'Catalyst::Action::RenderView';
+requires 'Moose';
+requires 'namespace::autoclean';
+requires 'Config::General'; # This should reflect the config file format you've chosen
+                 # See Catalyst::Plugin::ConfigLoader for supported formats
+test_requires 'Test::More' => '0.88';
+catalyst;
+
+install_script glob('script/*.pl');
+auto_install;
+WriteAll;

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/README
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/README	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/README	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1 @@
+Run script/myapp_server.pl to test the application.


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Controller
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Controller/Books.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Controller/Books.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Controller/Books.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,66 @@
+package MyApp::Controller::Books;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Books - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    $c->response->body('Matched MyApp::Controller::Books in Books.');
+}
+
+
+=head2 list
+
+Fetch all book objects and pass to books/list.tt2 in stash to be displayed
+
+=cut
+
+sub list :Local {
+    # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst
+    # 'Context' that's used to 'glue together' the various components
+    # that make up the application
+    my ($self, $c) = @_;
+
+    # Retrieve all of the book records as book model objects and store
+    # in the stash where they can be accessed by the TT template
+    $c->stash(books => [$c->model('DB::Book')->all]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Controller/Root.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Controller/Root.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Controller/Root.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,69 @@
+package MyApp::Controller::Root;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller' }
+
+#
+# Sets the actions in this controller to be registered with no prefix
+# so they function identically to actions created in MyApp.pm
+#
+__PACKAGE__->config(namespace => '');
+
+=head1 NAME
+
+MyApp::Controller::Root - Root Controller for MyApp
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 METHODS
+
+=head2 index
+
+The root page (/)
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    # Hello World
+    $c->response->body( $c->welcome_message );
+}
+
+=head2 default
+
+Standard 404 error page
+
+=cut
+
+sub default :Path {
+    my ( $self, $c ) = @_;
+    $c->response->body( 'Page not found' );
+    $c->response->status(404);
+}
+
+=head2 end
+
+Attempt to render a view, if needed.
+
+=cut
+
+sub end : ActionClass('RenderView') {}
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Model
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Model/DB.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Model/DB.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Model/DB.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,44 @@
+package MyApp::Model::DB;
+
+use strict;
+use base 'Catalyst::Model::DBIC::Schema';
+
+__PACKAGE__->config(
+    schema_class => 'MyApp::Schema',
+    
+    connect_info => {
+        dsn => 'dbi:SQLite:myapp.db',
+        user => '',
+        password => '',
+        on_connect_do => q{PRAGMA foreign_keys = ON},
+    }
+);
+
+=head1 NAME
+
+MyApp::Model::DB - Catalyst DBIC Schema Model
+
+=head1 SYNOPSIS
+
+See L<MyApp>
+
+=head1 DESCRIPTION
+
+L<Catalyst::Model::DBIC::Schema> Model using schema L<MyApp::Schema>
+
+=head1 GENERATED BY
+
+Catalyst::Helper::Model::DBIC::Schema - 0.54
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/Result
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/Result/Author.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/Result/Author.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/Result/Author.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,88 @@
+package MyApp::Schema::Result::Author;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime");
+
+=head1 NAME
+
+MyApp::Schema::Result::Author
+
+=cut
+
+__PACKAGE__->table("author");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 first_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 last_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "first_name",
+  { data_type => "text", is_nullable => 1 },
+  "last_name",
+  { data_type => "text", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 book_authors
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::BookAuthor>
+
+=cut
+
+__PACKAGE__->has_many(
+  "book_authors",
+  "MyApp::Schema::Result::BookAuthor",
+  { "foreign.author_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 18:09:34
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ySFHFMfGRF6aPfob1GbJSQ
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+# many_to_many():
+#   args:
+#     1) Name of relationship bridge, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(books => 'book_authors', 'book');
+
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/Result/Book.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/Result/Book.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/Result/Book.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,88 @@
+package MyApp::Schema::Result::Book;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime");
+
+=head1 NAME
+
+MyApp::Schema::Result::Book
+
+=cut
+
+__PACKAGE__->table("book");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 title
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 rating
+
+  data_type: 'integer'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "title",
+  { data_type => "text", is_nullable => 1 },
+  "rating",
+  { data_type => "integer", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 book_authors
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::BookAuthor>
+
+=cut
+
+__PACKAGE__->has_many(
+  "book_authors",
+  "MyApp::Schema::Result::BookAuthor",
+  { "foreign.book_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 18:09:34
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:t/qfcGk8ZG6YnFIo+3dH3w
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+# many_to_many():
+#   args:
+#     1) Name of relationship bridge, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(authors => 'book_authors', 'author');
+
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,87 @@
+package MyApp::Schema::Result::BookAuthor;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime");
+
+=head1 NAME
+
+MyApp::Schema::Result::BookAuthor
+
+=cut
+
+__PACKAGE__->table("book_author");
+
+=head1 ACCESSORS
+
+=head2 book_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 author_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "book_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+  "author_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("book_id", "author_id");
+
+=head1 RELATIONS
+
+=head2 author
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Author>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "author",
+  "MyApp::Schema::Result::Author",
+  { id => "author_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+=head2 book
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Book>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "book",
+  "MyApp::Schema::Result::Book",
+  { id => "book_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 18:09:34
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Vm28PNOEBbQuIjWHvBdhCw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/Schema.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,19 @@
+package MyApp::Schema;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use Moose;
+use namespace::autoclean;
+extends 'DBIx::Class::Schema';
+
+__PACKAGE__->load_namespaces;
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 18:09:34
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:0eebDopG0RPImUoVlRBhTg
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/View
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/View/HTML.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/View/HTML.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp/View/HTML.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+package MyApp::View::HTML;
+
+use strict;
+use warnings;
+
+use base 'Catalyst::View::TT';
+
+__PACKAGE__->config(
+    # Change default TT extension
+    TEMPLATE_EXTENSION => '.tt2',
+    # Set the location for TT files
+    INCLUDE_PATH => [
+            MyApp->path_to( 'root', 'src' ),
+        ],
+    # Set to 1 for detailed timer stats in your HTML as comments
+    TIMER              => 0,
+    # This is your wrapper template located in the 'root/src'
+    WRAPPER => 'wrapper.tt2',
+);
+
+
+=head1 NAME
+
+MyApp::View::HTML - TT View for MyApp
+
+=head1 DESCRIPTION
+
+TT View for MyApp.
+
+=head1 SEE ALSO
+
+L<MyApp>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/lib/MyApp.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,84 @@
+package MyApp;
+use Moose;
+use namespace::autoclean;
+
+use Catalyst::Runtime 5.80;
+
+# Set flags and add plugins for the application.
+#
+# Note that ORDERING IS IMPORTANT here as plugins are initialized in order,
+# therefore you almost certainly want to keep ConfigLoader at the head of the
+# list if you're using it.
+#
+#         -Debug: activates the debug mode for very useful log messages
+#   ConfigLoader: will load the configuration from a Config::General file in the
+#                 application's home directory
+# Static::Simple: will serve static files from the application's root
+#                 directory
+
+use Catalyst qw/
+    -Debug
+    ConfigLoader
+    Static::Simple
+/;
+
+extends 'Catalyst';
+
+our $VERSION = '0.01';
+
+# Configure the application.
+#
+# Note that settings in myapp.conf (or other external
+# configuration file that you set up manually) take precedence
+# over this when using ConfigLoader. Thus configuration
+# details given here can function as a default configuration,
+# with an external configuration file acting as an override for
+# local deployment.
+
+__PACKAGE__->config(
+    name => 'MyApp',
+    # Disable deprecated behavior needed by old applications
+    disable_component_resolution_regex_fallback => 1,
+);
+__PACKAGE__->config(
+    # Configure the view
+    'View::HTML' => {
+        #Set the location for TT files
+        INCLUDE_PATH => [
+            __PACKAGE__->path_to( 'root', 'src' ),
+        ],
+    },
+);
+
+# Start the application
+__PACKAGE__->setup();
+
+
+=head1 NAME
+
+MyApp - Catalyst based application
+
+=head1 SYNOPSIS
+
+    script/myapp_server.pl
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 SEE ALSO
+
+L<MyApp::Controller::Root>, L<Catalyst>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp.conf
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp.conf	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp.conf	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,3 @@
+# rename this file to myapp.yml and put a ':' after 'name' if
+# you want to use YAML like in old versions of Catalyst
+name MyApp

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp.db
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp.db
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp.psgi
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp.psgi	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp.psgi	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+use MyApp;
+
+my $app = MyApp->apply_default_middlewares(MyApp->psgi_app);
+$app;
+

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp01.sql
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp01.sql	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/myapp01.sql	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+--
+-- Create a very simple database to hold book and author information
+--
+PRAGMA foreign_keys = ON;
+CREATE TABLE book (
+        id          INTEGER PRIMARY KEY,
+        title       TEXT ,
+        rating      INTEGER
+);
+-- 'book_author' is a many-to-many join table between books & authors
+CREATE TABLE book_author (
+        book_id     INTEGER REFERENCES book(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        author_id   INTEGER REFERENCES author(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        PRIMARY KEY (book_id, author_id)
+);
+CREATE TABLE author (
+        id          INTEGER PRIMARY KEY,
+        first_name  TEXT,
+        last_name   TEXT
+);
+---
+--- Load some sample data
+---
+INSERT INTO book VALUES (1, 'CCSP SNRS Exam Certification Guide', 5);
+INSERT INTO book VALUES (2, 'TCP/IP Illustrated, Volume 1', 5);
+INSERT INTO book VALUES (3, 'Internetworking with TCP/IP Vol.1', 4);
+INSERT INTO book VALUES (4, 'Perl Cookbook', 5);
+INSERT INTO book VALUES (5, 'Designing with Web Standards', 5);
+INSERT INTO author VALUES (1, 'Greg', 'Bastien');
+INSERT INTO author VALUES (2, 'Sara', 'Nasseh');
+INSERT INTO author VALUES (3, 'Christian', 'Degu');
+INSERT INTO author VALUES (4, 'Richard', 'Stevens');
+INSERT INTO author VALUES (5, 'Douglas', 'Comer');
+INSERT INTO author VALUES (6, 'Tom', 'Christiansen');
+INSERT INTO author VALUES (7, 'Nathan', 'Torkington');
+INSERT INTO author VALUES (8, 'Jeffrey', 'Zeldman');
+INSERT INTO book_author VALUES (1, 1);
+INSERT INTO book_author VALUES (1, 2);
+INSERT INTO book_author VALUES (1, 3);
+INSERT INTO book_author VALUES (2, 4);
+INSERT INTO book_author VALUES (3, 5);
+INSERT INTO book_author VALUES (4, 6);
+INSERT INTO book_author VALUES (4, 7);
+INSERT INTO book_author VALUES (5, 8);
+


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/favicon.ico
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/favicon.ico
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/src
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/src/books
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/src/books/list.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/root/src/books/list.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/root/src/books/list.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,38 @@
+[% # This is a TT comment. -%]
+
+[%- # Provide a title -%]
+[% META title = 'Book List' -%]
+
+[% # Note That the '-' at the beginning or end of TT code  -%]
+[% # "chomps" the whitespace/newline at that end of the    -%]
+[% # output (use View Source in browser to see the effect) -%]
+
+[% # Some basic HTML with a loop to display books -%]
+<table>
+<tr><th>Title</th><th>Rating</th><th>Author(s)</th></tr>
+[% # Display each book in a table row %]
+[% FOREACH book IN books -%]
+  <tr>
+    <td>[% book.title %]</td>
+    <td>[% book.rating %]</td>
+    <td>
+      [% # NOTE: See Chapter 4 for a better way to do this!                      -%]
+      [% # First initialize a TT variable to hold a list.  Then use a TT FOREACH -%]
+      [% # loop in 'side effect notation' to load just the last names of the     -%]
+      [% # authors into the list. Note that the 'push' TT vmethod doesn't return -%]
+      [% # a value, so nothing will be printed here.  But, if you have something -%]
+      [% # in TT that does return a value and you don't want it printed, you     -%]
+      [% # 1) assign it to a bogus value, or                                     -%]
+      [% # 2) use the CALL keyword to call it and discard the return value.      -%]
+      [% tt_authors = [ ];
+         tt_authors.push(author.last_name) FOREACH author = book.authors %]
+      [% # Now use a TT 'virtual method' to display the author count in parens   -%]
+      [% # Note the use of the TT filter "| html" to escape dangerous characters -%]
+      ([% tt_authors.size | html %])
+      [% # Use another TT vmethod to join & print the names & comma separators   -%]
+      [% tt_authors.join(', ') | html %]
+    </td>
+  </tr>
+[% END -%]
+</table>
+

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/src/wrapper.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/root/src/wrapper.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/root/src/wrapper.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" [%#
+    %]"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>[% template.title or "My Catalyst App!" %]</title>
+<link rel="stylesheet" href="[% c.uri_for('/static/css/main.css') %]" />
+</head>
+
+<body>
+<div id="outer">
+<div id="header">
+    [%# Your logo could go here -%]
+    <img src="[% c.uri_for('/static/images/btn_88x31_powered.png') %]" />
+    [%# Insert the page title -%]
+    <h1>[% template.title or site.title %]</h1>
+</div>
+
+<div id="bodyblock">
+<div id="menu">
+    Navigation:
+    <ul>
+        <li><a href="[% c.uri_for('/books/list') %]">Home</a></li>
+        <li><a href="[% c.uri_for('/')
+            %]" title="Catalyst Welcome Page">Welcome</a></li>
+    </ul>
+</div><!-- end menu -->
+
+<div id="content">
+    [%# Status and error messages %]
+    <span class="message">[% status_msg %]</span>
+    <span class="error">[% error_msg %]</span>
+    [%# This is where TT will stick all of your template's contents. -%]
+    [% content %]
+</div><!-- end content -->
+</div><!-- end bodyblock -->
+
+<div id="footer">Copyright (c) your name goes here</div>
+</div><!-- end outer -->
+
+</body>
+</html>
+


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/css
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/css/main.css
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/css/main.css	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/css/main.css	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,37 @@
+#header {
+    text-align: center;
+}
+#header h1 {
+    margin: 0;
+}
+#header img {
+    float: right;
+}
+#footer {
+    text-align: center;
+    font-style: italic;
+    padding-top: 20px;
+}
+#menu {
+    font-weight: bold;
+    background-color: #ddd;
+}
+#menu ul {
+    list-style: none;
+    float: left;
+    margin: 0;
+    padding: 0 0 50% 5px;
+    font-weight: normal;
+    background-color: #ddd;
+    width: 100px;
+}
+#content {
+    margin-left: 120px;
+}
+.message {
+    color: #390;
+}
+.error {
+    color: #f00;
+}
+


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_120x50_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_120x50_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_120x50_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_120x50_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_120x50_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_120x50_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_120x50_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_120x50_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_88x31_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_88x31_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_88x31_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_88x31_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_88x31_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_88x31_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_88x31_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/btn_88x31_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/catalyst_logo.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/root/static/images/catalyst_logo.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/script
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_cgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_cgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_cgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'CGI');
+
+1;
+
+=head1 NAME
+
+myapp_cgi.pl - Catalyst CGI
+
+=head1 SYNOPSIS
+
+See L<Catalyst::Manual>
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as a cgi script.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_create.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_create.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_create.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Create');
+
+1;
+
+=head1 NAME
+
+myapp_create.pl - Create a new Catalyst Component
+
+=head1 SYNOPSIS
+
+myapp_create.pl [options] model|view|controller name [helper] [options]
+
+ Options:
+   --force        don't create a .new file where a file to be created exists
+   --mechanize    use Test::WWW::Mechanize::Catalyst for tests if available
+   --help         display this help and exits
+
+ Examples:
+   myapp_create.pl controller My::Controller
+   myapp_create.pl --mechanize controller My::Controller
+   myapp_create.pl view My::View
+   myapp_create.pl view HTML TT
+   myapp_create.pl model My::Model
+   myapp_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\
+   dbi:SQLite:/tmp/my.db
+   myapp_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static\
+   [Loader opts like db_schema, naming] dbi:Pg:dbname=foo root 4321
+   [connect_info opts like quote_char, name_sep]
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+   perldoc Catalyst::Helper::Model::DBIC::Schema
+   perldoc Catalyst::Model::DBIC::Schema
+   perldoc Catalyst::View::TT
+
+=head1 DESCRIPTION
+
+Create a new Catalyst Component.
+
+Existing component files are not overwritten.  If any of the component files
+to be created already exist the file will be written with a '.new' suffix.
+This behavior can be suppressed with the C<-force> option.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_fastcgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_fastcgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_fastcgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,48 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'FastCGI');
+
+1;
+
+=head1 NAME
+
+myapp_fastcgi.pl - Catalyst FastCGI
+
+=head1 SYNOPSIS
+
+myapp_fastcgi.pl [options]
+
+ Options:
+   -? -help      display this help and exits
+   -l --listen   Socket path to listen on
+                 (defaults to standard input)
+                 can be HOST:PORT, :PORT or a
+                 filesystem path
+   -n --nproc    specify number of processes to keep
+                 to serve requests (defaults to 1,
+                 requires -listen)
+   -p --pidfile  specify filename for pid file
+                 (requires -listen)
+   -d --daemon   daemonize (requires -listen)
+   -M --manager  specify alternate process manager
+                 (FCGI::ProcManager sub-class)
+                 or empty string to disable
+   -e --keeperr  send error messages to STDOUT, not
+                 to the webserver
+   --proc_title  Set the process title (is possible)
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as fastcgi.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_server.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_server.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_server.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+BEGIN {
+    $ENV{CATALYST_SCRIPT_GEN} = 40;
+}
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Server');
+
+1;
+
+=head1 NAME
+
+myapp_server.pl - Catalyst Test Server
+
+=head1 SYNOPSIS
+
+myapp_server.pl [options]
+
+   -d --debug           force debug mode
+   -f --fork            handle each request in a new process
+                        (defaults to false)
+   -? --help            display this help and exits
+   -h --host            host (defaults to all)
+   -p --port            port (defaults to 3000)
+   -k --keepalive       enable keep-alive connections
+   -r --restart         restart when files get modified
+                        (defaults to false)
+   -rd --restart_delay  delay between file checks
+                        (ignored if you have Linux::Inotify2 installed)
+   -rr --restart_regex  regex match files that trigger
+                        a restart when modified
+                        (defaults to '\.yml$|\.yaml$|\.conf|\.pm$')
+   --restart_directory  the directory to search for
+                        modified files, can be set multiple times
+                        (defaults to '[SCRIPT_DIR]/..')
+   --follow_symlinks    follow symlinks in search directories
+                        (defaults to false. this is a no-op on Win32)
+   --background         run the process in the background
+   --pidfile            specify filename for pid file
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst Testserver for this application.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_test.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_test.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/script/myapp_test.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,40 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Test');
+
+1;
+
+=head1 NAME
+
+myapp_test.pl - Catalyst Test
+
+=head1 SYNOPSIS
+
+myapp_test.pl [options] uri
+
+ Options:
+   --help    display this help and exits
+
+ Examples:
+   myapp_test.pl http://localhost/some_action
+   myapp_test.pl /some_action
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst action from the command line.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut


Property changes on: trunk/examples/Tutorial/Final/Chapter03/MyApp/t
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/t/01app.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/t/01app.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/t/01app.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+use Catalyst::Test 'MyApp';
+
+ok( request('/')->is_success, 'Request should succeed' );
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/t/02pod.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/t/02pod.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/t/02pod.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+eval "use Test::Pod 1.14";
+plan skip_all => 'Test::Pod 1.14 required' if $@;
+
+all_pod_files_ok();

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/t/03podcoverage.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/t/03podcoverage.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/t/03podcoverage.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,14 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@;
+
+eval "use Pod::Coverage 0.20";
+plan skip_all => 'Pod::Coverage 0.20 required' if $@;
+
+all_pod_coverage_ok();

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/t/controller_Books.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/t/controller_Books.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/t/controller_Books.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Books;
+
+ok( request('/books')->is_success, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/t/model_DB.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/t/model_DB.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/t/model_DB.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+BEGIN { use_ok 'MyApp::Model::DB' }
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter03/MyApp/t/view_HTML.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter03/MyApp/t/view_HTML.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter03/MyApp/t/view_HTML.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+BEGIN { use_ok 'MyApp::View::HTML' }
+
+done_testing();


Property changes on: trunk/examples/Tutorial/Final/Chapter04
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/Changes
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/Changes	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/Changes	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,4 @@
+This file documents the revision history for Perl extension MyApp.
+
+0.01  2011-08-30 17:51:40
+        - initial revision, generated by Catalyst

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/Makefile.PL
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/Makefile.PL	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/Makefile.PL	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,25 @@
+#!/usr/bin/env perl
+# IMPORTANT: if you delete this file your app will not work as
+# expected.  You have been warned.
+use inc::Module::Install;
+use Module::Install::Catalyst; # Complain loudly if you don't have
+                               # Catalyst::Devel installed or haven't said
+                               # 'make dist' to create a standalone tarball.
+
+name 'MyApp';
+all_from 'lib/MyApp.pm';
+
+requires 'Catalyst::Runtime' => '5.90002';
+requires 'Catalyst::Plugin::ConfigLoader';
+requires 'Catalyst::Plugin::Static::Simple';
+requires 'Catalyst::Action::RenderView';
+requires 'Moose';
+requires 'namespace::autoclean';
+requires 'Config::General'; # This should reflect the config file format you've chosen
+                 # See Catalyst::Plugin::ConfigLoader for supported formats
+test_requires 'Test::More' => '0.88';
+catalyst;
+
+install_script glob('script/*.pl');
+auto_install;
+WriteAll;

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/README
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/README	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/README	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1 @@
+Run script/myapp_server.pl to test the application.


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Controller
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Controller/Books.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Controller/Books.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Controller/Books.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,255 @@
+package MyApp::Controller::Books;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Books - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    $c->response->body('Matched MyApp::Controller::Books in Books.');
+}
+
+
+=head2 base
+
+Can place common logic to start chained dispatch here
+
+=cut
+
+sub base :Chained('/') :PathPart('books') :CaptureArgs(0) {
+    my ($self, $c) = @_;
+
+    # Store the ResultSet in stash so it's available for other methods
+    $c->stash(resultset => $c->model('DB::Book'));
+
+    # Print a message to the debug log
+    $c->log->debug('*** INSIDE BASE METHOD ***');
+}
+
+
+=head2 list
+
+Fetch all book objects and pass to books/list.tt2 in stash to be displayed
+
+=cut
+
+sub list :Local {
+    # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst
+    # 'Context' that's used to 'glue together' the various components
+    # that make up the application
+    my ($self, $c) = @_;
+
+    # Retrieve all of the book records as book model objects and store
+    # in the stash where they can be accessed by the TT template
+    $c->stash(books => [$c->model('DB::Book')->all]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head2 url_create
+
+Create a book with the supplied title, rating, and author
+
+=cut
+
+sub url_create :Chained('/') :PathPart('books/url_create') :Args(3) {
+    # In addition to self & context, get the title, rating, &
+    # author_id args from the URL.  Note that Catalyst automatically
+    # puts the first 3 arguments worth of extra information after the
+    # "/<controller_name>/<action_name/" into @_ because we specified
+    # "Args(3)".  The args are separated  by the '/' char on the URL.
+    my ($self, $c, $title, $rating, $author_id) = @_;
+
+    # Call create() on the book model object. Pass the table
+    # columns/field values we want to set as hash values
+    my $book = $c->model('DB::Book')->create({
+            title  => $title,
+            rating => $rating
+        });
+
+    # Add a record to the join table for this book, mapping to
+    # appropriate author
+    $book->add_to_book_authors({author_id => $author_id});
+    # Note: Above is a shortcut for this:
+    # $book->create_related('book_authors', {author_id => $author_id});
+
+    # Assign the Book object to the stash for display and set template
+    $c->stash(book     => $book,
+              template => 'books/create_done.tt2');
+
+    # Disable caching for this page
+    $c->response->header('Cache-Control' => 'no-cache');
+}
+
+
+=head2 form_create
+
+Display form to collect information for book to create
+
+=cut
+
+sub form_create :Chained('base') :PathPart('form_create') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Set the TT template to use
+    $c->stash(template => 'books/form_create.tt2');
+}
+
+
+=head2 form_create_do
+
+Take information from form and add to database
+
+=cut
+
+sub form_create_do :Chained('base') :PathPart('form_create_do') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Retrieve the values from the form
+    my $title     = $c->request->params->{title}     || 'N/A';
+    my $rating    = $c->request->params->{rating}    || 'N/A';
+    my $author_id = $c->request->params->{author_id} || '1';
+
+    # Create the book
+    my $book = $c->model('DB::Book')->create({
+            title   => $title,
+            rating  => $rating,
+        });
+    # Handle relationship with author
+    $book->add_to_book_authors({author_id => $author_id});
+    # Note: Above is a shortcut for this:
+    # $book->create_related('book_authors', {author_id => $author_id});
+
+    # Store new model object in stash and set template
+    $c->stash(book     => $book,
+              template => 'books/create_done.tt2');
+}
+
+
+=head2 object
+
+Fetch the specified book object based on the book ID and store
+it in the stash
+
+=cut
+
+sub object :Chained('base') :PathPart('id') :CaptureArgs(1) {
+    # $id = primary key of book to delete
+    my ($self, $c, $id) = @_;
+
+    # Find the book object and store it in the stash
+    $c->stash(object => $c->stash->{resultset}->find($id));
+
+    # Make sure the lookup was successful.  You would probably
+    # want to do something like this in a real app:
+    #   $c->detach('/error_404') if !$c->stash->{object};
+    die "Book $id not found!" if !$c->stash->{object};
+
+    # Print a message to the debug log
+    $c->log->debug("*** INSIDE OBJECT METHOD for obj id=$id ***");
+}
+
+
+=head2 delete
+
+Delete a book
+
+=cut
+
+sub delete :Chained('object') :PathPart('delete') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Use the book object saved by 'object' and delete it along
+    # with related 'book_author' entries
+    $c->stash->{object}->delete;
+
+    # Redirect the user back to the list page with status msg as an arg
+    $c->response->redirect($c->uri_for($self->action_for('list'),
+        {status_msg => "Book deleted."}));
+}
+
+
+=head2 list_recent
+
+List recently created books
+
+=cut
+
+sub list_recent :Chained('base') :PathPart('list_recent') :Args(1) {
+    my ($self, $c, $mins) = @_;
+
+    # Retrieve all of the book records as book model objects and store in the
+    # stash where they can be accessed by the TT template, but only
+    # retrieve books created within the last $min number of minutes
+    $c->stash(books => [$c->model('DB::Book')
+                            ->created_after(DateTime->now->subtract(minutes => $mins))]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head2 list_recent_tcp
+
+List recently created books
+
+=cut
+
+sub list_recent_tcp :Chained('base') :PathPart('list_recent_tcp') :Args(1) {
+    my ($self, $c, $mins) = @_;
+
+    # Retrieve all of the book records as book model objects and store in the
+    # stash where they can be accessed by the TT template, but only
+    # retrieve books created within the last $min number of minutes
+    # AND that have 'TCP' in the title
+    $c->stash(books => [
+            $c->model('DB::Book')
+                ->created_after(DateTime->now->subtract(minutes => $mins))
+                ->title_like('TCP')
+        ]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Controller/Root.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Controller/Root.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Controller/Root.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,69 @@
+package MyApp::Controller::Root;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller' }
+
+#
+# Sets the actions in this controller to be registered with no prefix
+# so they function identically to actions created in MyApp.pm
+#
+__PACKAGE__->config(namespace => '');
+
+=head1 NAME
+
+MyApp::Controller::Root - Root Controller for MyApp
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 METHODS
+
+=head2 index
+
+The root page (/)
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    # Hello World
+    $c->response->body( $c->welcome_message );
+}
+
+=head2 default
+
+Standard 404 error page
+
+=cut
+
+sub default :Path {
+    my ( $self, $c ) = @_;
+    $c->response->body( 'Page not found' );
+    $c->response->status(404);
+}
+
+=head2 end
+
+Attempt to render a view, if needed.
+
+=cut
+
+sub end : ActionClass('RenderView') {}
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Model
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Model/DB.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Model/DB.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Model/DB.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,44 @@
+package MyApp::Model::DB;
+
+use strict;
+use base 'Catalyst::Model::DBIC::Schema';
+
+__PACKAGE__->config(
+    schema_class => 'MyApp::Schema',
+    
+    connect_info => {
+        dsn => 'dbi:SQLite:myapp.db',
+        user => '',
+        password => '',
+        on_connect_do => q{PRAGMA foreign_keys = ON},
+    }
+);
+
+=head1 NAME
+
+MyApp::Model::DB - Catalyst DBIC Schema Model
+
+=head1 SYNOPSIS
+
+See L<MyApp>
+
+=head1 DESCRIPTION
+
+L<Catalyst::Model::DBIC::Schema> Model using schema L<MyApp::Schema>
+
+=head1 GENERATED BY
+
+Catalyst::Helper::Model::DBIC::Schema - 0.54
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/Result
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/Result/Author.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/Result/Author.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/Result/Author.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,98 @@
+package MyApp::Schema::Result::Author;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp");
+
+=head1 NAME
+
+MyApp::Schema::Result::Author
+
+=cut
+
+__PACKAGE__->table("author");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 first_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 last_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "first_name",
+  { data_type => "text", is_nullable => 1 },
+  "last_name",
+  { data_type => "text", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 book_authors
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::BookAuthor>
+
+=cut
+
+__PACKAGE__->has_many(
+  "book_authors",
+  "MyApp::Schema::Result::BookAuthor",
+  { "foreign.author_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 18:41:44
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CfrURMtQbXRgE6iR63rkNQ
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+# many_to_many():
+#   args:
+#     1) Name of relationship bridge, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(books => 'book_authors', 'book');
+
+
+#
+# Row-level helper methods
+#
+sub full_name {
+    my ($self) = @_;
+
+    return $self->first_name . ' ' . $self->last_name;
+}
+
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/Result/Book.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/Result/Book.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/Result/Book.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,147 @@
+package MyApp::Schema::Result::Book;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp");
+
+=head1 NAME
+
+MyApp::Schema::Result::Book
+
+=cut
+
+__PACKAGE__->table("book");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 title
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 rating
+
+  data_type: 'integer'
+  is_nullable: 1
+
+=head2 created
+
+  data_type: 'timestamp'
+  is_nullable: 1
+
+=head2 updated
+
+  data_type: 'timestamp'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "title",
+  { data_type => "text", is_nullable => 1 },
+  "rating",
+  { data_type => "integer", is_nullable => 1 },
+  "created",
+  { data_type => "timestamp", is_nullable => 1 },
+  "updated",
+  { data_type => "timestamp", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 book_authors
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::BookAuthor>
+
+=cut
+
+__PACKAGE__->has_many(
+  "book_authors",
+  "MyApp::Schema::Result::BookAuthor",
+  { "foreign.book_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 18:41:44
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Bk6EjZUTCBoSZcmj+LhN4g
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+# many_to_many():
+#   args:
+#     1) Name of relationship bridge, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(authors => 'book_authors', 'author');
+
+#
+# Enable automatic date handling
+#
+__PACKAGE__->add_columns(
+    "created",
+    { data_type => 'timestamp', set_on_create => 1 },
+    "updated",
+    { data_type => 'timestamp', set_on_create => 1, set_on_update => 1 },
+);
+
+
+=head2 author_count
+
+Return the number of authors for the current book
+
+=cut
+
+sub author_count {
+    my ($self) = @_;
+
+    # Use the 'many_to_many' relationship to fetch all of the authors for the current
+    # and the 'count' method in DBIx::Class::ResultSet to get a SQL COUNT
+    return $self->authors->count;
+}
+
+
+=head2 author_list
+
+Return a comma-separated list of authors for the current book
+
+=cut
+
+sub author_list {
+    my ($self) = @_;
+
+    # Loop through all authors for the current book, calling all the 'full_name'
+    # Result Class method for each
+    my @names;
+    foreach my $author ($self->authors) {
+        push(@names, $author->full_name);
+    }
+
+    return join(', ', @names);
+}
+
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,87 @@
+package MyApp::Schema::Result::BookAuthor;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp");
+
+=head1 NAME
+
+MyApp::Schema::Result::BookAuthor
+
+=cut
+
+__PACKAGE__->table("book_author");
+
+=head1 ACCESSORS
+
+=head2 book_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 author_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "book_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+  "author_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("book_id", "author_id");
+
+=head1 RELATIONS
+
+=head2 author
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Author>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "author",
+  "MyApp::Schema::Result::Author",
+  { id => "author_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+=head2 book
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Book>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "book",
+  "MyApp::Schema::Result::Book",
+  { id => "book_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 18:41:44
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:a5F445yA225n/hmYJi6tig
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/ResultSet
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/ResultSet/Book.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/ResultSet/Book.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema/ResultSet/Book.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,41 @@
+package MyApp::Schema::ResultSet::Book;
+
+use strict;
+use warnings;
+use base 'DBIx::Class::ResultSet';
+
+=head2 created_after
+
+A predefined search for recently added books
+
+=cut
+
+sub created_after {
+    my ($self, $datetime) = @_;
+
+    my $date_str = $self->result_source->schema->storage
+                          ->datetime_parser->format_datetime($datetime);
+
+    return $self->search({
+        created => { '>' => $date_str }
+    });
+}
+
+
+=head2 title_like
+
+A predefined search for books with a 'LIKE' search in the string
+
+=cut
+
+sub title_like {
+    my ($self, $title_str) = @_;
+
+    return $self->search({
+        title => { 'like' => "%$title_str%" }
+    });
+}
+
+
+1;
+

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/Schema.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,19 @@
+package MyApp::Schema;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use Moose;
+use namespace::autoclean;
+extends 'DBIx::Class::Schema';
+
+__PACKAGE__->load_namespaces;
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 18:09:34
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:0eebDopG0RPImUoVlRBhTg
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/View
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/View/HTML.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/View/HTML.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp/View/HTML.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+package MyApp::View::HTML;
+
+use strict;
+use warnings;
+
+use base 'Catalyst::View::TT';
+
+__PACKAGE__->config(
+    # Change default TT extension
+    TEMPLATE_EXTENSION => '.tt2',
+    # Set the location for TT files
+    INCLUDE_PATH => [
+            MyApp->path_to( 'root', 'src' ),
+        ],
+    # Set to 1 for detailed timer stats in your HTML as comments
+    TIMER              => 0,
+    # This is your wrapper template located in the 'root/src'
+    WRAPPER => 'wrapper.tt2',
+);
+
+
+=head1 NAME
+
+MyApp::View::HTML - TT View for MyApp
+
+=head1 DESCRIPTION
+
+TT View for MyApp.
+
+=head1 SEE ALSO
+
+L<MyApp>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/lib/MyApp.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,84 @@
+package MyApp;
+use Moose;
+use namespace::autoclean;
+
+use Catalyst::Runtime 5.80;
+
+# Set flags and add plugins for the application.
+#
+# Note that ORDERING IS IMPORTANT here as plugins are initialized in order,
+# therefore you almost certainly want to keep ConfigLoader at the head of the
+# list if you're using it.
+#
+#         -Debug: activates the debug mode for very useful log messages
+#   ConfigLoader: will load the configuration from a Config::General file in the
+#                 application's home directory
+# Static::Simple: will serve static files from the application's root
+#                 directory
+
+use Catalyst qw/
+    -Debug
+    ConfigLoader
+    Static::Simple
+/;
+
+extends 'Catalyst';
+
+our $VERSION = '0.01';
+
+# Configure the application.
+#
+# Note that settings in myapp.conf (or other external
+# configuration file that you set up manually) take precedence
+# over this when using ConfigLoader. Thus configuration
+# details given here can function as a default configuration,
+# with an external configuration file acting as an override for
+# local deployment.
+
+__PACKAGE__->config(
+    name => 'MyApp',
+    # Disable deprecated behavior needed by old applications
+    disable_component_resolution_regex_fallback => 1,
+);
+__PACKAGE__->config(
+    # Configure the view
+    'View::HTML' => {
+        #Set the location for TT files
+        INCLUDE_PATH => [
+            __PACKAGE__->path_to( 'root', 'src' ),
+        ],
+    },
+);
+
+# Start the application
+__PACKAGE__->setup();
+
+
+=head1 NAME
+
+MyApp - Catalyst based application
+
+=head1 SYNOPSIS
+
+    script/myapp_server.pl
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 SEE ALSO
+
+L<MyApp::Controller::Root>, L<Catalyst>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp.conf
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp.conf	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp.conf	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,3 @@
+# rename this file to myapp.yml and put a ':' after 'name' if
+# you want to use YAML like in old versions of Catalyst
+name MyApp

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp.db
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp.db
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp.psgi
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp.psgi	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp.psgi	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+use MyApp;
+
+my $app = MyApp->apply_default_middlewares(MyApp->psgi_app);
+$app;
+

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp01.sql
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp01.sql	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/myapp01.sql	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+--
+-- Create a very simple database to hold book and author information
+--
+PRAGMA foreign_keys = ON;
+CREATE TABLE book (
+        id          INTEGER PRIMARY KEY,
+        title       TEXT ,
+        rating      INTEGER
+);
+-- 'book_author' is a many-to-many join table between books & authors
+CREATE TABLE book_author (
+        book_id     INTEGER REFERENCES book(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        author_id   INTEGER REFERENCES author(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        PRIMARY KEY (book_id, author_id)
+);
+CREATE TABLE author (
+        id          INTEGER PRIMARY KEY,
+        first_name  TEXT,
+        last_name   TEXT
+);
+---
+--- Load some sample data
+---
+INSERT INTO book VALUES (1, 'CCSP SNRS Exam Certification Guide', 5);
+INSERT INTO book VALUES (2, 'TCP/IP Illustrated, Volume 1', 5);
+INSERT INTO book VALUES (3, 'Internetworking with TCP/IP Vol.1', 4);
+INSERT INTO book VALUES (4, 'Perl Cookbook', 5);
+INSERT INTO book VALUES (5, 'Designing with Web Standards', 5);
+INSERT INTO author VALUES (1, 'Greg', 'Bastien');
+INSERT INTO author VALUES (2, 'Sara', 'Nasseh');
+INSERT INTO author VALUES (3, 'Christian', 'Degu');
+INSERT INTO author VALUES (4, 'Richard', 'Stevens');
+INSERT INTO author VALUES (5, 'Douglas', 'Comer');
+INSERT INTO author VALUES (6, 'Tom', 'Christiansen');
+INSERT INTO author VALUES (7, 'Nathan', 'Torkington');
+INSERT INTO author VALUES (8, 'Jeffrey', 'Zeldman');
+INSERT INTO book_author VALUES (1, 1);
+INSERT INTO book_author VALUES (1, 2);
+INSERT INTO book_author VALUES (1, 3);
+INSERT INTO book_author VALUES (2, 4);
+INSERT INTO book_author VALUES (3, 5);
+INSERT INTO book_author VALUES (4, 6);
+INSERT INTO book_author VALUES (4, 7);
+INSERT INTO book_author VALUES (5, 8);
+


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/favicon.ico
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/favicon.ico
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/books
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/books/create_done.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/books/create_done.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/books/create_done.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,32 @@
+[% # Use the TT Dumper plugin to Data::Dumper variables to the browser   -%]
+[% # Not a good idea for production use, though. :-)  'Indent=1' is      -%]
+[% # optional, but prevents "massive indenting" of deeply nested objects -%]
+[% USE Dumper(Indent=1) -%]
+
+[% # Set the page title.  META can 'go back' and set values in templates -%]
+[% # that have been processed 'before' this template (here it's updating -%]
+[% # the title in the root/src/wrapper.tt2 wrapper template).  Note that -%]
+[% # META only works on simple/static strings (i.e. there is no variable -%]
+[% # interpolation -- if you need dynamic/interpolated content in your   -%]
+[% # title, set "$c->stash(title => $something)" in the controller).     -%]
+[% META title = 'Book Created' %]
+
+[% # Output information about the record that was added.  First title.   -%]
+<p>Added book '[% book.title %]'
+
+[% # Then, output the last name of the first author -%]
+by '[% book.authors.first.last_name %]'
+
+[% # Then, output the rating for the book that was added -%]
+with a rating of [% book.rating %].</p>
+
+[% # Provide a link back to the list page.  'c.uri_for' builds -%]
+[% # a full URI; e.g., 'http://localhost:3000/books/list'      -%]
+<p><a href="[% c.uri_for('/books/list') %]">Return to list</a></p>
+
+[% # Try out the TT Dumper (for development only!) -%]
+<pre>
+Dump of the 'book' variable:
+[% Dumper.dump(book) %]
+</pre>
+

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/books/form_create.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/books/form_create.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/books/form_create.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,11 @@
+[% META title = 'Manual Form Book Create' -%]
+
+<form method="post" action="[% c.uri_for('form_create_do') %]">
+<table>
+  <tr><td>Title:</td><td><input type="text" name="title"></td></tr>
+  <tr><td>Rating:</td><td><input type="text" name="rating"></td></tr>
+  <tr><td>Author ID:</td><td><input type="text" name="author_id"></td></tr>
+</table>
+<input type="submit" name="Submit" value="Submit">
+</form>
+

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/books/list.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/books/list.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/books/list.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,30 @@
+[% # This is a TT comment. -%]
+
+[%- # Provide a title -%]
+[% META title = 'Book List' -%]
+
+[% # Note That the '-' at the beginning or end of TT code  -%]
+[% # "chomps" the whitespace/newline at that end of the    -%]
+[% # output (use View Source in browser to see the effect) -%]
+
+[% # Some basic HTML with a loop to display books -%]
+<table>
+<tr><th>Title</th><th>Rating</th><th>Author(s)</th><th>Links</th></tr>
+[% # Display each book in a table row %]
+[% FOREACH book IN books -%]
+  <tr>
+    <td>[% book.title %]</td>
+    <td>[% book.rating %]</td>
+    <td>
+      [% # Print count and author list using Result Class methods -%]
+      ([% book.author_count | html %]) [% book.author_list | html %]
+    </td>
+    <td>
+      [% # Add a link to delete a book %]
+      <a href="[%
+        c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
+    </td>
+  </tr>
+[% END -%]
+</table>
+

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/wrapper.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/wrapper.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/root/src/wrapper.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" [%#
+    %]"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>[% template.title or "My Catalyst App!" %]</title>
+<link rel="stylesheet" href="[% c.uri_for('/static/css/main.css') %]" />
+</head>
+
+<body>
+<div id="outer">
+<div id="header">
+    [%# Your logo could go here -%]
+    <img src="[% c.uri_for('/static/images/btn_88x31_powered.png') %]" />
+    [%# Insert the page title -%]
+    <h1>[% template.title or site.title %]</h1>
+</div>
+
+<div id="bodyblock">
+<div id="menu">
+    Navigation:
+    <ul>
+        <li><a href="[% c.uri_for('/books/list') %]">Home</a></li>
+        <li><a href="[% c.uri_for('/')
+            %]" title="Catalyst Welcome Page">Welcome</a></li>
+    </ul>
+</div><!-- end menu -->
+
+<div id="content">
+    [%# Status and error messages %]
+    <span class="message">[%
+        status_msg || c.request.params.status_msg | html %]</span>
+    <span class="error">[% error_msg %]</span>
+    [%# This is where TT will stick all of your template's contents. -%]
+    [% content %]
+</div><!-- end content -->
+</div><!-- end bodyblock -->
+
+<div id="footer">Copyright (c) your name goes here</div>
+</div><!-- end outer -->
+
+</body>
+</html>
+


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/css
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/css/main.css
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/css/main.css	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/css/main.css	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,37 @@
+#header {
+    text-align: center;
+}
+#header h1 {
+    margin: 0;
+}
+#header img {
+    float: right;
+}
+#footer {
+    text-align: center;
+    font-style: italic;
+    padding-top: 20px;
+}
+#menu {
+    font-weight: bold;
+    background-color: #ddd;
+}
+#menu ul {
+    list-style: none;
+    float: left;
+    margin: 0;
+    padding: 0 0 50% 5px;
+    font-weight: normal;
+    background-color: #ddd;
+    width: 100px;
+}
+#content {
+    margin-left: 120px;
+}
+.message {
+    color: #390;
+}
+.error {
+    color: #f00;
+}
+


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_120x50_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_120x50_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_120x50_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_120x50_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_120x50_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_120x50_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_120x50_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_120x50_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_88x31_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_88x31_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_88x31_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_88x31_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_88x31_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_88x31_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_88x31_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/btn_88x31_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/catalyst_logo.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/root/static/images/catalyst_logo.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/script
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_cgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_cgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_cgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'CGI');
+
+1;
+
+=head1 NAME
+
+myapp_cgi.pl - Catalyst CGI
+
+=head1 SYNOPSIS
+
+See L<Catalyst::Manual>
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as a cgi script.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_create.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_create.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_create.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Create');
+
+1;
+
+=head1 NAME
+
+myapp_create.pl - Create a new Catalyst Component
+
+=head1 SYNOPSIS
+
+myapp_create.pl [options] model|view|controller name [helper] [options]
+
+ Options:
+   --force        don't create a .new file where a file to be created exists
+   --mechanize    use Test::WWW::Mechanize::Catalyst for tests if available
+   --help         display this help and exits
+
+ Examples:
+   myapp_create.pl controller My::Controller
+   myapp_create.pl --mechanize controller My::Controller
+   myapp_create.pl view My::View
+   myapp_create.pl view HTML TT
+   myapp_create.pl model My::Model
+   myapp_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\
+   dbi:SQLite:/tmp/my.db
+   myapp_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static\
+   [Loader opts like db_schema, naming] dbi:Pg:dbname=foo root 4321
+   [connect_info opts like quote_char, name_sep]
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+   perldoc Catalyst::Helper::Model::DBIC::Schema
+   perldoc Catalyst::Model::DBIC::Schema
+   perldoc Catalyst::View::TT
+
+=head1 DESCRIPTION
+
+Create a new Catalyst Component.
+
+Existing component files are not overwritten.  If any of the component files
+to be created already exist the file will be written with a '.new' suffix.
+This behavior can be suppressed with the C<-force> option.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_fastcgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_fastcgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_fastcgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,48 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'FastCGI');
+
+1;
+
+=head1 NAME
+
+myapp_fastcgi.pl - Catalyst FastCGI
+
+=head1 SYNOPSIS
+
+myapp_fastcgi.pl [options]
+
+ Options:
+   -? -help      display this help and exits
+   -l --listen   Socket path to listen on
+                 (defaults to standard input)
+                 can be HOST:PORT, :PORT or a
+                 filesystem path
+   -n --nproc    specify number of processes to keep
+                 to serve requests (defaults to 1,
+                 requires -listen)
+   -p --pidfile  specify filename for pid file
+                 (requires -listen)
+   -d --daemon   daemonize (requires -listen)
+   -M --manager  specify alternate process manager
+                 (FCGI::ProcManager sub-class)
+                 or empty string to disable
+   -e --keeperr  send error messages to STDOUT, not
+                 to the webserver
+   --proc_title  Set the process title (is possible)
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as fastcgi.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_server.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_server.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_server.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+BEGIN {
+    $ENV{CATALYST_SCRIPT_GEN} = 40;
+}
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Server');
+
+1;
+
+=head1 NAME
+
+myapp_server.pl - Catalyst Test Server
+
+=head1 SYNOPSIS
+
+myapp_server.pl [options]
+
+   -d --debug           force debug mode
+   -f --fork            handle each request in a new process
+                        (defaults to false)
+   -? --help            display this help and exits
+   -h --host            host (defaults to all)
+   -p --port            port (defaults to 3000)
+   -k --keepalive       enable keep-alive connections
+   -r --restart         restart when files get modified
+                        (defaults to false)
+   -rd --restart_delay  delay between file checks
+                        (ignored if you have Linux::Inotify2 installed)
+   -rr --restart_regex  regex match files that trigger
+                        a restart when modified
+                        (defaults to '\.yml$|\.yaml$|\.conf|\.pm$')
+   --restart_directory  the directory to search for
+                        modified files, can be set multiple times
+                        (defaults to '[SCRIPT_DIR]/..')
+   --follow_symlinks    follow symlinks in search directories
+                        (defaults to false. this is a no-op on Win32)
+   --background         run the process in the background
+   --pidfile            specify filename for pid file
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst Testserver for this application.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_test.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_test.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/script/myapp_test.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,40 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Test');
+
+1;
+
+=head1 NAME
+
+myapp_test.pl - Catalyst Test
+
+=head1 SYNOPSIS
+
+myapp_test.pl [options] uri
+
+ Options:
+   --help    display this help and exits
+
+ Examples:
+   myapp_test.pl http://localhost/some_action
+   myapp_test.pl /some_action
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst action from the command line.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut


Property changes on: trunk/examples/Tutorial/Final/Chapter04/MyApp/t
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/t/01app.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/t/01app.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/t/01app.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+use Catalyst::Test 'MyApp';
+
+ok( request('/')->is_success, 'Request should succeed' );
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/t/02pod.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/t/02pod.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/t/02pod.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+eval "use Test::Pod 1.14";
+plan skip_all => 'Test::Pod 1.14 required' if $@;
+
+all_pod_files_ok();

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/t/03podcoverage.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/t/03podcoverage.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/t/03podcoverage.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,14 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@;
+
+eval "use Pod::Coverage 0.20";
+plan skip_all => 'Pod::Coverage 0.20 required' if $@;
+
+all_pod_coverage_ok();

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/t/controller_Books.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/t/controller_Books.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/t/controller_Books.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Books;
+
+ok( request('/books')->is_success, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/t/model_DB.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/t/model_DB.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/t/model_DB.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+BEGIN { use_ok 'MyApp::Model::DB' }
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter04/MyApp/t/view_HTML.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter04/MyApp/t/view_HTML.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter04/MyApp/t/view_HTML.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+BEGIN { use_ok 'MyApp::View::HTML' }
+
+done_testing();


Property changes on: trunk/examples/Tutorial/Final/Chapter05
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/Changes
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/Changes	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/Changes	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,4 @@
+This file documents the revision history for Perl extension MyApp.
+
+0.01  2011-08-30 17:51:40
+        - initial revision, generated by Catalyst

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/Makefile.PL
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/Makefile.PL	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/Makefile.PL	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+# IMPORTANT: if you delete this file your app will not work as
+# expected.  You have been warned.
+use inc::Module::Install;
+use Module::Install::Catalyst; # Complain loudly if you don't have
+                               # Catalyst::Devel installed or haven't said
+                               # 'make dist' to create a standalone tarball.
+
+name 'MyApp';
+all_from 'lib/MyApp.pm';
+
+requires 'Catalyst::Runtime' => '5.90002';
+requires 'Catalyst::Plugin::ConfigLoader';
+requires 'Catalyst::Plugin::Static::Simple';
+requires 'Catalyst::Action::RenderView';
+requires 'Moose';
+requires 'namespace::autoclean';
+requires 'Config::General'; # This should reflect the config file format you've chosen
+                 # See Catalyst::Plugin::ConfigLoader for supported formats
+requires 'Catalyst::Plugin::Authentication';
+requires 'Catalyst::Plugin::Session';
+requires 'Catalyst::Plugin::Session::Store::File';
+requires 'Catalyst::Plugin::Session::State::Cookie';
+requires 'Catalyst::Plugin::StatusMessage';
+test_requires 'Test::More' => '0.88';
+catalyst;
+
+install_script glob('script/*.pl');
+auto_install;
+WriteAll;

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/README
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/README	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/README	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1 @@
+Run script/myapp_server.pl to test the application.


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Books.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Books.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Books.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,261 @@
+package MyApp::Controller::Books;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Books - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    $c->response->body('Matched MyApp::Controller::Books in Books.');
+}
+
+
+=head2 base
+
+Can place common logic to start chained dispatch here
+
+=cut
+
+sub base :Chained('/') :PathPart('books') :CaptureArgs(0) {
+    my ($self, $c) = @_;
+
+    # Store the ResultSet in stash so it's available for other methods
+    $c->stash(resultset => $c->model('DB::Book'));
+
+    # Print a message to the debug log
+    $c->log->debug('*** INSIDE BASE METHOD ***');
+
+    # Load status messages
+    $c->load_status_msgs;
+}
+
+
+=head2 list
+
+Fetch all book objects and pass to books/list.tt2 in stash to be displayed
+
+=cut
+
+sub list :Chained('base') :PathParth('list') :Args(0) {
+    # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst
+    # 'Context' that's used to 'glue together' the various components
+    # that make up the application
+    my ($self, $c) = @_;
+
+    # Retrieve all of the book records as book model objects and store
+    # in the stash where they can be accessed by the TT template
+    $c->stash(books => [$c->model('DB::Book')->all]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head2 url_create
+
+Create a book with the supplied title, rating, and author
+
+=cut
+
+sub url_create :Chained('/') :PathPart('books/url_create') :Args(3) {
+    # In addition to self & context, get the title, rating, &
+    # author_id args from the URL.  Note that Catalyst automatically
+    # puts the first 3 arguments worth of extra information after the
+    # "/<controller_name>/<action_name/" into @_ because we specified
+    # "Args(3)".  The args are separated  by the '/' char on the URL.
+    my ($self, $c, $title, $rating, $author_id) = @_;
+
+    # Call create() on the book model object. Pass the table
+    # columns/field values we want to set as hash values
+    my $book = $c->model('DB::Book')->create({
+            title  => $title,
+            rating => $rating
+        });
+
+    # Add a record to the join table for this book, mapping to
+    # appropriate author
+    $book->add_to_book_authors({author_id => $author_id});
+    # Note: Above is a shortcut for this:
+    # $book->create_related('book_authors', {author_id => $author_id});
+
+    # Assign the Book object to the stash for display and set template
+    $c->stash(book     => $book,
+              template => 'books/create_done.tt2');
+
+    # Disable caching for this page
+    $c->response->header('Cache-Control' => 'no-cache');
+}
+
+
+=head2 form_create
+
+Display form to collect information for book to create
+
+=cut
+
+sub form_create :Chained('base') :PathPart('form_create') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Set the TT template to use
+    $c->stash(template => 'books/form_create.tt2');
+}
+
+
+=head2 form_create_do
+
+Take information from form and add to database
+
+=cut
+
+sub form_create_do :Chained('base') :PathPart('form_create_do') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Retrieve the values from the form
+    my $title     = $c->request->params->{title}     || 'N/A';
+    my $rating    = $c->request->params->{rating}    || 'N/A';
+    my $author_id = $c->request->params->{author_id} || '1';
+
+    # Create the book
+    my $book = $c->model('DB::Book')->create({
+            title   => $title,
+            rating  => $rating,
+        });
+    # Handle relationship with author
+    $book->add_to_book_authors({author_id => $author_id});
+    # Note: Above is a shortcut for this:
+    # $book->create_related('book_authors', {author_id => $author_id});
+
+    # Store new model object in stash and set template
+    $c->stash(book     => $book,
+              template => 'books/create_done.tt2');
+}
+
+
+=head2 object
+
+Fetch the specified book object based on the book ID and store
+it in the stash
+
+=cut
+
+sub object :Chained('base') :PathPart('id') :CaptureArgs(1) {
+    # $id = primary key of book to delete
+    my ($self, $c, $id) = @_;
+
+    # Find the book object and store it in the stash
+    $c->stash(object => $c->stash->{resultset}->find($id));
+
+    # Make sure the lookup was successful.  You would probably
+    # want to do something like this in a real app:
+    #   $c->detach('/error_404') if !$c->stash->{object};
+    die "Book $id not found!" if !$c->stash->{object};
+
+    # Print a message to the debug log
+    $c->log->debug("*** INSIDE OBJECT METHOD for obj id=$id ***");
+}
+
+
+=head2 delete
+
+Delete a book
+
+=cut
+
+sub delete :Chained('object') :PathPart('delete') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Saved the PK id for status_msg below
+    my $id = $c->stash->{object}->id;
+
+    # Use the book object saved by 'object' and delete it along
+    # with related 'book_authors' entries
+    $c->stash->{object}->delete;
+
+    # Redirect the user back to the list page
+    $c->response->redirect($c->uri_for($self->action_for('list'),
+        {mid => $c->set_status_msg("Deleted book $id")}));
+}
+
+
+=head2 list_recent
+
+List recently created books
+
+=cut
+
+sub list_recent :Chained('base') :PathPart('list_recent') :Args(1) {
+    my ($self, $c, $mins) = @_;
+
+    # Retrieve all of the book records as book model objects and store in the
+    # stash where they can be accessed by the TT template, but only
+    # retrieve books created within the last $min number of minutes
+    $c->stash(books => [$c->model('DB::Book')
+                            ->created_after(DateTime->now->subtract(minutes => $mins))]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head2 list_recent_tcp
+
+List recently created books
+
+=cut
+
+sub list_recent_tcp :Chained('base') :PathPart('list_recent_tcp') :Args(1) {
+    my ($self, $c, $mins) = @_;
+
+    # Retrieve all of the book records as book model objects and store in the
+    # stash where they can be accessed by the TT template, but only
+    # retrieve books created within the last $min number of minutes
+    # AND that have 'TCP' in the title
+    $c->stash(books => [
+            $c->model('DB::Book')
+                ->created_after(DateTime->now->subtract(minutes => $mins))
+                ->title_like('TCP')
+        ]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Login.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Login.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Login.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,70 @@
+package MyApp::Controller::Login;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Login - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+Login logic
+
+=cut
+
+sub index :Path :Args(0) {
+    my ($self, $c) = @_;
+
+    # Get the username and password from form
+    my $username = $c->request->params->{username};
+    my $password = $c->request->params->{password};
+
+    # If the username and password values were found in form
+    if ($username && $password) {
+        # Attempt to log the user in
+        if ($c->authenticate({ username => $username,
+                               password => $password  } )) {
+            # If successful, then let them use the application
+            $c->response->redirect($c->uri_for(
+                $c->controller('Books')->action_for('list')));
+            return;
+        } else {
+            # Set an error message
+            $c->stash(error_msg => "Bad username or password.");
+        }
+    } else {
+        # Set an error message
+        $c->stash(error_msg => "Empty username or password.")
+            unless ($c->user_exists);
+    }
+
+    # If either of above don't work out, send to the login page
+    $c->stash(template => 'login.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Logout.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Logout.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Logout.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,50 @@
+package MyApp::Controller::Logout;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Logout - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+Logout logic
+
+=cut
+
+sub index :Path :Args(0) {
+    my ($self, $c) = @_;
+
+    # Clear the user's state
+    $c->logout;
+
+    # Send the user to the starting point
+    $c->response->redirect($c->uri_for('/'));
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Root.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Root.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Controller/Root.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,106 @@
+package MyApp::Controller::Root;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller' }
+
+#
+# Sets the actions in this controller to be registered with no prefix
+# so they function identically to actions created in MyApp.pm
+#
+__PACKAGE__->config(namespace => '');
+
+=head1 NAME
+
+MyApp::Controller::Root - Root Controller for MyApp
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 METHODS
+
+=head2 index
+
+The root page (/)
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    # Hello World
+    $c->response->body( $c->welcome_message );
+}
+
+=head2 default
+
+Standard 404 error page
+
+=cut
+
+sub default :Path {
+    my ( $self, $c ) = @_;
+    $c->response->body( 'Page not found' );
+    $c->response->status(404);
+}
+
+=head2 end
+
+Attempt to render a view, if needed.
+
+=cut
+
+sub end : ActionClass('RenderView') {}
+
+
+=head2 auto
+
+Check if there is a user and, if not, forward to login page
+
+=cut
+
+# Note that 'auto' runs after 'begin' but before your actions and that
+# 'auto's "chain" (all from application path to most specific class are run)
+# See the 'Actions' section of 'Catalyst::Manual::Intro' for more info.
+sub auto :Private {
+    my ($self, $c) = @_;
+
+    # Allow unauthenticated users to reach the login page.  This
+    # allows unauthenticated users to reach any action in the Login
+    # controller.  To lock it down to a single action, we could use:
+    #   if ($c->action eq $c->controller('Login')->action_for('index'))
+    # to only allow unauthenticated access to the 'index' action we
+    # added above.
+    if ($c->controller eq $c->controller('Login')) {
+        return 1;
+    }
+
+    # If a user doesn't exist, force login
+    if (!$c->user_exists) {
+        # Dump a log message to the development server debug output
+        $c->log->debug('***Root::auto User not found, forwarding to /login');
+        # Redirect the user to the login page
+        $c->response->redirect($c->uri_for('/login'));
+        # Return 0 to cancel 'post-auto' processing and prevent use of application
+        return 0;
+    }
+
+    # User found, so return 1 to continue with processing after this 'auto'
+    return 1;
+}
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Model
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Model/DB.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Model/DB.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Model/DB.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,44 @@
+package MyApp::Model::DB;
+
+use strict;
+use base 'Catalyst::Model::DBIC::Schema';
+
+__PACKAGE__->config(
+    schema_class => 'MyApp::Schema',
+    
+    connect_info => {
+        dsn => 'dbi:SQLite:myapp.db',
+        user => '',
+        password => '',
+        on_connect_do => q{PRAGMA foreign_keys = ON},
+    }
+);
+
+=head1 NAME
+
+MyApp::Model::DB - Catalyst DBIC Schema Model
+
+=head1 SYNOPSIS
+
+See L<MyApp>
+
+=head1 DESCRIPTION
+
+L<Catalyst::Model::DBIC::Schema> Model using schema L<MyApp::Schema>
+
+=head1 GENERATED BY
+
+Catalyst::Helper::Model::DBIC::Schema - 0.54
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/Author.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/Author.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/Author.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,98 @@
+package MyApp::Schema::Result::Author;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Author
+
+=cut
+
+__PACKAGE__->table("author");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 first_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 last_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "first_name",
+  { data_type => "text", is_nullable => 1 },
+  "last_name",
+  { data_type => "text", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 book_authors
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::BookAuthor>
+
+=cut
+
+__PACKAGE__->has_many(
+  "book_authors",
+  "MyApp::Schema::Result::BookAuthor",
+  { "foreign.author_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:R4dWFS+inIVXQOOhTbQr6A
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+# many_to_many():
+#   args:
+#     1) Name of relationship bridge, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(books => 'book_authors', 'book');
+
+
+#
+# Row-level helper methods
+#
+sub full_name {
+    my ($self) = @_;
+
+    return $self->first_name . ' ' . $self->last_name;
+}
+
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/Book.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/Book.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/Book.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,147 @@
+package MyApp::Schema::Result::Book;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Book
+
+=cut
+
+__PACKAGE__->table("book");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 title
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 rating
+
+  data_type: 'integer'
+  is_nullable: 1
+
+=head2 created
+
+  data_type: 'timestamp'
+  is_nullable: 1
+
+=head2 updated
+
+  data_type: 'timestamp'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "title",
+  { data_type => "text", is_nullable => 1 },
+  "rating",
+  { data_type => "integer", is_nullable => 1 },
+  "created",
+  { data_type => "timestamp", is_nullable => 1 },
+  "updated",
+  { data_type => "timestamp", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 book_authors
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::BookAuthor>
+
+=cut
+
+__PACKAGE__->has_many(
+  "book_authors",
+  "MyApp::Schema::Result::BookAuthor",
+  { "foreign.book_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:siH/HgXEpB/wjvd3w9eInw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+# many_to_many():
+#   args:
+#     1) Name of relationship bridge, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(authors => 'book_authors', 'author');
+
+#
+# Enable automatic date handling
+#
+__PACKAGE__->add_columns(
+    "created",
+    { data_type => 'timestamp', set_on_create => 1 },
+    "updated",
+    { data_type => 'timestamp', set_on_create => 1, set_on_update => 1 },
+);
+
+
+=head2 author_count
+
+Return the number of authors for the current book
+
+=cut
+
+sub author_count {
+    my ($self) = @_;
+
+    # Use the 'many_to_many' relationship to fetch all of the authors for the current
+    # and the 'count' method in DBIx::Class::ResultSet to get a SQL COUNT
+    return $self->authors->count;
+}
+
+
+=head2 author_list
+
+Return a comma-separated list of authors for the current book
+
+=cut
+
+sub author_list {
+    my ($self) = @_;
+
+    # Loop through all authors for the current book, calling all the 'full_name'
+    # Result Class method for each
+    my @names;
+    foreach my $author ($self->authors) {
+        push(@names, $author->full_name);
+    }
+
+    return join(', ', @names);
+}
+
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,87 @@
+package MyApp::Schema::Result::BookAuthor;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::BookAuthor
+
+=cut
+
+__PACKAGE__->table("book_author");
+
+=head1 ACCESSORS
+
+=head2 book_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 author_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "book_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+  "author_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("book_id", "author_id");
+
+=head1 RELATIONS
+
+=head2 author
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Author>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "author",
+  "MyApp::Schema::Result::Author",
+  { id => "author_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+=head2 book
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Book>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "book",
+  "MyApp::Schema::Result::Book",
+  { id => "book_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:YTOr2GyOa/ueirsn87uFCQ
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/Role.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/Role.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/Role.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,71 @@
+package MyApp::Schema::Result::Role;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Role
+
+=cut
+
+__PACKAGE__->table("role");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 role
+
+  data_type: 'text'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "role",
+  { data_type => "text", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 user_roles
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::UserRole>
+
+=cut
+
+__PACKAGE__->has_many(
+  "user_roles",
+  "MyApp::Schema::Result::UserRole",
+  { "foreign.role_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:AeucUX0dvANOw59CvUt4Pw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/User.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/User.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/User.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,131 @@
+package MyApp::Schema::Result::User;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::User
+
+=cut
+
+__PACKAGE__->table("users");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 username
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 password
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 email_address
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 first_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 last_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 active
+
+  data_type: 'integer'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "username",
+  { data_type => "text", is_nullable => 1 },
+  "password",
+  { data_type => "text", is_nullable => 1 },
+  "email_address",
+  { data_type => "text", is_nullable => 1 },
+  "first_name",
+  { data_type => "text", is_nullable => 1 },
+  "last_name",
+  { data_type => "text", is_nullable => 1 },
+  "active",
+  { data_type => "integer", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 user_roles
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::UserRole>
+
+=cut
+
+__PACKAGE__->has_many(
+  "user_roles",
+  "MyApp::Schema::Result::UserRole",
+  { "foreign.user_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:wVIvJZy8mps8EDY+mkUjTA
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+
+# many_to_many():
+#   args:
+#     1) Name of relationship, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(roles => 'user_roles', 'role');
+
+
+# Have the 'password' column use a SHA-1 hash and 20-byte salt
+# with RFC 2307 encoding; Generate the 'check_password" method
+__PACKAGE__->add_columns(
+    'password' => {
+        passphrase       => 'rfc2307',
+        passphrase_class => 'SaltedDigest',
+        passphrase_args  => {
+            algorithm   => 'SHA-1',
+            salt_random => 20.
+        },
+        passphrase_check_method => 'check_password',
+    },
+);
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/UserRole.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/UserRole.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/Result/UserRole.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,87 @@
+package MyApp::Schema::Result::UserRole;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::UserRole
+
+=cut
+
+__PACKAGE__->table("user_role");
+
+=head1 ACCESSORS
+
+=head2 user_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 role_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "user_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+  "role_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("user_id", "role_id");
+
+=head1 RELATIONS
+
+=head2 role
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Role>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "role",
+  "MyApp::Schema::Result::Role",
+  { id => "role_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+=head2 user
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::User>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "user",
+  "MyApp::Schema::Result::User",
+  { id => "user_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:HK7ZoN6FJ7zdBAjCzqJZ8w
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/ResultSet
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/ResultSet/Book.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/ResultSet/Book.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema/ResultSet/Book.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,41 @@
+package MyApp::Schema::ResultSet::Book;
+
+use strict;
+use warnings;
+use base 'DBIx::Class::ResultSet';
+
+=head2 created_after
+
+A predefined search for recently added books
+
+=cut
+
+sub created_after {
+    my ($self, $datetime) = @_;
+
+    my $date_str = $self->result_source->schema->storage
+                          ->datetime_parser->format_datetime($datetime);
+
+    return $self->search({
+        created => { '>' => $date_str }
+    });
+}
+
+
+=head2 title_like
+
+A predefined search for books with a 'LIKE' search in the string
+
+=cut
+
+sub title_like {
+    my ($self, $title_str) = @_;
+
+    return $self->search({
+        title => { 'like' => "%$title_str%" }
+    });
+}
+
+
+1;
+

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/Schema.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,19 @@
+package MyApp::Schema;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use Moose;
+use namespace::autoclean;
+extends 'DBIx::Class::Schema';
+
+__PACKAGE__->load_namespaces;
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 18:09:34
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:0eebDopG0RPImUoVlRBhTg
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/View
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/View/HTML.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/View/HTML.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp/View/HTML.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+package MyApp::View::HTML;
+
+use strict;
+use warnings;
+
+use base 'Catalyst::View::TT';
+
+__PACKAGE__->config(
+    # Change default TT extension
+    TEMPLATE_EXTENSION => '.tt2',
+    # Set the location for TT files
+    INCLUDE_PATH => [
+            MyApp->path_to( 'root', 'src' ),
+        ],
+    # Set to 1 for detailed timer stats in your HTML as comments
+    TIMER              => 0,
+    # This is your wrapper template located in the 'root/src'
+    WRAPPER => 'wrapper.tt2',
+);
+
+
+=head1 NAME
+
+MyApp::View::HTML - TT View for MyApp
+
+=head1 DESCRIPTION
+
+TT View for MyApp.
+
+=head1 SEE ALSO
+
+L<MyApp>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/lib/MyApp.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,102 @@
+package MyApp;
+use Moose;
+use namespace::autoclean;
+
+use Catalyst::Runtime 5.80;
+
+# Set flags and add plugins for the application.
+#
+# Note that ORDERING IS IMPORTANT here as plugins are initialized in order,
+# therefore you almost certainly want to keep ConfigLoader at the head of the
+# list if you're using it.
+#
+#         -Debug: activates the debug mode for very useful log messages
+#   ConfigLoader: will load the configuration from a Config::General file in the
+#                 application's home directory
+# Static::Simple: will serve static files from the application's root
+#                 directory
+
+use Catalyst qw/
+    -Debug
+    ConfigLoader
+    Static::Simple
+
+    Authentication
+    
+    Session
+    Session::Store::File
+    Session::State::Cookie
+
+    StatusMessage
+/;
+
+extends 'Catalyst';
+
+our $VERSION = '0.01';
+
+# Configure the application.
+#
+# Note that settings in myapp.conf (or other external
+# configuration file that you set up manually) take precedence
+# over this when using ConfigLoader. Thus configuration
+# details given here can function as a default configuration,
+# with an external configuration file acting as an override for
+# local deployment.
+
+__PACKAGE__->config(
+    name => 'MyApp',
+    # Disable deprecated behavior needed by old applications
+    disable_component_resolution_regex_fallback => 1,
+);
+__PACKAGE__->config(
+    # Configure the view
+    'View::HTML' => {
+        #Set the location for TT files
+        INCLUDE_PATH => [
+            __PACKAGE__->path_to( 'root', 'src' ),
+        ],
+    },
+);
+# Configure SimpleDB Authentication
+__PACKAGE__->config(
+    'Plugin::Authentication' => {
+        default => {
+            class           => 'SimpleDB',
+            user_model      => 'DB::User',
+            password_type   => 'self_check',
+        },
+    },
+);
+
+# Start the application
+__PACKAGE__->setup();
+
+
+=head1 NAME
+
+MyApp - Catalyst based application
+
+=head1 SYNOPSIS
+
+    script/myapp_server.pl
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 SEE ALSO
+
+L<MyApp::Controller::Root>, L<Catalyst>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp.conf
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp.conf	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp.conf	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,3 @@
+# rename this file to myapp.yml and put a ':' after 'name' if
+# you want to use YAML like in old versions of Catalyst
+name MyApp

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp.db
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp.db
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp.psgi
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp.psgi	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp.psgi	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+use MyApp;
+
+my $app = MyApp->apply_default_middlewares(MyApp->psgi_app);
+$app;
+

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp01.sql
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp01.sql	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp01.sql	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+--
+-- Create a very simple database to hold book and author information
+--
+PRAGMA foreign_keys = ON;
+CREATE TABLE book (
+        id          INTEGER PRIMARY KEY,
+        title       TEXT ,
+        rating      INTEGER
+);
+-- 'book_author' is a many-to-many join table between books & authors
+CREATE TABLE book_author (
+        book_id     INTEGER REFERENCES book(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        author_id   INTEGER REFERENCES author(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        PRIMARY KEY (book_id, author_id)
+);
+CREATE TABLE author (
+        id          INTEGER PRIMARY KEY,
+        first_name  TEXT,
+        last_name   TEXT
+);
+---
+--- Load some sample data
+---
+INSERT INTO book VALUES (1, 'CCSP SNRS Exam Certification Guide', 5);
+INSERT INTO book VALUES (2, 'TCP/IP Illustrated, Volume 1', 5);
+INSERT INTO book VALUES (3, 'Internetworking with TCP/IP Vol.1', 4);
+INSERT INTO book VALUES (4, 'Perl Cookbook', 5);
+INSERT INTO book VALUES (5, 'Designing with Web Standards', 5);
+INSERT INTO author VALUES (1, 'Greg', 'Bastien');
+INSERT INTO author VALUES (2, 'Sara', 'Nasseh');
+INSERT INTO author VALUES (3, 'Christian', 'Degu');
+INSERT INTO author VALUES (4, 'Richard', 'Stevens');
+INSERT INTO author VALUES (5, 'Douglas', 'Comer');
+INSERT INTO author VALUES (6, 'Tom', 'Christiansen');
+INSERT INTO author VALUES (7, 'Nathan', 'Torkington');
+INSERT INTO author VALUES (8, 'Jeffrey', 'Zeldman');
+INSERT INTO book_author VALUES (1, 1);
+INSERT INTO book_author VALUES (1, 2);
+INSERT INTO book_author VALUES (1, 3);
+INSERT INTO book_author VALUES (2, 4);
+INSERT INTO book_author VALUES (3, 5);
+INSERT INTO book_author VALUES (4, 6);
+INSERT INTO book_author VALUES (4, 7);
+INSERT INTO book_author VALUES (5, 8);
+

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp02.sql
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp02.sql	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/myapp02.sql	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,36 @@
+--
+-- Add users and role tables, along with a many-to-many join table
+--
+PRAGMA foreign_keys = ON;
+CREATE TABLE users (
+        id            INTEGER PRIMARY KEY,
+        username      TEXT,
+        password      TEXT,
+        email_address TEXT,
+        first_name    TEXT,
+        last_name     TEXT,
+        active        INTEGER
+);
+CREATE TABLE role (
+        id   INTEGER PRIMARY KEY,
+        role TEXT
+);
+CREATE TABLE user_role (
+        user_id INTEGER REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        role_id INTEGER REFERENCES role(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        PRIMARY KEY (user_id, role_id)
+);
+--
+-- Load up some initial test data
+--
+INSERT INTO users VALUES (1, 'test01', 'mypass', 't01 at na.com', 'Joe',  'Blow', 1);
+INSERT INTO users VALUES (2, 'test02', 'mypass', 't02 at na.com', 'Jane', 'Doe',  1);
+INSERT INTO users VALUES (3, 'test03', 'mypass', 't03 at na.com', 'No',   'Go',   0);
+INSERT INTO role VALUES (1, 'user');
+INSERT INTO role VALUES (2, 'admin');
+INSERT INTO user_role VALUES (1, 1);
+INSERT INTO user_role VALUES (1, 2);
+INSERT INTO user_role VALUES (2, 1);
+INSERT INTO user_role VALUES (3, 1);
+
+


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/favicon.ico
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/favicon.ico
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/books
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/books/create_done.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/books/create_done.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/books/create_done.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,32 @@
+[% # Use the TT Dumper plugin to Data::Dumper variables to the browser   -%]
+[% # Not a good idea for production use, though. :-)  'Indent=1' is      -%]
+[% # optional, but prevents "massive indenting" of deeply nested objects -%]
+[% USE Dumper(Indent=1) -%]
+
+[% # Set the page title.  META can 'go back' and set values in templates -%]
+[% # that have been processed 'before' this template (here it's updating -%]
+[% # the title in the root/src/wrapper.tt2 wrapper template).  Note that -%]
+[% # META only works on simple/static strings (i.e. there is no variable -%]
+[% # interpolation -- if you need dynamic/interpolated content in your   -%]
+[% # title, set "$c->stash(title => $something)" in the controller).     -%]
+[% META title = 'Book Created' %]
+
+[% # Output information about the record that was added.  First title.   -%]
+<p>Added book '[% book.title %]'
+
+[% # Then, output the last name of the first author -%]
+by '[% book.authors.first.last_name %]'
+
+[% # Then, output the rating for the book that was added -%]
+with a rating of [% book.rating %].</p>
+
+[% # Provide a link back to the list page.  'c.uri_for' builds -%]
+[% # a full URI; e.g., 'http://localhost:3000/books/list'      -%]
+<p><a href="[% c.uri_for('/books/list') %]">Return to list</a></p>
+
+[% # Try out the TT Dumper (for development only!) -%]
+<pre>
+Dump of the 'book' variable:
+[% Dumper.dump(book) %]
+</pre>
+

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/books/form_create.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/books/form_create.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/books/form_create.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,11 @@
+[% META title = 'Manual Form Book Create' -%]
+
+<form method="post" action="[% c.uri_for('form_create_do') %]">
+<table>
+  <tr><td>Title:</td><td><input type="text" name="title"></td></tr>
+  <tr><td>Rating:</td><td><input type="text" name="rating"></td></tr>
+  <tr><td>Author ID:</td><td><input type="text" name="author_id"></td></tr>
+</table>
+<input type="submit" name="Submit" value="Submit">
+</form>
+

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/books/list.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/books/list.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/books/list.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,33 @@
+[% # This is a TT comment. -%]
+
+[%- # Provide a title -%]
+[% META title = 'Book List' -%]
+
+[% # Note That the '-' at the beginning or end of TT code  -%]
+[% # "chomps" the whitespace/newline at that end of the    -%]
+[% # output (use View Source in browser to see the effect) -%]
+
+[% # Some basic HTML with a loop to display books -%]
+<table>
+<tr><th>Title</th><th>Rating</th><th>Author(s)</th><th>Links</th></tr>
+[% # Display each book in a table row %]
+[% FOREACH book IN books -%]
+  <tr>
+    <td>[% book.title %]</td>
+    <td>[% book.rating %]</td>
+    <td>
+      [% # Print count and author list using Result Class methods -%]
+      ([% book.author_count | html %]) [% book.author_list | html %]
+    </td>
+    <td>
+      [% # Add a link to delete a book %]
+      <a href="[%
+        c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
+    </td>
+  </tr>
+[% END -%]
+</table>
+<p>
+  <a href="[% c.uri_for('/login') %]">Login</a>
+  <a href="[% c.uri_for(c.controller.action_for('form_create')) %]">Create</a>
+</p>

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/login.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/login.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/login.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,37 @@
+[% META title = 'Login' %]
+
+<!-- Login form -->
+<form method="post" action="[% c.uri_for('/login') %]">
+  <table>
+    <tr>
+      <td>Username:</td>
+      <td><input type="text" name="username" size="40" /></td>
+    </tr>
+    <tr>
+      <td>Password:</td>
+      <td><input type="password" name="password" size="40" /></td>
+    </tr>
+    <tr>
+      <td colspan="2"><input type="submit" name="submit" value="Submit" /></td>
+    </tr>
+  </table>
+</form>
+<p>
+[%
+   # This code illustrates how certain parts of the TT
+   # template will only be shown to users who have logged in
+%]
+[% IF c.user_exists %]
+    Please Note: You are already logged in as '[% c.user.username %]'.
+    You can <a href="[% c.uri_for('/logout') %]">logout</a> here.
+[% ELSE %]
+    You need to log in to use this application.
+[% END %]
+[%#
+   Note that this whole block is a comment because the "#" appears
+   immediate after the "[%" (with no spaces in between).  Although it
+   can be a handy way to temporarily "comment out" a whole block of
+   TT code, it's probably a little too subtle for use in "normal"
+   comments.
+%]
+</p>

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/wrapper.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/wrapper.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/root/src/wrapper.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" [%#
+    %]"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>[% template.title or "My Catalyst App!" %]</title>
+<link rel="stylesheet" href="[% c.uri_for('/static/css/main.css') %]" />
+</head>
+
+<body>
+<div id="outer">
+<div id="header">
+    [%# Your logo could go here -%]
+    <img src="[% c.uri_for('/static/images/btn_88x31_powered.png') %]" />
+    [%# Insert the page title -%]
+    <h1>[% template.title or site.title %]</h1>
+</div>
+
+<div id="bodyblock">
+<div id="menu">
+    Navigation:
+    <ul>
+        <li><a href="[% c.uri_for('/books/list') %]">Home</a></li>
+        <li><a href="[% c.uri_for('/')
+            %]" title="Catalyst Welcome Page">Welcome</a></li>
+    </ul>
+</div><!-- end menu -->
+
+<div id="content">
+    [%# Status and error messages %]
+    <span class="message">[% status_msg %]</span>
+    <span class="error">[% error_msg %]</span>
+    [%# This is where TT will stick all of your template's contents. -%]
+    [% content %]
+</div><!-- end content -->
+</div><!-- end bodyblock -->
+
+<div id="footer">Copyright (c) your name goes here</div>
+</div><!-- end outer -->
+
+</body>
+</html>
+


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/css
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/css/main.css
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/css/main.css	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/css/main.css	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,37 @@
+#header {
+    text-align: center;
+}
+#header h1 {
+    margin: 0;
+}
+#header img {
+    float: right;
+}
+#footer {
+    text-align: center;
+    font-style: italic;
+    padding-top: 20px;
+}
+#menu {
+    font-weight: bold;
+    background-color: #ddd;
+}
+#menu ul {
+    list-style: none;
+    float: left;
+    margin: 0;
+    padding: 0 0 50% 5px;
+    font-weight: normal;
+    background-color: #ddd;
+    width: 100px;
+}
+#content {
+    margin-left: 120px;
+}
+.message {
+    color: #390;
+}
+.error {
+    color: #f00;
+}
+


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_120x50_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_120x50_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_120x50_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_120x50_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_120x50_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_120x50_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_120x50_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_120x50_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_88x31_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_88x31_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_88x31_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_88x31_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_88x31_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_88x31_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_88x31_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/btn_88x31_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/catalyst_logo.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/root/static/images/catalyst_logo.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/script
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_cgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_cgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_cgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'CGI');
+
+1;
+
+=head1 NAME
+
+myapp_cgi.pl - Catalyst CGI
+
+=head1 SYNOPSIS
+
+See L<Catalyst::Manual>
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as a cgi script.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_create.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_create.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_create.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Create');
+
+1;
+
+=head1 NAME
+
+myapp_create.pl - Create a new Catalyst Component
+
+=head1 SYNOPSIS
+
+myapp_create.pl [options] model|view|controller name [helper] [options]
+
+ Options:
+   --force        don't create a .new file where a file to be created exists
+   --mechanize    use Test::WWW::Mechanize::Catalyst for tests if available
+   --help         display this help and exits
+
+ Examples:
+   myapp_create.pl controller My::Controller
+   myapp_create.pl --mechanize controller My::Controller
+   myapp_create.pl view My::View
+   myapp_create.pl view HTML TT
+   myapp_create.pl model My::Model
+   myapp_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\
+   dbi:SQLite:/tmp/my.db
+   myapp_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static\
+   [Loader opts like db_schema, naming] dbi:Pg:dbname=foo root 4321
+   [connect_info opts like quote_char, name_sep]
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+   perldoc Catalyst::Helper::Model::DBIC::Schema
+   perldoc Catalyst::Model::DBIC::Schema
+   perldoc Catalyst::View::TT
+
+=head1 DESCRIPTION
+
+Create a new Catalyst Component.
+
+Existing component files are not overwritten.  If any of the component files
+to be created already exist the file will be written with a '.new' suffix.
+This behavior can be suppressed with the C<-force> option.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_fastcgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_fastcgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_fastcgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,48 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'FastCGI');
+
+1;
+
+=head1 NAME
+
+myapp_fastcgi.pl - Catalyst FastCGI
+
+=head1 SYNOPSIS
+
+myapp_fastcgi.pl [options]
+
+ Options:
+   -? -help      display this help and exits
+   -l --listen   Socket path to listen on
+                 (defaults to standard input)
+                 can be HOST:PORT, :PORT or a
+                 filesystem path
+   -n --nproc    specify number of processes to keep
+                 to serve requests (defaults to 1,
+                 requires -listen)
+   -p --pidfile  specify filename for pid file
+                 (requires -listen)
+   -d --daemon   daemonize (requires -listen)
+   -M --manager  specify alternate process manager
+                 (FCGI::ProcManager sub-class)
+                 or empty string to disable
+   -e --keeperr  send error messages to STDOUT, not
+                 to the webserver
+   --proc_title  Set the process title (is possible)
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as fastcgi.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_server.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_server.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_server.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+BEGIN {
+    $ENV{CATALYST_SCRIPT_GEN} = 40;
+}
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Server');
+
+1;
+
+=head1 NAME
+
+myapp_server.pl - Catalyst Test Server
+
+=head1 SYNOPSIS
+
+myapp_server.pl [options]
+
+   -d --debug           force debug mode
+   -f --fork            handle each request in a new process
+                        (defaults to false)
+   -? --help            display this help and exits
+   -h --host            host (defaults to all)
+   -p --port            port (defaults to 3000)
+   -k --keepalive       enable keep-alive connections
+   -r --restart         restart when files get modified
+                        (defaults to false)
+   -rd --restart_delay  delay between file checks
+                        (ignored if you have Linux::Inotify2 installed)
+   -rr --restart_regex  regex match files that trigger
+                        a restart when modified
+                        (defaults to '\.yml$|\.yaml$|\.conf|\.pm$')
+   --restart_directory  the directory to search for
+                        modified files, can be set multiple times
+                        (defaults to '[SCRIPT_DIR]/..')
+   --follow_symlinks    follow symlinks in search directories
+                        (defaults to false. this is a no-op on Win32)
+   --background         run the process in the background
+   --pidfile            specify filename for pid file
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst Testserver for this application.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_test.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_test.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/script/myapp_test.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,40 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Test');
+
+1;
+
+=head1 NAME
+
+myapp_test.pl - Catalyst Test
+
+=head1 SYNOPSIS
+
+myapp_test.pl [options] uri
+
+ Options:
+   --help    display this help and exits
+
+ Examples:
+   myapp_test.pl http://localhost/some_action
+   myapp_test.pl /some_action
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst action from the command line.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/set_hashed_passwords.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/set_hashed_passwords.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/set_hashed_passwords.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use MyApp::Schema;
+
+my $schema = MyApp::Schema->connect('dbi:SQLite:myapp.db');
+
+my @users = $schema->resultset('User')->all;
+
+foreach my $user (@users) {
+    $user->password('mypass');
+    $user->update;
+}


Property changes on: trunk/examples/Tutorial/Final/Chapter05/MyApp/t
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/t/01app.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/t/01app.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/t/01app.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+use Catalyst::Test 'MyApp';
+
+ok( request('/')->is_success, 'Request should succeed' );
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/t/02pod.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/t/02pod.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/t/02pod.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+eval "use Test::Pod 1.14";
+plan skip_all => 'Test::Pod 1.14 required' if $@;
+
+all_pod_files_ok();

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/t/03podcoverage.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/t/03podcoverage.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/t/03podcoverage.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,14 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@;
+
+eval "use Pod::Coverage 0.20";
+plan skip_all => 'Pod::Coverage 0.20 required' if $@;
+
+all_pod_coverage_ok();

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/t/controller_Books.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/t/controller_Books.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/t/controller_Books.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Books;
+
+ok( request('/books')->is_success, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/t/controller_Login.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/t/controller_Login.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/t/controller_Login.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Login;
+
+ok( request('/login')->is_success, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/t/controller_Logout.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/t/controller_Logout.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/t/controller_Logout.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Logout;
+
+ok( request('/logout')->is_success, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/t/model_DB.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/t/model_DB.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/t/model_DB.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+BEGIN { use_ok 'MyApp::Model::DB' }
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter05/MyApp/t/view_HTML.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter05/MyApp/t/view_HTML.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter05/MyApp/t/view_HTML.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+BEGIN { use_ok 'MyApp::View::HTML' }
+
+done_testing();


Property changes on: trunk/examples/Tutorial/Final/Chapter06
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/Changes
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/Changes	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/Changes	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,4 @@
+This file documents the revision history for Perl extension MyApp.
+
+0.01  2011-08-30 17:51:40
+        - initial revision, generated by Catalyst

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/Makefile.PL
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/Makefile.PL	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/Makefile.PL	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+# IMPORTANT: if you delete this file your app will not work as
+# expected.  You have been warned.
+use inc::Module::Install;
+use Module::Install::Catalyst; # Complain loudly if you don't have
+                               # Catalyst::Devel installed or haven't said
+                               # 'make dist' to create a standalone tarball.
+
+name 'MyApp';
+all_from 'lib/MyApp.pm';
+
+requires 'Catalyst::Runtime' => '5.90002';
+requires 'Catalyst::Plugin::ConfigLoader';
+requires 'Catalyst::Plugin::Static::Simple';
+requires 'Catalyst::Action::RenderView';
+requires 'Moose';
+requires 'namespace::autoclean';
+requires 'Config::General'; # This should reflect the config file format you've chosen
+                 # See Catalyst::Plugin::ConfigLoader for supported formats
+requires 'Catalyst::Plugin::Authentication';
+requires 'Catalyst::Plugin::Session';
+requires 'Catalyst::Plugin::Session::Store::File';
+requires 'Catalyst::Plugin::Session::State::Cookie';
+requires 'Catalyst::Plugin::StatusMessage';
+requires 'Catalyst::Plugin::Authorization::Roles';
+requires 'Perl6::Junction';
+test_requires 'Test::More' => '0.88';
+catalyst;
+
+install_script glob('script/*.pl');
+auto_install;
+WriteAll;

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/README
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/README	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/README	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1 @@
+Run script/myapp_server.pl to test the application.


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Books.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Books.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Books.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,267 @@
+package MyApp::Controller::Books;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Books - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    $c->response->body('Matched MyApp::Controller::Books in Books.');
+}
+
+
+=head2 base
+
+Can place common logic to start chained dispatch here
+
+=cut
+
+sub base :Chained('/') :PathPart('books') :CaptureArgs(0) {
+    my ($self, $c) = @_;
+
+    # Store the ResultSet in stash so it's available for other methods
+    $c->stash(resultset => $c->model('DB::Book'));
+
+    # Print a message to the debug log
+    $c->log->debug('*** INSIDE BASE METHOD ***');
+
+    # Load status messages
+    $c->load_status_msgs;
+}
+
+
+=head2 list
+
+Fetch all book objects and pass to books/list.tt2 in stash to be displayed
+
+=cut
+
+sub list :Chained('base') :PathParth('list') :Args(0) {
+    # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst
+    # 'Context' that's used to 'glue together' the various components
+    # that make up the application
+    my ($self, $c) = @_;
+
+    # Retrieve all of the book records as book model objects and store
+    # in the stash where they can be accessed by the TT template
+    $c->stash(books => [$c->model('DB::Book')->all]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head2 url_create
+
+Create a book with the supplied title and rating,
+with manual authorization
+
+=cut
+
+sub url_create :Chained('base') :PathPart('url_create') :Args(3) {
+    # In addition to self & context, get the title, rating & author_id args
+    # from the URL.  Note that Catalyst automatically puts extra information
+    # after the "/<controller_name>/<action_name/" into @_
+    my ($self, $c, $title, $rating, $author_id) = @_;
+
+    # Check the user's roles
+    if ($c->check_user_roles('admin')) {
+        # Call create() on the book model object. Pass the table
+        # columns/field values we want to set as hash values
+        my $book = $c->model('DB::Book')->create({
+                title   => $title,
+                rating  => $rating
+            });
+
+        # Add a record to the join table for this book, mapping to
+        # appropriate author
+        $book->add_to_book_authors({author_id => $author_id});
+        # Note: Above is a shortcut for this:
+        # $book->create_related('book_authors', {author_id => $author_id});
+
+        # Assign the Book object to the stash and set template
+        $c->stash(book     => $book,
+                  template => 'books/create_done.tt2');
+    } else {
+        # Provide very simple feedback to the user.
+        $c->response->body('Unauthorized!');
+    }
+}
+
+
+=head2 form_create
+
+Display form to collect information for book to create
+
+=cut
+
+sub form_create :Chained('base') :PathPart('form_create') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Set the TT template to use
+    $c->stash(template => 'books/form_create.tt2');
+}
+
+
+=head2 form_create_do
+
+Take information from form and add to database
+
+=cut
+
+sub form_create_do :Chained('base') :PathPart('form_create_do') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Retrieve the values from the form
+    my $title     = $c->request->params->{title}     || 'N/A';
+    my $rating    = $c->request->params->{rating}    || 'N/A';
+    my $author_id = $c->request->params->{author_id} || '1';
+
+    # Create the book
+    my $book = $c->model('DB::Book')->create({
+            title   => $title,
+            rating  => $rating,
+        });
+    # Handle relationship with author
+    $book->add_to_book_authors({author_id => $author_id});
+    # Note: Above is a shortcut for this:
+    # $book->create_related('book_authors', {author_id => $author_id});
+
+    # Store new model object in stash and set template
+    $c->stash(book     => $book,
+              template => 'books/create_done.tt2');
+}
+
+
+=head2 object
+
+Fetch the specified book object based on the book ID and store
+it in the stash
+
+=cut
+
+sub object :Chained('base') :PathPart('id') :CaptureArgs(1) {
+    # $id = primary key of book to delete
+    my ($self, $c, $id) = @_;
+
+    # Find the book object and store it in the stash
+    $c->stash(object => $c->stash->{resultset}->find($id));
+
+    # Make sure the lookup was successful.  You would probably
+    # want to do something like this in a real app:
+    #   $c->detach('/error_404') if !$c->stash->{object};
+    die "Book $id not found!" if !$c->stash->{object};
+
+    # Print a message to the debug log
+    $c->log->debug("*** INSIDE OBJECT METHOD for obj id=$id ***");
+}
+
+
+=head2 delete
+
+Delete a book
+
+=cut
+
+sub delete :Chained('object') :PathPart('delete') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Check permissions
+    $c->detach('/error_noperms')
+        unless $c->stash->{object}->delete_allowed_by($c->user->get_object);
+
+    # Saved the PK id for status_msg below
+    my $id = $c->stash->{object}->id;
+
+    # Use the book object saved by 'object' and delete it along
+    # with related 'book_authors' entries
+    $c->stash->{object}->delete;
+
+    # Redirect the user back to the list page
+    $c->response->redirect($c->uri_for($self->action_for('list'),
+        {mid => $c->set_status_msg("Deleted book $id")}));
+}
+
+
+=head2 list_recent
+
+List recently created books
+
+=cut
+
+sub list_recent :Chained('base') :PathPart('list_recent') :Args(1) {
+    my ($self, $c, $mins) = @_;
+
+    # Retrieve all of the book records as book model objects and store in the
+    # stash where they can be accessed by the TT template, but only
+    # retrieve books created within the last $min number of minutes
+    $c->stash(books => [$c->model('DB::Book')
+                            ->created_after(DateTime->now->subtract(minutes => $mins))]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head2 list_recent_tcp
+
+List recently created books
+
+=cut
+
+sub list_recent_tcp :Chained('base') :PathPart('list_recent_tcp') :Args(1) {
+    my ($self, $c, $mins) = @_;
+
+    # Retrieve all of the book records as book model objects and store in the
+    # stash where they can be accessed by the TT template, but only
+    # retrieve books created within the last $min number of minutes
+    # AND that have 'TCP' in the title
+    $c->stash(books => [
+            $c->model('DB::Book')
+                ->created_after(DateTime->now->subtract(minutes => $mins))
+                ->title_like('TCP')
+        ]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Login.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Login.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Login.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,70 @@
+package MyApp::Controller::Login;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Login - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+Login logic
+
+=cut
+
+sub index :Path :Args(0) {
+    my ($self, $c) = @_;
+
+    # Get the username and password from form
+    my $username = $c->request->params->{username};
+    my $password = $c->request->params->{password};
+
+    # If the username and password values were found in form
+    if ($username && $password) {
+        # Attempt to log the user in
+        if ($c->authenticate({ username => $username,
+                               password => $password  } )) {
+            # If successful, then let them use the application
+            $c->response->redirect($c->uri_for(
+                $c->controller('Books')->action_for('list')));
+            return;
+        } else {
+            # Set an error message
+            $c->stash(error_msg => "Bad username or password.");
+        }
+    } else {
+        # Set an error message
+        $c->stash(error_msg => "Empty username or password.")
+            unless ($c->user_exists);
+    }
+
+    # If either of above don't work out, send to the login page
+    $c->stash(template => 'login.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Logout.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Logout.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Logout.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,50 @@
+package MyApp::Controller::Logout;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Logout - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+Logout logic
+
+=cut
+
+sub index :Path :Args(0) {
+    my ($self, $c) = @_;
+
+    # Clear the user's state
+    $c->logout;
+
+    # Send the user to the starting point
+    $c->response->redirect($c->uri_for('/'));
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Root.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Root.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Controller/Root.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,120 @@
+package MyApp::Controller::Root;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller' }
+
+#
+# Sets the actions in this controller to be registered with no prefix
+# so they function identically to actions created in MyApp.pm
+#
+__PACKAGE__->config(namespace => '');
+
+=head1 NAME
+
+MyApp::Controller::Root - Root Controller for MyApp
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 METHODS
+
+=head2 index
+
+The root page (/)
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    # Hello World
+    $c->response->body( $c->welcome_message );
+}
+
+=head2 default
+
+Standard 404 error page
+
+=cut
+
+sub default :Path {
+    my ( $self, $c ) = @_;
+    $c->response->body( 'Page not found' );
+    $c->response->status(404);
+}
+
+=head2 end
+
+Attempt to render a view, if needed.
+
+=cut
+
+sub end : ActionClass('RenderView') {}
+
+
+=head2 auto
+
+Check if there is a user and, if not, forward to login page
+
+=cut
+
+# Note that 'auto' runs after 'begin' but before your actions and that
+# 'auto's "chain" (all from application path to most specific class are run)
+# See the 'Actions' section of 'Catalyst::Manual::Intro' for more info.
+sub auto :Private {
+    my ($self, $c) = @_;
+
+    # Allow unauthenticated users to reach the login page.  This
+    # allows unauthenticated users to reach any action in the Login
+    # controller.  To lock it down to a single action, we could use:
+    #   if ($c->action eq $c->controller('Login')->action_for('index'))
+    # to only allow unauthenticated access to the 'index' action we
+    # added above.
+    if ($c->controller eq $c->controller('Login')) {
+        return 1;
+    }
+
+    # If a user doesn't exist, force login
+    if (!$c->user_exists) {
+        # Dump a log message to the development server debug output
+        $c->log->debug('***Root::auto User not found, forwarding to /login');
+        # Redirect the user to the login page
+        $c->response->redirect($c->uri_for('/login'));
+        # Return 0 to cancel 'post-auto' processing and prevent use of application
+        return 0;
+    }
+
+    # User found, so return 1 to continue with processing after this 'auto'
+    return 1;
+}
+
+
+=head2 error_noperms
+
+Permissions error screen
+
+=cut
+
+sub error_noperms :Chained('/') :PathPart('error_noperms') :Args(0) {
+    my ($self, $c) = @_;
+
+    $c->stash(template => 'error_noperms.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Model
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Model/DB.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Model/DB.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Model/DB.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,44 @@
+package MyApp::Model::DB;
+
+use strict;
+use base 'Catalyst::Model::DBIC::Schema';
+
+__PACKAGE__->config(
+    schema_class => 'MyApp::Schema',
+    
+    connect_info => {
+        dsn => 'dbi:SQLite:myapp.db',
+        user => '',
+        password => '',
+        on_connect_do => q{PRAGMA foreign_keys = ON},
+    }
+);
+
+=head1 NAME
+
+MyApp::Model::DB - Catalyst DBIC Schema Model
+
+=head1 SYNOPSIS
+
+See L<MyApp>
+
+=head1 DESCRIPTION
+
+L<Catalyst::Model::DBIC::Schema> Model using schema L<MyApp::Schema>
+
+=head1 GENERATED BY
+
+Catalyst::Helper::Model::DBIC::Schema - 0.54
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/Author.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/Author.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/Author.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,98 @@
+package MyApp::Schema::Result::Author;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Author
+
+=cut
+
+__PACKAGE__->table("author");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 first_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 last_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "first_name",
+  { data_type => "text", is_nullable => 1 },
+  "last_name",
+  { data_type => "text", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 book_authors
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::BookAuthor>
+
+=cut
+
+__PACKAGE__->has_many(
+  "book_authors",
+  "MyApp::Schema::Result::BookAuthor",
+  { "foreign.author_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:R4dWFS+inIVXQOOhTbQr6A
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+# many_to_many():
+#   args:
+#     1) Name of relationship bridge, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(books => 'book_authors', 'book');
+
+
+#
+# Row-level helper methods
+#
+sub full_name {
+    my ($self) = @_;
+
+    return $self->first_name . ' ' . $self->last_name;
+}
+
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/Book.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/Book.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/Book.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,161 @@
+package MyApp::Schema::Result::Book;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Book
+
+=cut
+
+__PACKAGE__->table("book");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 title
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 rating
+
+  data_type: 'integer'
+  is_nullable: 1
+
+=head2 created
+
+  data_type: 'timestamp'
+  is_nullable: 1
+
+=head2 updated
+
+  data_type: 'timestamp'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "title",
+  { data_type => "text", is_nullable => 1 },
+  "rating",
+  { data_type => "integer", is_nullable => 1 },
+  "created",
+  { data_type => "timestamp", is_nullable => 1 },
+  "updated",
+  { data_type => "timestamp", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 book_authors
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::BookAuthor>
+
+=cut
+
+__PACKAGE__->has_many(
+  "book_authors",
+  "MyApp::Schema::Result::BookAuthor",
+  { "foreign.book_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:siH/HgXEpB/wjvd3w9eInw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+# many_to_many():
+#   args:
+#     1) Name of relationship bridge, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(authors => 'book_authors', 'author');
+
+#
+# Enable automatic date handling
+#
+__PACKAGE__->add_columns(
+    "created",
+    { data_type => 'timestamp', set_on_create => 1 },
+    "updated",
+    { data_type => 'timestamp', set_on_create => 1, set_on_update => 1 },
+);
+
+
+=head2 author_count
+
+Return the number of authors for the current book
+
+=cut
+
+sub author_count {
+    my ($self) = @_;
+
+    # Use the 'many_to_many' relationship to fetch all of the authors for the current
+    # and the 'count' method in DBIx::Class::ResultSet to get a SQL COUNT
+    return $self->authors->count;
+}
+
+
+=head2 author_list
+
+Return a comma-separated list of authors for the current book
+
+=cut
+
+sub author_list {
+    my ($self) = @_;
+
+    # Loop through all authors for the current book, calling all the 'full_name'
+    # Result Class method for each
+    my @names;
+    foreach my $author ($self->authors) {
+        push(@names, $author->full_name);
+    }
+
+    return join(', ', @names);
+}
+
+
+
+=head2 delete_allowed_by
+
+Can the specified user delete the current book?
+
+=cut
+
+sub delete_allowed_by {
+    my ($self, $user) = @_;
+
+    # Only allow delete if user has 'admin' role
+    return $user->has_role('admin');
+}
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,87 @@
+package MyApp::Schema::Result::BookAuthor;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::BookAuthor
+
+=cut
+
+__PACKAGE__->table("book_author");
+
+=head1 ACCESSORS
+
+=head2 book_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 author_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "book_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+  "author_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("book_id", "author_id");
+
+=head1 RELATIONS
+
+=head2 author
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Author>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "author",
+  "MyApp::Schema::Result::Author",
+  { id => "author_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+=head2 book
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Book>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "book",
+  "MyApp::Schema::Result::Book",
+  { id => "book_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:YTOr2GyOa/ueirsn87uFCQ
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/Role.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/Role.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/Role.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,71 @@
+package MyApp::Schema::Result::Role;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Role
+
+=cut
+
+__PACKAGE__->table("role");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 role
+
+  data_type: 'text'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "role",
+  { data_type => "text", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 user_roles
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::UserRole>
+
+=cut
+
+__PACKAGE__->has_many(
+  "user_roles",
+  "MyApp::Schema::Result::UserRole",
+  { "foreign.role_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:AeucUX0dvANOw59CvUt4Pw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/User.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/User.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/User.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,146 @@
+package MyApp::Schema::Result::User;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::User
+
+=cut
+
+__PACKAGE__->table("users");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 username
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 password
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 email_address
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 first_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 last_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 active
+
+  data_type: 'integer'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "username",
+  { data_type => "text", is_nullable => 1 },
+  "password",
+  { data_type => "text", is_nullable => 1 },
+  "email_address",
+  { data_type => "text", is_nullable => 1 },
+  "first_name",
+  { data_type => "text", is_nullable => 1 },
+  "last_name",
+  { data_type => "text", is_nullable => 1 },
+  "active",
+  { data_type => "integer", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 user_roles
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::UserRole>
+
+=cut
+
+__PACKAGE__->has_many(
+  "user_roles",
+  "MyApp::Schema::Result::UserRole",
+  { "foreign.user_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:wVIvJZy8mps8EDY+mkUjTA
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+
+# many_to_many():
+#   args:
+#     1) Name of relationship, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(roles => 'user_roles', 'role');
+
+
+# Have the 'password' column use a SHA-1 hash and 20-byte salt
+# with RFC 2307 encoding; Generate the 'check_password" method
+__PACKAGE__->add_columns(
+    'password' => {
+        passphrase       => 'rfc2307',
+        passphrase_class => 'SaltedDigest',
+        passphrase_args  => {
+            algorithm   => 'SHA-1',
+            salt_random => 20.
+        },
+        passphrase_check_method => 'check_password',
+    },
+);
+
+
+=head2 has_role
+
+Check if a user has the specified role
+
+=cut
+
+use Perl6::Junction qw/any/;
+sub has_role {
+    my ($self, $role) = @_;
+
+    # Does this user posses the required role?
+    return any(map { $_->role } $self->roles) eq $role;
+}
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/UserRole.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/UserRole.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/Result/UserRole.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,87 @@
+package MyApp::Schema::Result::UserRole;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::UserRole
+
+=cut
+
+__PACKAGE__->table("user_role");
+
+=head1 ACCESSORS
+
+=head2 user_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 role_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "user_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+  "role_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("user_id", "role_id");
+
+=head1 RELATIONS
+
+=head2 role
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Role>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "role",
+  "MyApp::Schema::Result::Role",
+  { id => "role_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+=head2 user
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::User>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "user",
+  "MyApp::Schema::Result::User",
+  { id => "user_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:HK7ZoN6FJ7zdBAjCzqJZ8w
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/ResultSet
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/ResultSet/Book.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/ResultSet/Book.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema/ResultSet/Book.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,41 @@
+package MyApp::Schema::ResultSet::Book;
+
+use strict;
+use warnings;
+use base 'DBIx::Class::ResultSet';
+
+=head2 created_after
+
+A predefined search for recently added books
+
+=cut
+
+sub created_after {
+    my ($self, $datetime) = @_;
+
+    my $date_str = $self->result_source->schema->storage
+                          ->datetime_parser->format_datetime($datetime);
+
+    return $self->search({
+        created => { '>' => $date_str }
+    });
+}
+
+
+=head2 title_like
+
+A predefined search for books with a 'LIKE' search in the string
+
+=cut
+
+sub title_like {
+    my ($self, $title_str) = @_;
+
+    return $self->search({
+        title => { 'like' => "%$title_str%" }
+    });
+}
+
+
+1;
+

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/Schema.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,19 @@
+package MyApp::Schema;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use Moose;
+use namespace::autoclean;
+extends 'DBIx::Class::Schema';
+
+__PACKAGE__->load_namespaces;
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 18:09:34
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:0eebDopG0RPImUoVlRBhTg
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/View
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/View/HTML.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/View/HTML.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp/View/HTML.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+package MyApp::View::HTML;
+
+use strict;
+use warnings;
+
+use base 'Catalyst::View::TT';
+
+__PACKAGE__->config(
+    # Change default TT extension
+    TEMPLATE_EXTENSION => '.tt2',
+    # Set the location for TT files
+    INCLUDE_PATH => [
+            MyApp->path_to( 'root', 'src' ),
+        ],
+    # Set to 1 for detailed timer stats in your HTML as comments
+    TIMER              => 0,
+    # This is your wrapper template located in the 'root/src'
+    WRAPPER => 'wrapper.tt2',
+);
+
+
+=head1 NAME
+
+MyApp::View::HTML - TT View for MyApp
+
+=head1 DESCRIPTION
+
+TT View for MyApp.
+
+=head1 SEE ALSO
+
+L<MyApp>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/lib/MyApp.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,103 @@
+package MyApp;
+use Moose;
+use namespace::autoclean;
+
+use Catalyst::Runtime 5.80;
+
+# Set flags and add plugins for the application.
+#
+# Note that ORDERING IS IMPORTANT here as plugins are initialized in order,
+# therefore you almost certainly want to keep ConfigLoader at the head of the
+# list if you're using it.
+#
+#         -Debug: activates the debug mode for very useful log messages
+#   ConfigLoader: will load the configuration from a Config::General file in the
+#                 application's home directory
+# Static::Simple: will serve static files from the application's root
+#                 directory
+
+use Catalyst qw/
+    -Debug
+    ConfigLoader
+    Static::Simple
+
+    Authentication
+    Authorization::Roles
+    
+    Session
+    Session::Store::File
+    Session::State::Cookie
+
+    StatusMessage
+/;
+
+extends 'Catalyst';
+
+our $VERSION = '0.01';
+
+# Configure the application.
+#
+# Note that settings in myapp.conf (or other external
+# configuration file that you set up manually) take precedence
+# over this when using ConfigLoader. Thus configuration
+# details given here can function as a default configuration,
+# with an external configuration file acting as an override for
+# local deployment.
+
+__PACKAGE__->config(
+    name => 'MyApp',
+    # Disable deprecated behavior needed by old applications
+    disable_component_resolution_regex_fallback => 1,
+);
+__PACKAGE__->config(
+    # Configure the view
+    'View::HTML' => {
+        #Set the location for TT files
+        INCLUDE_PATH => [
+            __PACKAGE__->path_to( 'root', 'src' ),
+        ],
+    },
+);
+# Configure SimpleDB Authentication
+__PACKAGE__->config(
+    'Plugin::Authentication' => {
+        default => {
+            class           => 'SimpleDB',
+            user_model      => 'DB::User',
+            password_type   => 'self_check',
+        },
+    },
+);
+
+# Start the application
+__PACKAGE__->setup();
+
+
+=head1 NAME
+
+MyApp - Catalyst based application
+
+=head1 SYNOPSIS
+
+    script/myapp_server.pl
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 SEE ALSO
+
+L<MyApp::Controller::Root>, L<Catalyst>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp.conf
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp.conf	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp.conf	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,3 @@
+# rename this file to myapp.yml and put a ':' after 'name' if
+# you want to use YAML like in old versions of Catalyst
+name MyApp

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp.db
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp.db
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp.psgi
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp.psgi	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp.psgi	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+use MyApp;
+
+my $app = MyApp->apply_default_middlewares(MyApp->psgi_app);
+$app;
+

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp01.sql
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp01.sql	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp01.sql	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+--
+-- Create a very simple database to hold book and author information
+--
+PRAGMA foreign_keys = ON;
+CREATE TABLE book (
+        id          INTEGER PRIMARY KEY,
+        title       TEXT ,
+        rating      INTEGER
+);
+-- 'book_author' is a many-to-many join table between books & authors
+CREATE TABLE book_author (
+        book_id     INTEGER REFERENCES book(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        author_id   INTEGER REFERENCES author(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        PRIMARY KEY (book_id, author_id)
+);
+CREATE TABLE author (
+        id          INTEGER PRIMARY KEY,
+        first_name  TEXT,
+        last_name   TEXT
+);
+---
+--- Load some sample data
+---
+INSERT INTO book VALUES (1, 'CCSP SNRS Exam Certification Guide', 5);
+INSERT INTO book VALUES (2, 'TCP/IP Illustrated, Volume 1', 5);
+INSERT INTO book VALUES (3, 'Internetworking with TCP/IP Vol.1', 4);
+INSERT INTO book VALUES (4, 'Perl Cookbook', 5);
+INSERT INTO book VALUES (5, 'Designing with Web Standards', 5);
+INSERT INTO author VALUES (1, 'Greg', 'Bastien');
+INSERT INTO author VALUES (2, 'Sara', 'Nasseh');
+INSERT INTO author VALUES (3, 'Christian', 'Degu');
+INSERT INTO author VALUES (4, 'Richard', 'Stevens');
+INSERT INTO author VALUES (5, 'Douglas', 'Comer');
+INSERT INTO author VALUES (6, 'Tom', 'Christiansen');
+INSERT INTO author VALUES (7, 'Nathan', 'Torkington');
+INSERT INTO author VALUES (8, 'Jeffrey', 'Zeldman');
+INSERT INTO book_author VALUES (1, 1);
+INSERT INTO book_author VALUES (1, 2);
+INSERT INTO book_author VALUES (1, 3);
+INSERT INTO book_author VALUES (2, 4);
+INSERT INTO book_author VALUES (3, 5);
+INSERT INTO book_author VALUES (4, 6);
+INSERT INTO book_author VALUES (4, 7);
+INSERT INTO book_author VALUES (5, 8);
+

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp02.sql
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp02.sql	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/myapp02.sql	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,36 @@
+--
+-- Add users and role tables, along with a many-to-many join table
+--
+PRAGMA foreign_keys = ON;
+CREATE TABLE users (
+        id            INTEGER PRIMARY KEY,
+        username      TEXT,
+        password      TEXT,
+        email_address TEXT,
+        first_name    TEXT,
+        last_name     TEXT,
+        active        INTEGER
+);
+CREATE TABLE role (
+        id   INTEGER PRIMARY KEY,
+        role TEXT
+);
+CREATE TABLE user_role (
+        user_id INTEGER REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        role_id INTEGER REFERENCES role(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        PRIMARY KEY (user_id, role_id)
+);
+--
+-- Load up some initial test data
+--
+INSERT INTO users VALUES (1, 'test01', 'mypass', 't01 at na.com', 'Joe',  'Blow', 1);
+INSERT INTO users VALUES (2, 'test02', 'mypass', 't02 at na.com', 'Jane', 'Doe',  1);
+INSERT INTO users VALUES (3, 'test03', 'mypass', 't03 at na.com', 'No',   'Go',   0);
+INSERT INTO role VALUES (1, 'user');
+INSERT INTO role VALUES (2, 'admin');
+INSERT INTO user_role VALUES (1, 1);
+INSERT INTO user_role VALUES (1, 2);
+INSERT INTO user_role VALUES (2, 1);
+INSERT INTO user_role VALUES (3, 1);
+
+


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/favicon.ico
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/favicon.ico
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/books
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/books/create_done.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/books/create_done.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/books/create_done.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,32 @@
+[% # Use the TT Dumper plugin to Data::Dumper variables to the browser   -%]
+[% # Not a good idea for production use, though. :-)  'Indent=1' is      -%]
+[% # optional, but prevents "massive indenting" of deeply nested objects -%]
+[% USE Dumper(Indent=1) -%]
+
+[% # Set the page title.  META can 'go back' and set values in templates -%]
+[% # that have been processed 'before' this template (here it's updating -%]
+[% # the title in the root/src/wrapper.tt2 wrapper template).  Note that -%]
+[% # META only works on simple/static strings (i.e. there is no variable -%]
+[% # interpolation -- if you need dynamic/interpolated content in your   -%]
+[% # title, set "$c->stash(title => $something)" in the controller).     -%]
+[% META title = 'Book Created' %]
+
+[% # Output information about the record that was added.  First title.   -%]
+<p>Added book '[% book.title %]'
+
+[% # Then, output the last name of the first author -%]
+by '[% book.authors.first.last_name %]'
+
+[% # Then, output the rating for the book that was added -%]
+with a rating of [% book.rating %].</p>
+
+[% # Provide a link back to the list page.  'c.uri_for' builds -%]
+[% # a full URI; e.g., 'http://localhost:3000/books/list'      -%]
+<p><a href="[% c.uri_for('/books/list') %]">Return to list</a></p>
+
+[% # Try out the TT Dumper (for development only!) -%]
+<pre>
+Dump of the 'book' variable:
+[% Dumper.dump(book) %]
+</pre>
+

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/books/form_create.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/books/form_create.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/books/form_create.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,11 @@
+[% META title = 'Manual Form Book Create' -%]
+
+<form method="post" action="[% c.uri_for('form_create_do') %]">
+<table>
+  <tr><td>Title:</td><td><input type="text" name="title"></td></tr>
+  <tr><td>Rating:</td><td><input type="text" name="rating"></td></tr>
+  <tr><td>Author ID:</td><td><input type="text" name="author_id"></td></tr>
+</table>
+<input type="submit" name="Submit" value="Submit">
+</form>
+

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/books/list.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/books/list.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/books/list.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,55 @@
+[% # This is a TT comment. -%]
+
+[%- # Provide a title -%]
+[% META title = 'Book List' -%]
+
+[% # Note That the '-' at the beginning or end of TT code  -%]
+[% # "chomps" the whitespace/newline at that end of the    -%]
+[% # output (use View Source in browser to see the effect) -%]
+
+[% # Some basic HTML with a loop to display books -%]
+<table>
+<tr><th>Title</th><th>Rating</th><th>Author(s)</th><th>Links</th></tr>
+[% # Display each book in a table row %]
+[% FOREACH book IN books -%]
+  <tr>
+    <td>[% book.title %]</td>
+    <td>[% book.rating %]</td>
+    <td>
+      [% # Print count and author list using Result Class methods -%]
+      ([% book.author_count | html %]) [% book.author_list | html %]
+    </td>
+    <td>
+      [% # Add a link to delete a book %]
+      <a href="[%
+        c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
+    </td>
+  </tr>
+[% END -%]
+</table>
+<p>
+  <a href="[% c.uri_for('/login') %]">Login</a>
+  <a href="[% c.uri_for(c.controller.action_for('form_create')) %]">Create</a>
+</p>
+<p>Hello [% c.user.username %], you have the following roles:</p>
+
+<ul>
+  [% # Dump list of roles -%]
+  [% FOR role = c.user.roles %]<li>[% role %]</li>[% END %]
+</ul>
+
+<p>
+[% # Add some simple role-specific logic to template %]
+[% # Use $c->check_user_roles() to check authz -%]
+[% IF c.check_user_roles('user') %]
+  [% # Give normal users a link for 'logout' %]
+  <a href="[% c.uri_for('/logout') %]">User Logout</a>
+[% END %]
+
+[% # Can also use $c->user->check_roles() to check authz -%]
+[% IF c.check_user_roles('admin') %]
+  [% # Give admin users a link for 'create' %]
+  <a href="[% c.uri_for(c.controller.action_for('form_create')) %]">Admin Create</a>
+[% END %]
+</p>
+

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/error_noperms.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/error_noperms.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/error_noperms.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1 @@
+<span class="error">Permission Denied</span>

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/login.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/login.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/login.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,37 @@
+[% META title = 'Login' %]
+
+<!-- Login form -->
+<form method="post" action="[% c.uri_for('/login') %]">
+  <table>
+    <tr>
+      <td>Username:</td>
+      <td><input type="text" name="username" size="40" /></td>
+    </tr>
+    <tr>
+      <td>Password:</td>
+      <td><input type="password" name="password" size="40" /></td>
+    </tr>
+    <tr>
+      <td colspan="2"><input type="submit" name="submit" value="Submit" /></td>
+    </tr>
+  </table>
+</form>
+<p>
+[%
+   # This code illustrates how certain parts of the TT
+   # template will only be shown to users who have logged in
+%]
+[% IF c.user_exists %]
+    Please Note: You are already logged in as '[% c.user.username %]'.
+    You can <a href="[% c.uri_for('/logout') %]">logout</a> here.
+[% ELSE %]
+    You need to log in to use this application.
+[% END %]
+[%#
+   Note that this whole block is a comment because the "#" appears
+   immediate after the "[%" (with no spaces in between).  Although it
+   can be a handy way to temporarily "comment out" a whole block of
+   TT code, it's probably a little too subtle for use in "normal"
+   comments.
+%]
+</p>

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/wrapper.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/wrapper.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/root/src/wrapper.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" [%#
+    %]"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>[% template.title or "My Catalyst App!" %]</title>
+<link rel="stylesheet" href="[% c.uri_for('/static/css/main.css') %]" />
+</head>
+
+<body>
+<div id="outer">
+<div id="header">
+    [%# Your logo could go here -%]
+    <img src="[% c.uri_for('/static/images/btn_88x31_powered.png') %]" />
+    [%# Insert the page title -%]
+    <h1>[% template.title or site.title %]</h1>
+</div>
+
+<div id="bodyblock">
+<div id="menu">
+    Navigation:
+    <ul>
+        <li><a href="[% c.uri_for('/books/list') %]">Home</a></li>
+        <li><a href="[% c.uri_for('/')
+            %]" title="Catalyst Welcome Page">Welcome</a></li>
+    </ul>
+</div><!-- end menu -->
+
+<div id="content">
+    [%# Status and error messages %]
+    <span class="message">[% status_msg %]</span>
+    <span class="error">[% error_msg %]</span>
+    [%# This is where TT will stick all of your template's contents. -%]
+    [% content %]
+</div><!-- end content -->
+</div><!-- end bodyblock -->
+
+<div id="footer">Copyright (c) your name goes here</div>
+</div><!-- end outer -->
+
+</body>
+</html>
+


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/css
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/css/main.css
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/css/main.css	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/css/main.css	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,37 @@
+#header {
+    text-align: center;
+}
+#header h1 {
+    margin: 0;
+}
+#header img {
+    float: right;
+}
+#footer {
+    text-align: center;
+    font-style: italic;
+    padding-top: 20px;
+}
+#menu {
+    font-weight: bold;
+    background-color: #ddd;
+}
+#menu ul {
+    list-style: none;
+    float: left;
+    margin: 0;
+    padding: 0 0 50% 5px;
+    font-weight: normal;
+    background-color: #ddd;
+    width: 100px;
+}
+#content {
+    margin-left: 120px;
+}
+.message {
+    color: #390;
+}
+.error {
+    color: #f00;
+}
+


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_120x50_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_120x50_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_120x50_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_120x50_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_120x50_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_120x50_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_120x50_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_120x50_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_88x31_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_88x31_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_88x31_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_88x31_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_88x31_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_88x31_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_88x31_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/btn_88x31_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/catalyst_logo.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/root/static/images/catalyst_logo.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/script
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_cgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_cgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_cgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'CGI');
+
+1;
+
+=head1 NAME
+
+myapp_cgi.pl - Catalyst CGI
+
+=head1 SYNOPSIS
+
+See L<Catalyst::Manual>
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as a cgi script.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_create.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_create.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_create.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Create');
+
+1;
+
+=head1 NAME
+
+myapp_create.pl - Create a new Catalyst Component
+
+=head1 SYNOPSIS
+
+myapp_create.pl [options] model|view|controller name [helper] [options]
+
+ Options:
+   --force        don't create a .new file where a file to be created exists
+   --mechanize    use Test::WWW::Mechanize::Catalyst for tests if available
+   --help         display this help and exits
+
+ Examples:
+   myapp_create.pl controller My::Controller
+   myapp_create.pl --mechanize controller My::Controller
+   myapp_create.pl view My::View
+   myapp_create.pl view HTML TT
+   myapp_create.pl model My::Model
+   myapp_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\
+   dbi:SQLite:/tmp/my.db
+   myapp_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static\
+   [Loader opts like db_schema, naming] dbi:Pg:dbname=foo root 4321
+   [connect_info opts like quote_char, name_sep]
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+   perldoc Catalyst::Helper::Model::DBIC::Schema
+   perldoc Catalyst::Model::DBIC::Schema
+   perldoc Catalyst::View::TT
+
+=head1 DESCRIPTION
+
+Create a new Catalyst Component.
+
+Existing component files are not overwritten.  If any of the component files
+to be created already exist the file will be written with a '.new' suffix.
+This behavior can be suppressed with the C<-force> option.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_fastcgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_fastcgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_fastcgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,48 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'FastCGI');
+
+1;
+
+=head1 NAME
+
+myapp_fastcgi.pl - Catalyst FastCGI
+
+=head1 SYNOPSIS
+
+myapp_fastcgi.pl [options]
+
+ Options:
+   -? -help      display this help and exits
+   -l --listen   Socket path to listen on
+                 (defaults to standard input)
+                 can be HOST:PORT, :PORT or a
+                 filesystem path
+   -n --nproc    specify number of processes to keep
+                 to serve requests (defaults to 1,
+                 requires -listen)
+   -p --pidfile  specify filename for pid file
+                 (requires -listen)
+   -d --daemon   daemonize (requires -listen)
+   -M --manager  specify alternate process manager
+                 (FCGI::ProcManager sub-class)
+                 or empty string to disable
+   -e --keeperr  send error messages to STDOUT, not
+                 to the webserver
+   --proc_title  Set the process title (is possible)
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as fastcgi.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_server.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_server.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_server.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+BEGIN {
+    $ENV{CATALYST_SCRIPT_GEN} = 40;
+}
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Server');
+
+1;
+
+=head1 NAME
+
+myapp_server.pl - Catalyst Test Server
+
+=head1 SYNOPSIS
+
+myapp_server.pl [options]
+
+   -d --debug           force debug mode
+   -f --fork            handle each request in a new process
+                        (defaults to false)
+   -? --help            display this help and exits
+   -h --host            host (defaults to all)
+   -p --port            port (defaults to 3000)
+   -k --keepalive       enable keep-alive connections
+   -r --restart         restart when files get modified
+                        (defaults to false)
+   -rd --restart_delay  delay between file checks
+                        (ignored if you have Linux::Inotify2 installed)
+   -rr --restart_regex  regex match files that trigger
+                        a restart when modified
+                        (defaults to '\.yml$|\.yaml$|\.conf|\.pm$')
+   --restart_directory  the directory to search for
+                        modified files, can be set multiple times
+                        (defaults to '[SCRIPT_DIR]/..')
+   --follow_symlinks    follow symlinks in search directories
+                        (defaults to false. this is a no-op on Win32)
+   --background         run the process in the background
+   --pidfile            specify filename for pid file
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst Testserver for this application.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_test.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_test.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/script/myapp_test.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,40 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Test');
+
+1;
+
+=head1 NAME
+
+myapp_test.pl - Catalyst Test
+
+=head1 SYNOPSIS
+
+myapp_test.pl [options] uri
+
+ Options:
+   --help    display this help and exits
+
+ Examples:
+   myapp_test.pl http://localhost/some_action
+   myapp_test.pl /some_action
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst action from the command line.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/set_hashed_passwords.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/set_hashed_passwords.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/set_hashed_passwords.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use MyApp::Schema;
+
+my $schema = MyApp::Schema->connect('dbi:SQLite:myapp.db');
+
+my @users = $schema->resultset('User')->all;
+
+foreach my $user (@users) {
+    $user->password('mypass');
+    $user->update;
+}


Property changes on: trunk/examples/Tutorial/Final/Chapter06/MyApp/t
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/t/01app.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/t/01app.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/t/01app.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+use Catalyst::Test 'MyApp';
+
+ok( request('/')->is_success, 'Request should succeed' );
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/t/02pod.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/t/02pod.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/t/02pod.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+eval "use Test::Pod 1.14";
+plan skip_all => 'Test::Pod 1.14 required' if $@;
+
+all_pod_files_ok();

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/t/03podcoverage.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/t/03podcoverage.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/t/03podcoverage.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,14 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@;
+
+eval "use Pod::Coverage 0.20";
+plan skip_all => 'Pod::Coverage 0.20 required' if $@;
+
+all_pod_coverage_ok();

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/t/controller_Books.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/t/controller_Books.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/t/controller_Books.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Books;
+
+ok( request('/books')->is_success, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/t/controller_Login.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/t/controller_Login.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/t/controller_Login.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Login;
+
+ok( request('/login')->is_success, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/t/controller_Logout.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/t/controller_Logout.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/t/controller_Logout.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Logout;
+
+ok( request('/logout')->is_success, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/t/model_DB.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/t/model_DB.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/t/model_DB.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+BEGIN { use_ok 'MyApp::Model::DB' }
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter06/MyApp/t/view_HTML.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter06/MyApp/t/view_HTML.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter06/MyApp/t/view_HTML.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+BEGIN { use_ok 'MyApp::View::HTML' }
+
+done_testing();


Property changes on: trunk/examples/Tutorial/Final/Chapter07
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/Changes
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/Changes	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/Changes	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,4 @@
+This file documents the revision history for Perl extension MyApp.
+
+0.01  2011-08-30 17:51:40
+        - initial revision, generated by Catalyst

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/Makefile.PL
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/Makefile.PL	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/Makefile.PL	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+# IMPORTANT: if you delete this file your app will not work as
+# expected.  You have been warned.
+use inc::Module::Install;
+use Module::Install::Catalyst; # Complain loudly if you don't have
+                               # Catalyst::Devel installed or haven't said
+                               # 'make dist' to create a standalone tarball.
+
+name 'MyApp';
+all_from 'lib/MyApp.pm';
+
+requires 'Catalyst::Runtime' => '5.90002';
+requires 'Catalyst::Plugin::ConfigLoader';
+requires 'Catalyst::Plugin::Static::Simple';
+requires 'Catalyst::Action::RenderView';
+requires 'Moose';
+requires 'namespace::autoclean';
+requires 'Config::General'; # This should reflect the config file format you've chosen
+                 # See Catalyst::Plugin::ConfigLoader for supported formats
+requires 'Catalyst::Plugin::Authentication';
+requires 'Catalyst::Plugin::Session';
+requires 'Catalyst::Plugin::Session::Store::File';
+requires 'Catalyst::Plugin::Session::State::Cookie';
+requires 'Catalyst::Plugin::StatusMessage';
+requires 'Catalyst::Plugin::Authorization::Roles';
+requires 'Perl6::Junction';
+test_requires 'Test::More' => '0.88';
+catalyst;
+
+install_script glob('script/*.pl');
+auto_install;
+WriteAll;

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/README
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/README	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/README	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1 @@
+Run script/myapp_server.pl to test the application.


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Books.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Books.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Books.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,267 @@
+package MyApp::Controller::Books;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Books - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    $c->response->body('Matched MyApp::Controller::Books in Books.');
+}
+
+
+=head2 base
+
+Can place common logic to start chained dispatch here
+
+=cut
+
+sub base :Chained('/') :PathPart('books') :CaptureArgs(0) {
+    my ($self, $c) = @_;
+
+    # Store the ResultSet in stash so it's available for other methods
+    $c->stash(resultset => $c->model('DB::Book'));
+
+    # Print a message to the debug log
+    $c->log->debug('*** INSIDE BASE METHOD ***');
+
+    # Load status messages
+    $c->load_status_msgs;
+}
+
+
+=head2 list
+
+Fetch all book objects and pass to books/list.tt2 in stash to be displayed
+
+=cut
+
+sub list :Chained('base') :PathParth('list') :Args(0) {
+    # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst
+    # 'Context' that's used to 'glue together' the various components
+    # that make up the application
+    my ($self, $c) = @_;
+
+    # Retrieve all of the book records as book model objects and store
+    # in the stash where they can be accessed by the TT template
+    $c->stash(books => [$c->model('DB::Book')->all]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head2 url_create
+
+Create a book with the supplied title and rating,
+with manual authorization
+
+=cut
+
+sub url_create :Chained('base') :PathPart('url_create') :Args(3) {
+    # In addition to self & context, get the title, rating & author_id args
+    # from the URL.  Note that Catalyst automatically puts extra information
+    # after the "/<controller_name>/<action_name/" into @_
+    my ($self, $c, $title, $rating, $author_id) = @_;
+
+    # Check the user's roles
+    if ($c->check_user_roles('admin')) {
+        # Call create() on the book model object. Pass the table
+        # columns/field values we want to set as hash values
+        my $book = $c->model('DB::Book')->create({
+                title   => $title,
+                rating  => $rating
+            });
+
+        # Add a record to the join table for this book, mapping to
+        # appropriate author
+        $book->add_to_book_authors({author_id => $author_id});
+        # Note: Above is a shortcut for this:
+        # $book->create_related('book_authors', {author_id => $author_id});
+
+        # Assign the Book object to the stash and set template
+        $c->stash(book     => $book,
+                  template => 'books/create_done.tt2');
+    } else {
+        # Provide very simple feedback to the user.
+        $c->response->body('Unauthorized!');
+    }
+}
+
+
+=head2 form_create
+
+Display form to collect information for book to create
+
+=cut
+
+sub form_create :Chained('base') :PathPart('form_create') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Set the TT template to use
+    $c->stash(template => 'books/form_create.tt2');
+}
+
+
+=head2 form_create_do
+
+Take information from form and add to database
+
+=cut
+
+sub form_create_do :Chained('base') :PathPart('form_create_do') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Retrieve the values from the form
+    my $title     = $c->request->params->{title}     || 'N/A';
+    my $rating    = $c->request->params->{rating}    || 'N/A';
+    my $author_id = $c->request->params->{author_id} || '1';
+
+    # Create the book
+    my $book = $c->model('DB::Book')->create({
+            title   => $title,
+            rating  => $rating,
+        });
+    # Handle relationship with author
+    $book->add_to_book_authors({author_id => $author_id});
+    # Note: Above is a shortcut for this:
+    # $book->create_related('book_authors', {author_id => $author_id});
+
+    # Store new model object in stash and set template
+    $c->stash(book     => $book,
+              template => 'books/create_done.tt2');
+}
+
+
+=head2 object
+
+Fetch the specified book object based on the book ID and store
+it in the stash
+
+=cut
+
+sub object :Chained('base') :PathPart('id') :CaptureArgs(1) {
+    # $id = primary key of book to delete
+    my ($self, $c, $id) = @_;
+
+    # Find the book object and store it in the stash
+    $c->stash(object => $c->stash->{resultset}->find($id));
+
+    # Make sure the lookup was successful.  You would probably
+    # want to do something like this in a real app:
+    #   $c->detach('/error_404') if !$c->stash->{object};
+    die "Book $id not found!" if !$c->stash->{object};
+
+    # Print a message to the debug log
+    $c->log->debug("*** INSIDE OBJECT METHOD for obj id=$id ***");
+}
+
+
+=head2 delete
+
+Delete a book
+
+=cut
+
+sub delete :Chained('object') :PathPart('delete') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Check permissions
+    $c->detach('/error_noperms')
+        unless $c->stash->{object}->delete_allowed_by($c->user->get_object);
+
+    # Saved the PK id for status_msg below
+    my $id = $c->stash->{object}->id;
+
+    # Use the book object saved by 'object' and delete it along
+    # with related 'book_authors' entries
+    $c->stash->{object}->delete;
+
+    # Redirect the user back to the list page
+    $c->response->redirect($c->uri_for($self->action_for('list'),
+        {mid => $c->set_status_msg("Deleted book $id")}));
+}
+
+
+=head2 list_recent
+
+List recently created books
+
+=cut
+
+sub list_recent :Chained('base') :PathPart('list_recent') :Args(1) {
+    my ($self, $c, $mins) = @_;
+
+    # Retrieve all of the book records as book model objects and store in the
+    # stash where they can be accessed by the TT template, but only
+    # retrieve books created within the last $min number of minutes
+    $c->stash(books => [$c->model('DB::Book')
+                            ->created_after(DateTime->now->subtract(minutes => $mins))]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head2 list_recent_tcp
+
+List recently created books
+
+=cut
+
+sub list_recent_tcp :Chained('base') :PathPart('list_recent_tcp') :Args(1) {
+    my ($self, $c, $mins) = @_;
+
+    # Retrieve all of the book records as book model objects and store in the
+    # stash where they can be accessed by the TT template, but only
+    # retrieve books created within the last $min number of minutes
+    # AND that have 'TCP' in the title
+    $c->stash(books => [
+            $c->model('DB::Book')
+                ->created_after(DateTime->now->subtract(minutes => $mins))
+                ->title_like('TCP')
+        ]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Login.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Login.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Login.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,70 @@
+package MyApp::Controller::Login;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Login - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+Login logic
+
+=cut
+
+sub index :Path :Args(0) {
+    my ($self, $c) = @_;
+
+    # Get the username and password from form
+    my $username = $c->request->params->{username};
+    my $password = $c->request->params->{password};
+
+    # If the username and password values were found in form
+    if ($username && $password) {
+        # Attempt to log the user in
+        if ($c->authenticate({ username => $username,
+                               password => $password  } )) {
+            # If successful, then let them use the application
+            $c->response->redirect($c->uri_for(
+                $c->controller('Books')->action_for('list')));
+            return;
+        } else {
+            # Set an error message
+            $c->stash(error_msg => "Bad username or password.");
+        }
+    } else {
+        # Set an error message
+        $c->stash(error_msg => "Empty username or password.")
+            unless ($c->user_exists);
+    }
+
+    # If either of above don't work out, send to the login page
+    $c->stash(template => 'login.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Logout.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Logout.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Logout.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,50 @@
+package MyApp::Controller::Logout;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Logout - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+Logout logic
+
+=cut
+
+sub index :Path :Args(0) {
+    my ($self, $c) = @_;
+
+    # Clear the user's state
+    $c->logout;
+
+    # Send the user to the starting point
+    $c->response->redirect($c->uri_for('/'));
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Root.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Root.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Controller/Root.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,120 @@
+package MyApp::Controller::Root;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller' }
+
+#
+# Sets the actions in this controller to be registered with no prefix
+# so they function identically to actions created in MyApp.pm
+#
+__PACKAGE__->config(namespace => '');
+
+=head1 NAME
+
+MyApp::Controller::Root - Root Controller for MyApp
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 METHODS
+
+=head2 index
+
+The root page (/)
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    # Hello World
+    $c->response->body( $c->welcome_message );
+}
+
+=head2 default
+
+Standard 404 error page
+
+=cut
+
+sub default :Path {
+    my ( $self, $c ) = @_;
+    $c->response->body( 'Page not found' );
+    $c->response->status(404);
+}
+
+=head2 end
+
+Attempt to render a view, if needed.
+
+=cut
+
+sub end : ActionClass('RenderView') {}
+
+
+=head2 auto
+
+Check if there is a user and, if not, forward to login page
+
+=cut
+
+# Note that 'auto' runs after 'begin' but before your actions and that
+# 'auto's "chain" (all from application path to most specific class are run)
+# See the 'Actions' section of 'Catalyst::Manual::Intro' for more info.
+sub auto :Private {
+    my ($self, $c) = @_;
+
+    # Allow unauthenticated users to reach the login page.  This
+    # allows unauthenticated users to reach any action in the Login
+    # controller.  To lock it down to a single action, we could use:
+    #   if ($c->action eq $c->controller('Login')->action_for('index'))
+    # to only allow unauthenticated access to the 'index' action we
+    # added above.
+    if ($c->controller eq $c->controller('Login')) {
+        return 1;
+    }
+
+    # If a user doesn't exist, force login
+    if (!$c->user_exists) {
+        # Dump a log message to the development server debug output
+        $c->log->debug('***Root::auto User not found, forwarding to /login');
+        # Redirect the user to the login page
+        $c->response->redirect($c->uri_for('/login'));
+        # Return 0 to cancel 'post-auto' processing and prevent use of application
+        return 0;
+    }
+
+    # User found, so return 1 to continue with processing after this 'auto'
+    return 1;
+}
+
+
+=head2 error_noperms
+
+Permissions error screen
+
+=cut
+
+sub error_noperms :Chained('/') :PathPart('error_noperms') :Args(0) {
+    my ($self, $c) = @_;
+
+    $c->stash(template => 'error_noperms.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Model
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Model/DB.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Model/DB.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Model/DB.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,44 @@
+package MyApp::Model::DB;
+
+use strict;
+use base 'Catalyst::Model::DBIC::Schema';
+
+__PACKAGE__->config(
+    schema_class => 'MyApp::Schema',
+    
+    connect_info => {
+        dsn => 'dbi:SQLite:myapp.db',
+        user => '',
+        password => '',
+        on_connect_do => q{PRAGMA foreign_keys = ON},
+    }
+);
+
+=head1 NAME
+
+MyApp::Model::DB - Catalyst DBIC Schema Model
+
+=head1 SYNOPSIS
+
+See L<MyApp>
+
+=head1 DESCRIPTION
+
+L<Catalyst::Model::DBIC::Schema> Model using schema L<MyApp::Schema>
+
+=head1 GENERATED BY
+
+Catalyst::Helper::Model::DBIC::Schema - 0.54
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/Author.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/Author.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/Author.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,98 @@
+package MyApp::Schema::Result::Author;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Author
+
+=cut
+
+__PACKAGE__->table("author");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 first_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 last_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "first_name",
+  { data_type => "text", is_nullable => 1 },
+  "last_name",
+  { data_type => "text", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 book_authors
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::BookAuthor>
+
+=cut
+
+__PACKAGE__->has_many(
+  "book_authors",
+  "MyApp::Schema::Result::BookAuthor",
+  { "foreign.author_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:R4dWFS+inIVXQOOhTbQr6A
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+# many_to_many():
+#   args:
+#     1) Name of relationship bridge, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(books => 'book_authors', 'book');
+
+
+#
+# Row-level helper methods
+#
+sub full_name {
+    my ($self) = @_;
+
+    return $self->first_name . ' ' . $self->last_name;
+}
+
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/Book.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/Book.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/Book.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,161 @@
+package MyApp::Schema::Result::Book;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Book
+
+=cut
+
+__PACKAGE__->table("book");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 title
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 rating
+
+  data_type: 'integer'
+  is_nullable: 1
+
+=head2 created
+
+  data_type: 'timestamp'
+  is_nullable: 1
+
+=head2 updated
+
+  data_type: 'timestamp'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "title",
+  { data_type => "text", is_nullable => 1 },
+  "rating",
+  { data_type => "integer", is_nullable => 1 },
+  "created",
+  { data_type => "timestamp", is_nullable => 1 },
+  "updated",
+  { data_type => "timestamp", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 book_authors
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::BookAuthor>
+
+=cut
+
+__PACKAGE__->has_many(
+  "book_authors",
+  "MyApp::Schema::Result::BookAuthor",
+  { "foreign.book_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:siH/HgXEpB/wjvd3w9eInw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+# many_to_many():
+#   args:
+#     1) Name of relationship bridge, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(authors => 'book_authors', 'author');
+
+#
+# Enable automatic date handling
+#
+__PACKAGE__->add_columns(
+    "created",
+    { data_type => 'timestamp', set_on_create => 1 },
+    "updated",
+    { data_type => 'timestamp', set_on_create => 1, set_on_update => 1 },
+);
+
+
+=head2 author_count
+
+Return the number of authors for the current book
+
+=cut
+
+sub author_count {
+    my ($self) = @_;
+
+    # Use the 'many_to_many' relationship to fetch all of the authors for the current
+    # and the 'count' method in DBIx::Class::ResultSet to get a SQL COUNT
+    return $self->authors->count;
+}
+
+
+=head2 author_list
+
+Return a comma-separated list of authors for the current book
+
+=cut
+
+sub author_list {
+    my ($self) = @_;
+
+    # Loop through all authors for the current book, calling all the 'full_name'
+    # Result Class method for each
+    my @names;
+    foreach my $author ($self->authors) {
+        push(@names, $author->full_name);
+    }
+
+    return join(', ', @names);
+}
+
+
+
+=head2 delete_allowed_by
+
+Can the specified user delete the current book?
+
+=cut
+
+sub delete_allowed_by {
+    my ($self, $user) = @_;
+
+    # Only allow delete if user has 'admin' role
+    return $user->has_role('admin');
+}
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,87 @@
+package MyApp::Schema::Result::BookAuthor;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::BookAuthor
+
+=cut
+
+__PACKAGE__->table("book_author");
+
+=head1 ACCESSORS
+
+=head2 book_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 author_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "book_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+  "author_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("book_id", "author_id");
+
+=head1 RELATIONS
+
+=head2 author
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Author>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "author",
+  "MyApp::Schema::Result::Author",
+  { id => "author_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+=head2 book
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Book>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "book",
+  "MyApp::Schema::Result::Book",
+  { id => "book_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:YTOr2GyOa/ueirsn87uFCQ
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/Role.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/Role.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/Role.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,71 @@
+package MyApp::Schema::Result::Role;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Role
+
+=cut
+
+__PACKAGE__->table("role");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 role
+
+  data_type: 'text'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "role",
+  { data_type => "text", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 user_roles
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::UserRole>
+
+=cut
+
+__PACKAGE__->has_many(
+  "user_roles",
+  "MyApp::Schema::Result::UserRole",
+  { "foreign.role_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:AeucUX0dvANOw59CvUt4Pw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/User.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/User.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/User.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,146 @@
+package MyApp::Schema::Result::User;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::User
+
+=cut
+
+__PACKAGE__->table("users");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 username
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 password
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 email_address
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 first_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 last_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 active
+
+  data_type: 'integer'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "username",
+  { data_type => "text", is_nullable => 1 },
+  "password",
+  { data_type => "text", is_nullable => 1 },
+  "email_address",
+  { data_type => "text", is_nullable => 1 },
+  "first_name",
+  { data_type => "text", is_nullable => 1 },
+  "last_name",
+  { data_type => "text", is_nullable => 1 },
+  "active",
+  { data_type => "integer", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 user_roles
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::UserRole>
+
+=cut
+
+__PACKAGE__->has_many(
+  "user_roles",
+  "MyApp::Schema::Result::UserRole",
+  { "foreign.user_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:wVIvJZy8mps8EDY+mkUjTA
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+
+# many_to_many():
+#   args:
+#     1) Name of relationship, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(roles => 'user_roles', 'role');
+
+
+# Have the 'password' column use a SHA-1 hash and 20-byte salt
+# with RFC 2307 encoding; Generate the 'check_password" method
+__PACKAGE__->add_columns(
+    'password' => {
+        passphrase       => 'rfc2307',
+        passphrase_class => 'SaltedDigest',
+        passphrase_args  => {
+            algorithm   => 'SHA-1',
+            salt_random => 20.
+        },
+        passphrase_check_method => 'check_password',
+    },
+);
+
+
+=head2 has_role
+
+Check if a user has the specified role
+
+=cut
+
+use Perl6::Junction qw/any/;
+sub has_role {
+    my ($self, $role) = @_;
+
+    # Does this user posses the required role?
+    return any(map { $_->role } $self->roles) eq $role;
+}
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/UserRole.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/UserRole.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/Result/UserRole.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,87 @@
+package MyApp::Schema::Result::UserRole;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::UserRole
+
+=cut
+
+__PACKAGE__->table("user_role");
+
+=head1 ACCESSORS
+
+=head2 user_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 role_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "user_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+  "role_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("user_id", "role_id");
+
+=head1 RELATIONS
+
+=head2 role
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Role>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "role",
+  "MyApp::Schema::Result::Role",
+  { id => "role_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+=head2 user
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::User>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "user",
+  "MyApp::Schema::Result::User",
+  { id => "user_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:HK7ZoN6FJ7zdBAjCzqJZ8w
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/ResultSet
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/ResultSet/Book.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/ResultSet/Book.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema/ResultSet/Book.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,41 @@
+package MyApp::Schema::ResultSet::Book;
+
+use strict;
+use warnings;
+use base 'DBIx::Class::ResultSet';
+
+=head2 created_after
+
+A predefined search for recently added books
+
+=cut
+
+sub created_after {
+    my ($self, $datetime) = @_;
+
+    my $date_str = $self->result_source->schema->storage
+                          ->datetime_parser->format_datetime($datetime);
+
+    return $self->search({
+        created => { '>' => $date_str }
+    });
+}
+
+
+=head2 title_like
+
+A predefined search for books with a 'LIKE' search in the string
+
+=cut
+
+sub title_like {
+    my ($self, $title_str) = @_;
+
+    return $self->search({
+        title => { 'like' => "%$title_str%" }
+    });
+}
+
+
+1;
+

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/Schema.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,19 @@
+package MyApp::Schema;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use Moose;
+use namespace::autoclean;
+extends 'DBIx::Class::Schema';
+
+__PACKAGE__->load_namespaces;
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 18:09:34
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:0eebDopG0RPImUoVlRBhTg
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/View
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/View/HTML.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/View/HTML.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp/View/HTML.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+package MyApp::View::HTML;
+
+use strict;
+use warnings;
+
+use base 'Catalyst::View::TT';
+
+__PACKAGE__->config(
+    # Change default TT extension
+    TEMPLATE_EXTENSION => '.tt2',
+    # Set the location for TT files
+    INCLUDE_PATH => [
+            MyApp->path_to( 'root', 'src' ),
+        ],
+    # Set to 1 for detailed timer stats in your HTML as comments
+    TIMER              => 0,
+    # This is your wrapper template located in the 'root/src'
+    WRAPPER => 'wrapper.tt2',
+);
+
+
+=head1 NAME
+
+MyApp::View::HTML - TT View for MyApp
+
+=head1 DESCRIPTION
+
+TT View for MyApp.
+
+=head1 SEE ALSO
+
+L<MyApp>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/lib/MyApp.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,103 @@
+package MyApp;
+use Moose;
+use namespace::autoclean;
+
+use Catalyst::Runtime 5.80;
+
+# Set flags and add plugins for the application.
+#
+# Note that ORDERING IS IMPORTANT here as plugins are initialized in order,
+# therefore you almost certainly want to keep ConfigLoader at the head of the
+# list if you're using it.
+#
+#         -Debug: activates the debug mode for very useful log messages
+#   ConfigLoader: will load the configuration from a Config::General file in the
+#                 application's home directory
+# Static::Simple: will serve static files from the application's root
+#                 directory
+
+use Catalyst qw/
+    -Debug
+    ConfigLoader
+    Static::Simple
+
+    Authentication
+    Authorization::Roles
+    
+    Session
+    Session::Store::File
+    Session::State::Cookie
+
+    StatusMessage
+/;
+
+extends 'Catalyst';
+
+our $VERSION = '0.01';
+
+# Configure the application.
+#
+# Note that settings in myapp.conf (or other external
+# configuration file that you set up manually) take precedence
+# over this when using ConfigLoader. Thus configuration
+# details given here can function as a default configuration,
+# with an external configuration file acting as an override for
+# local deployment.
+
+__PACKAGE__->config(
+    name => 'MyApp',
+    # Disable deprecated behavior needed by old applications
+    disable_component_resolution_regex_fallback => 1,
+);
+__PACKAGE__->config(
+    # Configure the view
+    'View::HTML' => {
+        #Set the location for TT files
+        INCLUDE_PATH => [
+            __PACKAGE__->path_to( 'root', 'src' ),
+        ],
+    },
+);
+# Configure SimpleDB Authentication
+__PACKAGE__->config(
+    'Plugin::Authentication' => {
+        default => {
+            class           => 'SimpleDB',
+            user_model      => 'DB::User',
+            password_type   => 'self_check',
+        },
+    },
+);
+
+# Start the application
+__PACKAGE__->setup();
+
+
+=head1 NAME
+
+MyApp - Catalyst based application
+
+=head1 SYNOPSIS
+
+    script/myapp_server.pl
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 SEE ALSO
+
+L<MyApp::Controller::Root>, L<Catalyst>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp.conf
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp.conf	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp.conf	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,3 @@
+# rename this file to myapp.yml and put a ':' after 'name' if
+# you want to use YAML like in old versions of Catalyst
+name MyApp

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp.db
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp.db
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp.psgi
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp.psgi	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp.psgi	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+use MyApp;
+
+my $app = MyApp->apply_default_middlewares(MyApp->psgi_app);
+$app;
+

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp01.sql
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp01.sql	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp01.sql	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+--
+-- Create a very simple database to hold book and author information
+--
+PRAGMA foreign_keys = ON;
+CREATE TABLE book (
+        id          INTEGER PRIMARY KEY,
+        title       TEXT ,
+        rating      INTEGER
+);
+-- 'book_author' is a many-to-many join table between books & authors
+CREATE TABLE book_author (
+        book_id     INTEGER REFERENCES book(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        author_id   INTEGER REFERENCES author(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        PRIMARY KEY (book_id, author_id)
+);
+CREATE TABLE author (
+        id          INTEGER PRIMARY KEY,
+        first_name  TEXT,
+        last_name   TEXT
+);
+---
+--- Load some sample data
+---
+INSERT INTO book VALUES (1, 'CCSP SNRS Exam Certification Guide', 5);
+INSERT INTO book VALUES (2, 'TCP/IP Illustrated, Volume 1', 5);
+INSERT INTO book VALUES (3, 'Internetworking with TCP/IP Vol.1', 4);
+INSERT INTO book VALUES (4, 'Perl Cookbook', 5);
+INSERT INTO book VALUES (5, 'Designing with Web Standards', 5);
+INSERT INTO author VALUES (1, 'Greg', 'Bastien');
+INSERT INTO author VALUES (2, 'Sara', 'Nasseh');
+INSERT INTO author VALUES (3, 'Christian', 'Degu');
+INSERT INTO author VALUES (4, 'Richard', 'Stevens');
+INSERT INTO author VALUES (5, 'Douglas', 'Comer');
+INSERT INTO author VALUES (6, 'Tom', 'Christiansen');
+INSERT INTO author VALUES (7, 'Nathan', 'Torkington');
+INSERT INTO author VALUES (8, 'Jeffrey', 'Zeldman');
+INSERT INTO book_author VALUES (1, 1);
+INSERT INTO book_author VALUES (1, 2);
+INSERT INTO book_author VALUES (1, 3);
+INSERT INTO book_author VALUES (2, 4);
+INSERT INTO book_author VALUES (3, 5);
+INSERT INTO book_author VALUES (4, 6);
+INSERT INTO book_author VALUES (4, 7);
+INSERT INTO book_author VALUES (5, 8);
+

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp02.sql
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp02.sql	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/myapp02.sql	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,36 @@
+--
+-- Add users and role tables, along with a many-to-many join table
+--
+PRAGMA foreign_keys = ON;
+CREATE TABLE users (
+        id            INTEGER PRIMARY KEY,
+        username      TEXT,
+        password      TEXT,
+        email_address TEXT,
+        first_name    TEXT,
+        last_name     TEXT,
+        active        INTEGER
+);
+CREATE TABLE role (
+        id   INTEGER PRIMARY KEY,
+        role TEXT
+);
+CREATE TABLE user_role (
+        user_id INTEGER REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        role_id INTEGER REFERENCES role(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        PRIMARY KEY (user_id, role_id)
+);
+--
+-- Load up some initial test data
+--
+INSERT INTO users VALUES (1, 'test01', 'mypass', 't01 at na.com', 'Joe',  'Blow', 1);
+INSERT INTO users VALUES (2, 'test02', 'mypass', 't02 at na.com', 'Jane', 'Doe',  1);
+INSERT INTO users VALUES (3, 'test03', 'mypass', 't03 at na.com', 'No',   'Go',   0);
+INSERT INTO role VALUES (1, 'user');
+INSERT INTO role VALUES (2, 'admin');
+INSERT INTO user_role VALUES (1, 1);
+INSERT INTO user_role VALUES (1, 2);
+INSERT INTO user_role VALUES (2, 1);
+INSERT INTO user_role VALUES (3, 1);
+
+


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/favicon.ico
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/favicon.ico
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/books
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/books/create_done.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/books/create_done.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/books/create_done.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,32 @@
+[% # Use the TT Dumper plugin to Data::Dumper variables to the browser   -%]
+[% # Not a good idea for production use, though. :-)  'Indent=1' is      -%]
+[% # optional, but prevents "massive indenting" of deeply nested objects -%]
+[% USE Dumper(Indent=1) -%]
+
+[% # Set the page title.  META can 'go back' and set values in templates -%]
+[% # that have been processed 'before' this template (here it's updating -%]
+[% # the title in the root/src/wrapper.tt2 wrapper template).  Note that -%]
+[% # META only works on simple/static strings (i.e. there is no variable -%]
+[% # interpolation -- if you need dynamic/interpolated content in your   -%]
+[% # title, set "$c->stash(title => $something)" in the controller).     -%]
+[% META title = 'Book Created' %]
+
+[% # Output information about the record that was added.  First title.   -%]
+<p>Added book '[% book.title %]'
+
+[% # Then, output the last name of the first author -%]
+by '[% book.authors.first.last_name %]'
+
+[% # Then, output the rating for the book that was added -%]
+with a rating of [% book.rating %].</p>
+
+[% # Provide a link back to the list page.  'c.uri_for' builds -%]
+[% # a full URI; e.g., 'http://localhost:3000/books/list'      -%]
+<p><a href="[% c.uri_for('/books/list') %]">Return to list</a></p>
+
+[% # Try out the TT Dumper (for development only!) -%]
+<pre>
+Dump of the 'book' variable:
+[% Dumper.dump(book) %]
+</pre>
+

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/books/form_create.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/books/form_create.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/books/form_create.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,11 @@
+[% META title = 'Manual Form Book Create' -%]
+
+<form method="post" action="[% c.uri_for('form_create_do') %]">
+<table>
+  <tr><td>Title:</td><td><input type="text" name="title"></td></tr>
+  <tr><td>Rating:</td><td><input type="text" name="rating"></td></tr>
+  <tr><td>Author ID:</td><td><input type="text" name="author_id"></td></tr>
+</table>
+<input type="submit" name="Submit" value="Submit">
+</form>
+

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/books/list.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/books/list.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/books/list.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,55 @@
+[% # This is a TT comment. -%]
+
+[%- # Provide a title -%]
+[% META title = 'Book List' -%]
+
+[% # Note That the '-' at the beginning or end of TT code  -%]
+[% # "chomps" the whitespace/newline at that end of the    -%]
+[% # output (use View Source in browser to see the effect) -%]
+
+[% # Some basic HTML with a loop to display books -%]
+<table>
+<tr><th>Title</th><th>Rating</th><th>Author(s)</th><th>Links</th></tr>
+[% # Display each book in a table row %]
+[% FOREACH book IN books -%]
+  <tr>
+    <td>[% book.title %]</td>
+    <td>[% book.rating %]</td>
+    <td>
+      [% # Print count and author list using Result Class methods -%]
+      ([% book.author_count | html %]) [% book.author_list | html %]
+    </td>
+    <td>
+      [% # Add a link to delete a book %]
+      <a href="[%
+        c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
+    </td>
+  </tr>
+[% END -%]
+</table>
+<p>
+  <a href="[% c.uri_for('/login') %]">Login</a>
+  <a href="[% c.uri_for(c.controller.action_for('form_create')) %]">Create</a>
+</p>
+<p>Hello [% c.user.username %], you have the following roles:</p>
+
+<ul>
+  [% # Dump list of roles -%]
+  [% FOR role = c.user.roles %]<li>[% role %]</li>[% END %]
+</ul>
+
+<p>
+[% # Add some simple role-specific logic to template %]
+[% # Use $c->check_user_roles() to check authz -%]
+[% IF c.check_user_roles('user') %]
+  [% # Give normal users a link for 'logout' %]
+  <a href="[% c.uri_for('/logout') %]">User Logout</a>
+[% END %]
+
+[% # Can also use $c->user->check_roles() to check authz -%]
+[% IF c.check_user_roles('admin') %]
+  [% # Give admin users a link for 'create' %]
+  <a href="[% c.uri_for(c.controller.action_for('form_create')) %]">Admin Create</a>
+[% END %]
+</p>
+

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/error_noperms.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/error_noperms.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/error_noperms.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1 @@
+<span class="error">Permission Denied</span>

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/login.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/login.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/login.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,37 @@
+[% META title = 'Login' %]
+
+<!-- Login form -->
+<form method="post" action="[% c.uri_for('/login') %]">
+  <table>
+    <tr>
+      <td>Username:</td>
+      <td><input type="text" name="username" size="40" /></td>
+    </tr>
+    <tr>
+      <td>Password:</td>
+      <td><input type="password" name="password" size="40" /></td>
+    </tr>
+    <tr>
+      <td colspan="2"><input type="submit" name="submit" value="Submit" /></td>
+    </tr>
+  </table>
+</form>
+<p>
+[%
+   # This code illustrates how certain parts of the TT
+   # template will only be shown to users who have logged in
+%]
+[% IF c.user_exists %]
+    Please Note: You are already logged in as '[% c.user.username %]'.
+    You can <a href="[% c.uri_for('/logout') %]">logout</a> here.
+[% ELSE %]
+    You need to log in to use this application.
+[% END %]
+[%#
+   Note that this whole block is a comment because the "#" appears
+   immediate after the "[%" (with no spaces in between).  Although it
+   can be a handy way to temporarily "comment out" a whole block of
+   TT code, it's probably a little too subtle for use in "normal"
+   comments.
+%]
+</p>

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/wrapper.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/wrapper.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/root/src/wrapper.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" [%#
+    %]"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>[% template.title or "My Catalyst App!" %]</title>
+<link rel="stylesheet" href="[% c.uri_for('/static/css/main.css') %]" />
+</head>
+
+<body>
+<div id="outer">
+<div id="header">
+    [%# Your logo could go here -%]
+    <img src="[% c.uri_for('/static/images/btn_88x31_powered.png') %]" />
+    [%# Insert the page title -%]
+    <h1>[% template.title or site.title %]</h1>
+</div>
+
+<div id="bodyblock">
+<div id="menu">
+    Navigation:
+    <ul>
+        <li><a href="[% c.uri_for('/books/list') %]">Home</a></li>
+        <li><a href="[% c.uri_for('/')
+            %]" title="Catalyst Welcome Page">Welcome</a></li>
+    </ul>
+</div><!-- end menu -->
+
+<div id="content">
+    [%# Status and error messages %]
+    <span class="message">[% status_msg %]</span>
+    <span class="error">[% error_msg %]</span>
+    [%# This is where TT will stick all of your template's contents. -%]
+    [% content %]
+</div><!-- end content -->
+</div><!-- end bodyblock -->
+
+<div id="footer">Copyright (c) your name goes here</div>
+</div><!-- end outer -->
+
+</body>
+</html>
+


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/css
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/css/main.css
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/css/main.css	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/css/main.css	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,37 @@
+#header {
+    text-align: center;
+}
+#header h1 {
+    margin: 0;
+}
+#header img {
+    float: right;
+}
+#footer {
+    text-align: center;
+    font-style: italic;
+    padding-top: 20px;
+}
+#menu {
+    font-weight: bold;
+    background-color: #ddd;
+}
+#menu ul {
+    list-style: none;
+    float: left;
+    margin: 0;
+    padding: 0 0 50% 5px;
+    font-weight: normal;
+    background-color: #ddd;
+    width: 100px;
+}
+#content {
+    margin-left: 120px;
+}
+.message {
+    color: #390;
+}
+.error {
+    color: #f00;
+}
+


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_120x50_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_120x50_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_120x50_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_120x50_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_120x50_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_120x50_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_120x50_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_120x50_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_88x31_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_88x31_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_88x31_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_88x31_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_88x31_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_88x31_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_88x31_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/btn_88x31_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/catalyst_logo.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/root/static/images/catalyst_logo.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/script
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_cgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_cgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_cgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'CGI');
+
+1;
+
+=head1 NAME
+
+myapp_cgi.pl - Catalyst CGI
+
+=head1 SYNOPSIS
+
+See L<Catalyst::Manual>
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as a cgi script.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_create.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_create.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_create.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Create');
+
+1;
+
+=head1 NAME
+
+myapp_create.pl - Create a new Catalyst Component
+
+=head1 SYNOPSIS
+
+myapp_create.pl [options] model|view|controller name [helper] [options]
+
+ Options:
+   --force        don't create a .new file where a file to be created exists
+   --mechanize    use Test::WWW::Mechanize::Catalyst for tests if available
+   --help         display this help and exits
+
+ Examples:
+   myapp_create.pl controller My::Controller
+   myapp_create.pl --mechanize controller My::Controller
+   myapp_create.pl view My::View
+   myapp_create.pl view HTML TT
+   myapp_create.pl model My::Model
+   myapp_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\
+   dbi:SQLite:/tmp/my.db
+   myapp_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static\
+   [Loader opts like db_schema, naming] dbi:Pg:dbname=foo root 4321
+   [connect_info opts like quote_char, name_sep]
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+   perldoc Catalyst::Helper::Model::DBIC::Schema
+   perldoc Catalyst::Model::DBIC::Schema
+   perldoc Catalyst::View::TT
+
+=head1 DESCRIPTION
+
+Create a new Catalyst Component.
+
+Existing component files are not overwritten.  If any of the component files
+to be created already exist the file will be written with a '.new' suffix.
+This behavior can be suppressed with the C<-force> option.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_fastcgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_fastcgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_fastcgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,48 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'FastCGI');
+
+1;
+
+=head1 NAME
+
+myapp_fastcgi.pl - Catalyst FastCGI
+
+=head1 SYNOPSIS
+
+myapp_fastcgi.pl [options]
+
+ Options:
+   -? -help      display this help and exits
+   -l --listen   Socket path to listen on
+                 (defaults to standard input)
+                 can be HOST:PORT, :PORT or a
+                 filesystem path
+   -n --nproc    specify number of processes to keep
+                 to serve requests (defaults to 1,
+                 requires -listen)
+   -p --pidfile  specify filename for pid file
+                 (requires -listen)
+   -d --daemon   daemonize (requires -listen)
+   -M --manager  specify alternate process manager
+                 (FCGI::ProcManager sub-class)
+                 or empty string to disable
+   -e --keeperr  send error messages to STDOUT, not
+                 to the webserver
+   --proc_title  Set the process title (is possible)
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as fastcgi.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_server.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_server.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_server.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+BEGIN {
+    $ENV{CATALYST_SCRIPT_GEN} = 40;
+}
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Server');
+
+1;
+
+=head1 NAME
+
+myapp_server.pl - Catalyst Test Server
+
+=head1 SYNOPSIS
+
+myapp_server.pl [options]
+
+   -d --debug           force debug mode
+   -f --fork            handle each request in a new process
+                        (defaults to false)
+   -? --help            display this help and exits
+   -h --host            host (defaults to all)
+   -p --port            port (defaults to 3000)
+   -k --keepalive       enable keep-alive connections
+   -r --restart         restart when files get modified
+                        (defaults to false)
+   -rd --restart_delay  delay between file checks
+                        (ignored if you have Linux::Inotify2 installed)
+   -rr --restart_regex  regex match files that trigger
+                        a restart when modified
+                        (defaults to '\.yml$|\.yaml$|\.conf|\.pm$')
+   --restart_directory  the directory to search for
+                        modified files, can be set multiple times
+                        (defaults to '[SCRIPT_DIR]/..')
+   --follow_symlinks    follow symlinks in search directories
+                        (defaults to false. this is a no-op on Win32)
+   --background         run the process in the background
+   --pidfile            specify filename for pid file
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst Testserver for this application.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_test.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_test.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/script/myapp_test.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,40 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Test');
+
+1;
+
+=head1 NAME
+
+myapp_test.pl - Catalyst Test
+
+=head1 SYNOPSIS
+
+myapp_test.pl [options] uri
+
+ Options:
+   --help    display this help and exits
+
+ Examples:
+   myapp_test.pl http://localhost/some_action
+   myapp_test.pl /some_action
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst action from the command line.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/set_hashed_passwords.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/set_hashed_passwords.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/set_hashed_passwords.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use MyApp::Schema;
+
+my $schema = MyApp::Schema->connect('dbi:SQLite:myapp.db');
+
+my @users = $schema->resultset('User')->all;
+
+foreach my $user (@users) {
+    $user->password('mypass');
+    $user->update;
+}


Property changes on: trunk/examples/Tutorial/Final/Chapter07/MyApp/t
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/t/01app.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/t/01app.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/t/01app.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+use Catalyst::Test 'MyApp';
+
+ok( request('/')->is_success, 'Request should succeed' );
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/t/02pod.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/t/02pod.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/t/02pod.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+eval "use Test::Pod 1.14";
+plan skip_all => 'Test::Pod 1.14 required' if $@;
+
+all_pod_files_ok();

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/t/03podcoverage.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/t/03podcoverage.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/t/03podcoverage.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,14 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@;
+
+eval "use Pod::Coverage 0.20";
+plan skip_all => 'Pod::Coverage 0.20 required' if $@;
+
+all_pod_coverage_ok();

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/t/controller_Books.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/t/controller_Books.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/t/controller_Books.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Books;
+
+ok( request('/books')->is_success, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/t/controller_Login.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/t/controller_Login.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/t/controller_Login.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Login;
+
+ok( request('/login')->is_success, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/t/controller_Logout.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/t/controller_Logout.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/t/controller_Logout.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Logout;
+
+ok( request('/logout')->is_success, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/t/model_DB.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/t/model_DB.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/t/model_DB.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+BEGIN { use_ok 'MyApp::Model::DB' }
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter07/MyApp/t/view_HTML.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter07/MyApp/t/view_HTML.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter07/MyApp/t/view_HTML.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+BEGIN { use_ok 'MyApp::View::HTML' }
+
+done_testing();


Property changes on: trunk/examples/Tutorial/Final/Chapter08
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/Changes
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/Changes	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/Changes	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,4 @@
+This file documents the revision history for Perl extension MyApp.
+
+0.01  2011-08-30 17:51:40
+        - initial revision, generated by Catalyst

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/Makefile.PL
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/Makefile.PL	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/Makefile.PL	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,32 @@
+#!/usr/bin/env perl
+# IMPORTANT: if you delete this file your app will not work as
+# expected.  You have been warned.
+use inc::Module::Install;
+use Module::Install::Catalyst; # Complain loudly if you don't have
+                               # Catalyst::Devel installed or haven't said
+                               # 'make dist' to create a standalone tarball.
+
+name 'MyApp';
+all_from 'lib/MyApp.pm';
+
+requires 'Catalyst::Runtime' => '5.90002';
+requires 'Catalyst::Plugin::ConfigLoader';
+requires 'Catalyst::Plugin::Static::Simple';
+requires 'Catalyst::Action::RenderView';
+requires 'Moose';
+requires 'namespace::autoclean';
+requires 'Config::General'; # This should reflect the config file format you've chosen
+                 # See Catalyst::Plugin::ConfigLoader for supported formats
+requires 'Catalyst::Plugin::Authentication';
+requires 'Catalyst::Plugin::Session';
+requires 'Catalyst::Plugin::Session::Store::File';
+requires 'Catalyst::Plugin::Session::State::Cookie';
+requires 'Catalyst::Plugin::StatusMessage';
+requires 'Catalyst::Plugin::Authorization::Roles';
+requires 'Perl6::Junction';
+test_requires 'Test::More' => '0.88';
+catalyst;
+
+install_script glob('script/*.pl');
+auto_install;
+WriteAll;

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/README
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/README	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/README	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1 @@
+Run script/myapp_server.pl to test the application.


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Books.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Books.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Books.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,267 @@
+package MyApp::Controller::Books;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Books - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    $c->response->body('Matched MyApp::Controller::Books in Books.');
+}
+
+
+=head2 base
+
+Can place common logic to start chained dispatch here
+
+=cut
+
+sub base :Chained('/') :PathPart('books') :CaptureArgs(0) {
+    my ($self, $c) = @_;
+
+    # Store the ResultSet in stash so it's available for other methods
+    $c->stash(resultset => $c->model('DB::Book'));
+
+    # Print a message to the debug log
+    $c->log->debug('*** INSIDE BASE METHOD ***');
+
+    # Load status messages
+    $c->load_status_msgs;
+}
+
+
+=head2 list
+
+Fetch all book objects and pass to books/list.tt2 in stash to be displayed
+
+=cut
+
+sub list :Chained('base') :PathParth('list') :Args(0) {
+    # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst
+    # 'Context' that's used to 'glue together' the various components
+    # that make up the application
+    my ($self, $c) = @_;
+
+    # Retrieve all of the book records as book model objects and store
+    # in the stash where they can be accessed by the TT template
+    $c->stash(books => [$c->model('DB::Book')->all]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head2 url_create
+
+Create a book with the supplied title and rating,
+with manual authorization
+
+=cut
+
+sub url_create :Chained('base') :PathPart('url_create') :Args(3) {
+    # In addition to self & context, get the title, rating & author_id args
+    # from the URL.  Note that Catalyst automatically puts extra information
+    # after the "/<controller_name>/<action_name/" into @_
+    my ($self, $c, $title, $rating, $author_id) = @_;
+
+    # Check the user's roles
+    if ($c->check_user_roles('admin')) {
+        # Call create() on the book model object. Pass the table
+        # columns/field values we want to set as hash values
+        my $book = $c->model('DB::Book')->create({
+                title   => $title,
+                rating  => $rating
+            });
+
+        # Add a record to the join table for this book, mapping to
+        # appropriate author
+        $book->add_to_book_authors({author_id => $author_id});
+        # Note: Above is a shortcut for this:
+        # $book->create_related('book_authors', {author_id => $author_id});
+
+        # Assign the Book object to the stash and set template
+        $c->stash(book     => $book,
+                  template => 'books/create_done.tt2');
+    } else {
+        # Provide very simple feedback to the user.
+        $c->response->body('Unauthorized!');
+    }
+}
+
+
+=head2 form_create
+
+Display form to collect information for book to create
+
+=cut
+
+sub form_create :Chained('base') :PathPart('form_create') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Set the TT template to use
+    $c->stash(template => 'books/form_create.tt2');
+}
+
+
+=head2 form_create_do
+
+Take information from form and add to database
+
+=cut
+
+sub form_create_do :Chained('base') :PathPart('form_create_do') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Retrieve the values from the form
+    my $title     = $c->request->params->{title}     || 'N/A';
+    my $rating    = $c->request->params->{rating}    || 'N/A';
+    my $author_id = $c->request->params->{author_id} || '1';
+
+    # Create the book
+    my $book = $c->model('DB::Book')->create({
+            title   => $title,
+            rating  => $rating,
+        });
+    # Handle relationship with author
+    $book->add_to_book_authors({author_id => $author_id});
+    # Note: Above is a shortcut for this:
+    # $book->create_related('book_authors', {author_id => $author_id});
+
+    # Store new model object in stash and set template
+    $c->stash(book     => $book,
+              template => 'books/create_done.tt2');
+}
+
+
+=head2 object
+
+Fetch the specified book object based on the book ID and store
+it in the stash
+
+=cut
+
+sub object :Chained('base') :PathPart('id') :CaptureArgs(1) {
+    # $id = primary key of book to delete
+    my ($self, $c, $id) = @_;
+
+    # Find the book object and store it in the stash
+    $c->stash(object => $c->stash->{resultset}->find($id));
+
+    # Make sure the lookup was successful.  You would probably
+    # want to do something like this in a real app:
+    #   $c->detach('/error_404') if !$c->stash->{object};
+    die "Book $id not found!" if !$c->stash->{object};
+
+    # Print a message to the debug log
+    $c->log->debug("*** INSIDE OBJECT METHOD for obj id=$id ***");
+}
+
+
+=head2 delete
+
+Delete a book
+
+=cut
+
+sub delete :Chained('object') :PathPart('delete') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Check permissions
+    $c->detach('/error_noperms')
+        unless $c->stash->{object}->delete_allowed_by($c->user->get_object);
+
+    # Saved the PK id for status_msg below
+    my $id = $c->stash->{object}->id;
+
+    # Use the book object saved by 'object' and delete it along
+    # with related 'book_authors' entries
+    $c->stash->{object}->delete;
+
+    # Redirect the user back to the list page
+    $c->response->redirect($c->uri_for($self->action_for('list'),
+        {mid => $c->set_status_msg("Deleted book $id")}));
+}
+
+
+=head2 list_recent
+
+List recently created books
+
+=cut
+
+sub list_recent :Chained('base') :PathPart('list_recent') :Args(1) {
+    my ($self, $c, $mins) = @_;
+
+    # Retrieve all of the book records as book model objects and store in the
+    # stash where they can be accessed by the TT template, but only
+    # retrieve books created within the last $min number of minutes
+    $c->stash(books => [$c->model('DB::Book')
+                            ->created_after(DateTime->now->subtract(minutes => $mins))]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head2 list_recent_tcp
+
+List recently created books
+
+=cut
+
+sub list_recent_tcp :Chained('base') :PathPart('list_recent_tcp') :Args(1) {
+    my ($self, $c, $mins) = @_;
+
+    # Retrieve all of the book records as book model objects and store in the
+    # stash where they can be accessed by the TT template, but only
+    # retrieve books created within the last $min number of minutes
+    # AND that have 'TCP' in the title
+    $c->stash(books => [
+            $c->model('DB::Book')
+                ->created_after(DateTime->now->subtract(minutes => $mins))
+                ->title_like('TCP')
+        ]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Login.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Login.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Login.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,70 @@
+package MyApp::Controller::Login;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Login - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+Login logic
+
+=cut
+
+sub index :Path :Args(0) {
+    my ($self, $c) = @_;
+
+    # Get the username and password from form
+    my $username = $c->request->params->{username};
+    my $password = $c->request->params->{password};
+
+    # If the username and password values were found in form
+    if ($username && $password) {
+        # Attempt to log the user in
+        if ($c->authenticate({ username => $username,
+                               password => $password  } )) {
+            # If successful, then let them use the application
+            $c->response->redirect($c->uri_for(
+                $c->controller('Books')->action_for('list')));
+            return;
+        } else {
+            # Set an error message
+            $c->stash(error_msg => "Bad username or password.");
+        }
+    } else {
+        # Set an error message
+        $c->stash(error_msg => "Empty username or password.")
+            unless ($c->user_exists);
+    }
+
+    # If either of above don't work out, send to the login page
+    $c->stash(template => 'login.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Logout.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Logout.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Logout.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,50 @@
+package MyApp::Controller::Logout;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Logout - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+Logout logic
+
+=cut
+
+sub index :Path :Args(0) {
+    my ($self, $c) = @_;
+
+    # Clear the user's state
+    $c->logout;
+
+    # Send the user to the starting point
+    $c->response->redirect($c->uri_for('/'));
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Root.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Root.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Controller/Root.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,120 @@
+package MyApp::Controller::Root;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller' }
+
+#
+# Sets the actions in this controller to be registered with no prefix
+# so they function identically to actions created in MyApp.pm
+#
+__PACKAGE__->config(namespace => '');
+
+=head1 NAME
+
+MyApp::Controller::Root - Root Controller for MyApp
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 METHODS
+
+=head2 index
+
+The root page (/)
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    # Hello World
+    $c->response->body( $c->welcome_message );
+}
+
+=head2 default
+
+Standard 404 error page
+
+=cut
+
+sub default :Path {
+    my ( $self, $c ) = @_;
+    $c->response->body( 'Page not found' );
+    $c->response->status(404);
+}
+
+=head2 end
+
+Attempt to render a view, if needed.
+
+=cut
+
+sub end : ActionClass('RenderView') {}
+
+
+=head2 auto
+
+Check if there is a user and, if not, forward to login page
+
+=cut
+
+# Note that 'auto' runs after 'begin' but before your actions and that
+# 'auto's "chain" (all from application path to most specific class are run)
+# See the 'Actions' section of 'Catalyst::Manual::Intro' for more info.
+sub auto :Private {
+    my ($self, $c) = @_;
+
+    # Allow unauthenticated users to reach the login page.  This
+    # allows unauthenticated users to reach any action in the Login
+    # controller.  To lock it down to a single action, we could use:
+    #   if ($c->action eq $c->controller('Login')->action_for('index'))
+    # to only allow unauthenticated access to the 'index' action we
+    # added above.
+    if ($c->controller eq $c->controller('Login')) {
+        return 1;
+    }
+
+    # If a user doesn't exist, force login
+    if (!$c->user_exists) {
+        # Dump a log message to the development server debug output
+        $c->log->debug('***Root::auto User not found, forwarding to /login');
+        # Redirect the user to the login page
+        $c->response->redirect($c->uri_for('/login'));
+        # Return 0 to cancel 'post-auto' processing and prevent use of application
+        return 0;
+    }
+
+    # User found, so return 1 to continue with processing after this 'auto'
+    return 1;
+}
+
+
+=head2 error_noperms
+
+Permissions error screen
+
+=cut
+
+sub error_noperms :Chained('/') :PathPart('error_noperms') :Args(0) {
+    my ($self, $c) = @_;
+
+    $c->stash(template => 'error_noperms.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Model
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Model/DB.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Model/DB.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Model/DB.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+package MyApp::Model::DB;
+
+use strict;
+use base 'Catalyst::Model::DBIC::Schema';
+
+my $dsn = $ENV{MYAPP_DSN} ||= 'dbi:SQLite:myapp.db';
+__PACKAGE__->config(
+    schema_class => 'MyApp::Schema',
+
+    connect_info => {
+        dsn => $dsn,
+        user => '',
+        password => '',
+        on_connect_do => q{PRAGMA foreign_keys = ON},
+    }
+);
+
+=head1 NAME
+
+MyApp::Model::DB - Catalyst DBIC Schema Model
+
+=head1 SYNOPSIS
+
+See L<MyApp>
+
+=head1 DESCRIPTION
+
+L<Catalyst::Model::DBIC::Schema> Model using schema L<MyApp::Schema>
+
+=head1 GENERATED BY
+
+Catalyst::Helper::Model::DBIC::Schema - 0.54
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/Author.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/Author.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/Author.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,98 @@
+package MyApp::Schema::Result::Author;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Author
+
+=cut
+
+__PACKAGE__->table("author");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 first_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 last_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "first_name",
+  { data_type => "text", is_nullable => 1 },
+  "last_name",
+  { data_type => "text", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 book_authors
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::BookAuthor>
+
+=cut
+
+__PACKAGE__->has_many(
+  "book_authors",
+  "MyApp::Schema::Result::BookAuthor",
+  { "foreign.author_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:R4dWFS+inIVXQOOhTbQr6A
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+# many_to_many():
+#   args:
+#     1) Name of relationship bridge, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(books => 'book_authors', 'book');
+
+
+#
+# Row-level helper methods
+#
+sub full_name {
+    my ($self) = @_;
+
+    return $self->first_name . ' ' . $self->last_name;
+}
+
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/Book.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/Book.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/Book.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,161 @@
+package MyApp::Schema::Result::Book;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Book
+
+=cut
+
+__PACKAGE__->table("book");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 title
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 rating
+
+  data_type: 'integer'
+  is_nullable: 1
+
+=head2 created
+
+  data_type: 'timestamp'
+  is_nullable: 1
+
+=head2 updated
+
+  data_type: 'timestamp'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "title",
+  { data_type => "text", is_nullable => 1 },
+  "rating",
+  { data_type => "integer", is_nullable => 1 },
+  "created",
+  { data_type => "timestamp", is_nullable => 1 },
+  "updated",
+  { data_type => "timestamp", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 book_authors
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::BookAuthor>
+
+=cut
+
+__PACKAGE__->has_many(
+  "book_authors",
+  "MyApp::Schema::Result::BookAuthor",
+  { "foreign.book_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:siH/HgXEpB/wjvd3w9eInw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+# many_to_many():
+#   args:
+#     1) Name of relationship bridge, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(authors => 'book_authors', 'author');
+
+#
+# Enable automatic date handling
+#
+__PACKAGE__->add_columns(
+    "created",
+    { data_type => 'timestamp', set_on_create => 1 },
+    "updated",
+    { data_type => 'timestamp', set_on_create => 1, set_on_update => 1 },
+);
+
+
+=head2 author_count
+
+Return the number of authors for the current book
+
+=cut
+
+sub author_count {
+    my ($self) = @_;
+
+    # Use the 'many_to_many' relationship to fetch all of the authors for the current
+    # and the 'count' method in DBIx::Class::ResultSet to get a SQL COUNT
+    return $self->authors->count;
+}
+
+
+=head2 author_list
+
+Return a comma-separated list of authors for the current book
+
+=cut
+
+sub author_list {
+    my ($self) = @_;
+
+    # Loop through all authors for the current book, calling all the 'full_name'
+    # Result Class method for each
+    my @names;
+    foreach my $author ($self->authors) {
+        push(@names, $author->full_name);
+    }
+
+    return join(', ', @names);
+}
+
+
+
+=head2 delete_allowed_by
+
+Can the specified user delete the current book?
+
+=cut
+
+sub delete_allowed_by {
+    my ($self, $user) = @_;
+
+    # Only allow delete if user has 'admin' role
+    return $user->has_role('admin');
+}
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,87 @@
+package MyApp::Schema::Result::BookAuthor;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::BookAuthor
+
+=cut
+
+__PACKAGE__->table("book_author");
+
+=head1 ACCESSORS
+
+=head2 book_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 author_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "book_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+  "author_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("book_id", "author_id");
+
+=head1 RELATIONS
+
+=head2 author
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Author>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "author",
+  "MyApp::Schema::Result::Author",
+  { id => "author_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+=head2 book
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Book>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "book",
+  "MyApp::Schema::Result::Book",
+  { id => "book_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:YTOr2GyOa/ueirsn87uFCQ
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/Role.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/Role.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/Role.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,71 @@
+package MyApp::Schema::Result::Role;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Role
+
+=cut
+
+__PACKAGE__->table("role");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 role
+
+  data_type: 'text'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "role",
+  { data_type => "text", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 user_roles
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::UserRole>
+
+=cut
+
+__PACKAGE__->has_many(
+  "user_roles",
+  "MyApp::Schema::Result::UserRole",
+  { "foreign.role_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:AeucUX0dvANOw59CvUt4Pw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/User.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/User.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/User.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,146 @@
+package MyApp::Schema::Result::User;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::User
+
+=cut
+
+__PACKAGE__->table("users");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 username
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 password
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 email_address
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 first_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 last_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 active
+
+  data_type: 'integer'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "username",
+  { data_type => "text", is_nullable => 1 },
+  "password",
+  { data_type => "text", is_nullable => 1 },
+  "email_address",
+  { data_type => "text", is_nullable => 1 },
+  "first_name",
+  { data_type => "text", is_nullable => 1 },
+  "last_name",
+  { data_type => "text", is_nullable => 1 },
+  "active",
+  { data_type => "integer", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 user_roles
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::UserRole>
+
+=cut
+
+__PACKAGE__->has_many(
+  "user_roles",
+  "MyApp::Schema::Result::UserRole",
+  { "foreign.user_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:wVIvJZy8mps8EDY+mkUjTA
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+
+# many_to_many():
+#   args:
+#     1) Name of relationship, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(roles => 'user_roles', 'role');
+
+
+# Have the 'password' column use a SHA-1 hash and 20-byte salt
+# with RFC 2307 encoding; Generate the 'check_password" method
+__PACKAGE__->add_columns(
+    'password' => {
+        passphrase       => 'rfc2307',
+        passphrase_class => 'SaltedDigest',
+        passphrase_args  => {
+            algorithm   => 'SHA-1',
+            salt_random => 20.
+        },
+        passphrase_check_method => 'check_password',
+    },
+);
+
+
+=head2 has_role
+
+Check if a user has the specified role
+
+=cut
+
+use Perl6::Junction qw/any/;
+sub has_role {
+    my ($self, $role) = @_;
+
+    # Does this user posses the required role?
+    return any(map { $_->role } $self->roles) eq $role;
+}
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/UserRole.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/UserRole.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/Result/UserRole.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,87 @@
+package MyApp::Schema::Result::UserRole;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::UserRole
+
+=cut
+
+__PACKAGE__->table("user_role");
+
+=head1 ACCESSORS
+
+=head2 user_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 role_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "user_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+  "role_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("user_id", "role_id");
+
+=head1 RELATIONS
+
+=head2 role
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Role>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "role",
+  "MyApp::Schema::Result::Role",
+  { id => "role_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+=head2 user
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::User>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "user",
+  "MyApp::Schema::Result::User",
+  { id => "user_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:HK7ZoN6FJ7zdBAjCzqJZ8w
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/ResultSet
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/ResultSet/Book.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/ResultSet/Book.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema/ResultSet/Book.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,41 @@
+package MyApp::Schema::ResultSet::Book;
+
+use strict;
+use warnings;
+use base 'DBIx::Class::ResultSet';
+
+=head2 created_after
+
+A predefined search for recently added books
+
+=cut
+
+sub created_after {
+    my ($self, $datetime) = @_;
+
+    my $date_str = $self->result_source->schema->storage
+                          ->datetime_parser->format_datetime($datetime);
+
+    return $self->search({
+        created => { '>' => $date_str }
+    });
+}
+
+
+=head2 title_like
+
+A predefined search for books with a 'LIKE' search in the string
+
+=cut
+
+sub title_like {
+    my ($self, $title_str) = @_;
+
+    return $self->search({
+        title => { 'like' => "%$title_str%" }
+    });
+}
+
+
+1;
+

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/Schema.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,19 @@
+package MyApp::Schema;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use Moose;
+use namespace::autoclean;
+extends 'DBIx::Class::Schema';
+
+__PACKAGE__->load_namespaces;
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 18:09:34
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:0eebDopG0RPImUoVlRBhTg
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/View
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/View/HTML.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/View/HTML.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp/View/HTML.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+package MyApp::View::HTML;
+
+use strict;
+use warnings;
+
+use base 'Catalyst::View::TT';
+
+__PACKAGE__->config(
+    # Change default TT extension
+    TEMPLATE_EXTENSION => '.tt2',
+    # Set the location for TT files
+    INCLUDE_PATH => [
+            MyApp->path_to( 'root', 'src' ),
+        ],
+    # Set to 1 for detailed timer stats in your HTML as comments
+    TIMER              => 0,
+    # This is your wrapper template located in the 'root/src'
+    WRAPPER => 'wrapper.tt2',
+);
+
+
+=head1 NAME
+
+MyApp::View::HTML - TT View for MyApp
+
+=head1 DESCRIPTION
+
+TT View for MyApp.
+
+=head1 SEE ALSO
+
+L<MyApp>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/lib/MyApp.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,103 @@
+package MyApp;
+use Moose;
+use namespace::autoclean;
+
+use Catalyst::Runtime 5.80;
+
+# Set flags and add plugins for the application.
+#
+# Note that ORDERING IS IMPORTANT here as plugins are initialized in order,
+# therefore you almost certainly want to keep ConfigLoader at the head of the
+# list if you're using it.
+#
+#         -Debug: activates the debug mode for very useful log messages
+#   ConfigLoader: will load the configuration from a Config::General file in the
+#                 application's home directory
+# Static::Simple: will serve static files from the application's root
+#                 directory
+
+use Catalyst qw/
+    -Debug
+    ConfigLoader
+    Static::Simple
+
+    Authentication
+    Authorization::Roles
+    
+    Session
+    Session::Store::File
+    Session::State::Cookie
+
+    StatusMessage
+/;
+
+extends 'Catalyst';
+
+our $VERSION = '0.01';
+
+# Configure the application.
+#
+# Note that settings in myapp.conf (or other external
+# configuration file that you set up manually) take precedence
+# over this when using ConfigLoader. Thus configuration
+# details given here can function as a default configuration,
+# with an external configuration file acting as an override for
+# local deployment.
+
+__PACKAGE__->config(
+    name => 'MyApp',
+    # Disable deprecated behavior needed by old applications
+    disable_component_resolution_regex_fallback => 1,
+);
+__PACKAGE__->config(
+    # Configure the view
+    'View::HTML' => {
+        #Set the location for TT files
+        INCLUDE_PATH => [
+            __PACKAGE__->path_to( 'root', 'src' ),
+        ],
+    },
+);
+# Configure SimpleDB Authentication
+__PACKAGE__->config(
+    'Plugin::Authentication' => {
+        default => {
+            class           => 'SimpleDB',
+            user_model      => 'DB::User',
+            password_type   => 'self_check',
+        },
+    },
+);
+
+# Start the application
+__PACKAGE__->setup();
+
+
+=head1 NAME
+
+MyApp - Catalyst based application
+
+=head1 SYNOPSIS
+
+    script/myapp_server.pl
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 SEE ALSO
+
+L<MyApp::Controller::Root>, L<Catalyst>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp.conf
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp.conf	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp.conf	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,3 @@
+# rename this file to myapp.yml and put a ':' after 'name' if
+# you want to use YAML like in old versions of Catalyst
+name MyApp

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp.db
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp.db
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp.psgi
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp.psgi	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp.psgi	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+use MyApp;
+
+my $app = MyApp->apply_default_middlewares(MyApp->psgi_app);
+$app;
+

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp01.sql
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp01.sql	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp01.sql	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+--
+-- Create a very simple database to hold book and author information
+--
+PRAGMA foreign_keys = ON;
+CREATE TABLE book (
+        id          INTEGER PRIMARY KEY,
+        title       TEXT ,
+        rating      INTEGER
+);
+-- 'book_author' is a many-to-many join table between books & authors
+CREATE TABLE book_author (
+        book_id     INTEGER REFERENCES book(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        author_id   INTEGER REFERENCES author(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        PRIMARY KEY (book_id, author_id)
+);
+CREATE TABLE author (
+        id          INTEGER PRIMARY KEY,
+        first_name  TEXT,
+        last_name   TEXT
+);
+---
+--- Load some sample data
+---
+INSERT INTO book VALUES (1, 'CCSP SNRS Exam Certification Guide', 5);
+INSERT INTO book VALUES (2, 'TCP/IP Illustrated, Volume 1', 5);
+INSERT INTO book VALUES (3, 'Internetworking with TCP/IP Vol.1', 4);
+INSERT INTO book VALUES (4, 'Perl Cookbook', 5);
+INSERT INTO book VALUES (5, 'Designing with Web Standards', 5);
+INSERT INTO author VALUES (1, 'Greg', 'Bastien');
+INSERT INTO author VALUES (2, 'Sara', 'Nasseh');
+INSERT INTO author VALUES (3, 'Christian', 'Degu');
+INSERT INTO author VALUES (4, 'Richard', 'Stevens');
+INSERT INTO author VALUES (5, 'Douglas', 'Comer');
+INSERT INTO author VALUES (6, 'Tom', 'Christiansen');
+INSERT INTO author VALUES (7, 'Nathan', 'Torkington');
+INSERT INTO author VALUES (8, 'Jeffrey', 'Zeldman');
+INSERT INTO book_author VALUES (1, 1);
+INSERT INTO book_author VALUES (1, 2);
+INSERT INTO book_author VALUES (1, 3);
+INSERT INTO book_author VALUES (2, 4);
+INSERT INTO book_author VALUES (3, 5);
+INSERT INTO book_author VALUES (4, 6);
+INSERT INTO book_author VALUES (4, 7);
+INSERT INTO book_author VALUES (5, 8);
+

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp02.sql
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp02.sql	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/myapp02.sql	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,36 @@
+--
+-- Add users and role tables, along with a many-to-many join table
+--
+PRAGMA foreign_keys = ON;
+CREATE TABLE users (
+        id            INTEGER PRIMARY KEY,
+        username      TEXT,
+        password      TEXT,
+        email_address TEXT,
+        first_name    TEXT,
+        last_name     TEXT,
+        active        INTEGER
+);
+CREATE TABLE role (
+        id   INTEGER PRIMARY KEY,
+        role TEXT
+);
+CREATE TABLE user_role (
+        user_id INTEGER REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        role_id INTEGER REFERENCES role(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        PRIMARY KEY (user_id, role_id)
+);
+--
+-- Load up some initial test data
+--
+INSERT INTO users VALUES (1, 'test01', 'mypass', 't01 at na.com', 'Joe',  'Blow', 1);
+INSERT INTO users VALUES (2, 'test02', 'mypass', 't02 at na.com', 'Jane', 'Doe',  1);
+INSERT INTO users VALUES (3, 'test03', 'mypass', 't03 at na.com', 'No',   'Go',   0);
+INSERT INTO role VALUES (1, 'user');
+INSERT INTO role VALUES (2, 'admin');
+INSERT INTO user_role VALUES (1, 1);
+INSERT INTO user_role VALUES (1, 2);
+INSERT INTO user_role VALUES (2, 1);
+INSERT INTO user_role VALUES (3, 1);
+
+

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/myappTEST.db
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/myappTEST.db
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/favicon.ico
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/favicon.ico
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/books
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/books/create_done.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/books/create_done.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/books/create_done.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,32 @@
+[% # Use the TT Dumper plugin to Data::Dumper variables to the browser   -%]
+[% # Not a good idea for production use, though. :-)  'Indent=1' is      -%]
+[% # optional, but prevents "massive indenting" of deeply nested objects -%]
+[% USE Dumper(Indent=1) -%]
+
+[% # Set the page title.  META can 'go back' and set values in templates -%]
+[% # that have been processed 'before' this template (here it's updating -%]
+[% # the title in the root/src/wrapper.tt2 wrapper template).  Note that -%]
+[% # META only works on simple/static strings (i.e. there is no variable -%]
+[% # interpolation -- if you need dynamic/interpolated content in your   -%]
+[% # title, set "$c->stash(title => $something)" in the controller).     -%]
+[% META title = 'Book Created' %]
+
+[% # Output information about the record that was added.  First title.   -%]
+<p>Added book '[% book.title %]'
+
+[% # Then, output the last name of the first author -%]
+by '[% book.authors.first.last_name %]'
+
+[% # Then, output the rating for the book that was added -%]
+with a rating of [% book.rating %].</p>
+
+[% # Provide a link back to the list page.  'c.uri_for' builds -%]
+[% # a full URI; e.g., 'http://localhost:3000/books/list'      -%]
+<p><a href="[% c.uri_for('/books/list') %]">Return to list</a></p>
+
+[% # Try out the TT Dumper (for development only!) -%]
+<pre>
+Dump of the 'book' variable:
+[% Dumper.dump(book) %]
+</pre>
+

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/books/form_create.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/books/form_create.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/books/form_create.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,11 @@
+[% META title = 'Manual Form Book Create' -%]
+
+<form method="post" action="[% c.uri_for('form_create_do') %]">
+<table>
+  <tr><td>Title:</td><td><input type="text" name="title"></td></tr>
+  <tr><td>Rating:</td><td><input type="text" name="rating"></td></tr>
+  <tr><td>Author ID:</td><td><input type="text" name="author_id"></td></tr>
+</table>
+<input type="submit" name="Submit" value="Submit">
+</form>
+

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/books/list.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/books/list.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/books/list.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,55 @@
+[% # This is a TT comment. -%]
+
+[%- # Provide a title -%]
+[% META title = 'Book List' -%]
+
+[% # Note That the '-' at the beginning or end of TT code  -%]
+[% # "chomps" the whitespace/newline at that end of the    -%]
+[% # output (use View Source in browser to see the effect) -%]
+
+[% # Some basic HTML with a loop to display books -%]
+<table>
+<tr><th>Title</th><th>Rating</th><th>Author(s)</th><th>Links</th></tr>
+[% # Display each book in a table row %]
+[% FOREACH book IN books -%]
+  <tr>
+    <td>[% book.title %]</td>
+    <td>[% book.rating %]</td>
+    <td>
+      [% # Print count and author list using Result Class methods -%]
+      ([% book.author_count | html %]) [% book.author_list | html %]
+    </td>
+    <td>
+      [% # Add a link to delete a book %]
+      <a href="[%
+        c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
+    </td>
+  </tr>
+[% END -%]
+</table>
+<p>
+  <a href="[% c.uri_for('/login') %]">Login</a>
+  <a href="[% c.uri_for(c.controller.action_for('form_create')) %]">Create</a>
+</p>
+<p>Hello [% c.user.username %], you have the following roles:</p>
+
+<ul>
+  [% # Dump list of roles -%]
+  [% FOR role = c.user.roles %]<li>[% role %]</li>[% END %]
+</ul>
+
+<p>
+[% # Add some simple role-specific logic to template %]
+[% # Use $c->check_user_roles() to check authz -%]
+[% IF c.check_user_roles('user') %]
+  [% # Give normal users a link for 'logout' %]
+  <a href="[% c.uri_for('/logout') %]">User Logout</a>
+[% END %]
+
+[% # Can also use $c->user->check_roles() to check authz -%]
+[% IF c.check_user_roles('admin') %]
+  [% # Give admin users a link for 'create' %]
+  <a href="[% c.uri_for(c.controller.action_for('form_create')) %]">Admin Create</a>
+[% END %]
+</p>
+

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/error_noperms.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/error_noperms.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/error_noperms.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1 @@
+<span class="error">Permission Denied</span>

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/login.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/login.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/login.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,37 @@
+[% META title = 'Login' %]
+
+<!-- Login form -->
+<form method="post" action="[% c.uri_for('/login') %]">
+  <table>
+    <tr>
+      <td>Username:</td>
+      <td><input type="text" name="username" size="40" /></td>
+    </tr>
+    <tr>
+      <td>Password:</td>
+      <td><input type="password" name="password" size="40" /></td>
+    </tr>
+    <tr>
+      <td colspan="2"><input type="submit" name="submit" value="Submit" /></td>
+    </tr>
+  </table>
+</form>
+<p>
+[%
+   # This code illustrates how certain parts of the TT
+   # template will only be shown to users who have logged in
+%]
+[% IF c.user_exists %]
+    Please Note: You are already logged in as '[% c.user.username %]'.
+    You can <a href="[% c.uri_for('/logout') %]">logout</a> here.
+[% ELSE %]
+    You need to log in to use this application.
+[% END %]
+[%#
+   Note that this whole block is a comment because the "#" appears
+   immediate after the "[%" (with no spaces in between).  Although it
+   can be a handy way to temporarily "comment out" a whole block of
+   TT code, it's probably a little too subtle for use in "normal"
+   comments.
+%]
+</p>

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/wrapper.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/wrapper.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/root/src/wrapper.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" [%#
+    %]"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>[% template.title or "My Catalyst App!" %]</title>
+<link rel="stylesheet" href="[% c.uri_for('/static/css/main.css') %]" />
+</head>
+
+<body>
+<div id="outer">
+<div id="header">
+    [%# Your logo could go here -%]
+    <img src="[% c.uri_for('/static/images/btn_88x31_powered.png') %]" />
+    [%# Insert the page title -%]
+    <h1>[% template.title or site.title %]</h1>
+</div>
+
+<div id="bodyblock">
+<div id="menu">
+    Navigation:
+    <ul>
+        <li><a href="[% c.uri_for('/books/list') %]">Home</a></li>
+        <li><a href="[% c.uri_for('/')
+            %]" title="Catalyst Welcome Page">Welcome</a></li>
+    </ul>
+</div><!-- end menu -->
+
+<div id="content">
+    [%# Status and error messages %]
+    <span class="message">[% status_msg %]</span>
+    <span class="error">[% error_msg %]</span>
+    [%# This is where TT will stick all of your template's contents. -%]
+    [% content %]
+</div><!-- end content -->
+</div><!-- end bodyblock -->
+
+<div id="footer">Copyright (c) your name goes here</div>
+</div><!-- end outer -->
+
+</body>
+</html>
+


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/css
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/css/main.css
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/css/main.css	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/css/main.css	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,37 @@
+#header {
+    text-align: center;
+}
+#header h1 {
+    margin: 0;
+}
+#header img {
+    float: right;
+}
+#footer {
+    text-align: center;
+    font-style: italic;
+    padding-top: 20px;
+}
+#menu {
+    font-weight: bold;
+    background-color: #ddd;
+}
+#menu ul {
+    list-style: none;
+    float: left;
+    margin: 0;
+    padding: 0 0 50% 5px;
+    font-weight: normal;
+    background-color: #ddd;
+    width: 100px;
+}
+#content {
+    margin-left: 120px;
+}
+.message {
+    color: #390;
+}
+.error {
+    color: #f00;
+}
+


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_120x50_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_120x50_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_120x50_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_120x50_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_120x50_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_120x50_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_120x50_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_120x50_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_88x31_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_88x31_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_88x31_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_88x31_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_88x31_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_88x31_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_88x31_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/btn_88x31_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/catalyst_logo.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/root/static/images/catalyst_logo.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/script
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_cgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_cgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_cgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'CGI');
+
+1;
+
+=head1 NAME
+
+myapp_cgi.pl - Catalyst CGI
+
+=head1 SYNOPSIS
+
+See L<Catalyst::Manual>
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as a cgi script.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_create.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_create.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_create.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Create');
+
+1;
+
+=head1 NAME
+
+myapp_create.pl - Create a new Catalyst Component
+
+=head1 SYNOPSIS
+
+myapp_create.pl [options] model|view|controller name [helper] [options]
+
+ Options:
+   --force        don't create a .new file where a file to be created exists
+   --mechanize    use Test::WWW::Mechanize::Catalyst for tests if available
+   --help         display this help and exits
+
+ Examples:
+   myapp_create.pl controller My::Controller
+   myapp_create.pl --mechanize controller My::Controller
+   myapp_create.pl view My::View
+   myapp_create.pl view HTML TT
+   myapp_create.pl model My::Model
+   myapp_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\
+   dbi:SQLite:/tmp/my.db
+   myapp_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static\
+   [Loader opts like db_schema, naming] dbi:Pg:dbname=foo root 4321
+   [connect_info opts like quote_char, name_sep]
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+   perldoc Catalyst::Helper::Model::DBIC::Schema
+   perldoc Catalyst::Model::DBIC::Schema
+   perldoc Catalyst::View::TT
+
+=head1 DESCRIPTION
+
+Create a new Catalyst Component.
+
+Existing component files are not overwritten.  If any of the component files
+to be created already exist the file will be written with a '.new' suffix.
+This behavior can be suppressed with the C<-force> option.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_fastcgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_fastcgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_fastcgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,48 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'FastCGI');
+
+1;
+
+=head1 NAME
+
+myapp_fastcgi.pl - Catalyst FastCGI
+
+=head1 SYNOPSIS
+
+myapp_fastcgi.pl [options]
+
+ Options:
+   -? -help      display this help and exits
+   -l --listen   Socket path to listen on
+                 (defaults to standard input)
+                 can be HOST:PORT, :PORT or a
+                 filesystem path
+   -n --nproc    specify number of processes to keep
+                 to serve requests (defaults to 1,
+                 requires -listen)
+   -p --pidfile  specify filename for pid file
+                 (requires -listen)
+   -d --daemon   daemonize (requires -listen)
+   -M --manager  specify alternate process manager
+                 (FCGI::ProcManager sub-class)
+                 or empty string to disable
+   -e --keeperr  send error messages to STDOUT, not
+                 to the webserver
+   --proc_title  Set the process title (is possible)
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as fastcgi.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_server.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_server.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_server.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+BEGIN {
+    $ENV{CATALYST_SCRIPT_GEN} = 40;
+}
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Server');
+
+1;
+
+=head1 NAME
+
+myapp_server.pl - Catalyst Test Server
+
+=head1 SYNOPSIS
+
+myapp_server.pl [options]
+
+   -d --debug           force debug mode
+   -f --fork            handle each request in a new process
+                        (defaults to false)
+   -? --help            display this help and exits
+   -h --host            host (defaults to all)
+   -p --port            port (defaults to 3000)
+   -k --keepalive       enable keep-alive connections
+   -r --restart         restart when files get modified
+                        (defaults to false)
+   -rd --restart_delay  delay between file checks
+                        (ignored if you have Linux::Inotify2 installed)
+   -rr --restart_regex  regex match files that trigger
+                        a restart when modified
+                        (defaults to '\.yml$|\.yaml$|\.conf|\.pm$')
+   --restart_directory  the directory to search for
+                        modified files, can be set multiple times
+                        (defaults to '[SCRIPT_DIR]/..')
+   --follow_symlinks    follow symlinks in search directories
+                        (defaults to false. this is a no-op on Win32)
+   --background         run the process in the background
+   --pidfile            specify filename for pid file
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst Testserver for this application.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_test.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_test.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/script/myapp_test.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,40 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Test');
+
+1;
+
+=head1 NAME
+
+myapp_test.pl - Catalyst Test
+
+=head1 SYNOPSIS
+
+myapp_test.pl [options] uri
+
+ Options:
+   --help    display this help and exits
+
+ Examples:
+   myapp_test.pl http://localhost/some_action
+   myapp_test.pl /some_action
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst action from the command line.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/set_hashed_passwords.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/set_hashed_passwords.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/set_hashed_passwords.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use MyApp::Schema;
+
+my $schema = MyApp::Schema->connect('dbi:SQLite:myapp.db');
+
+my @users = $schema->resultset('User')->all;
+
+foreach my $user (@users) {
+    $user->password('mypass');
+    $user->update;
+}


Property changes on: trunk/examples/Tutorial/Final/Chapter08/MyApp/t
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/t/01app.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/t/01app.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/t/01app.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+use Catalyst::Test 'MyApp';
+
+ok( request('/login')->is_success, 'Request should succeed' );
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/t/02pod.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/t/02pod.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/t/02pod.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+eval "use Test::Pod 1.14";
+plan skip_all => 'Test::Pod 1.14 required' if $@;
+
+all_pod_files_ok();

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/t/03podcoverage.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/t/03podcoverage.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/t/03podcoverage.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,14 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@;
+
+eval "use Pod::Coverage 0.20";
+plan skip_all => 'Pod::Coverage 0.20 required' if $@;
+
+all_pod_coverage_ok();

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/t/controller_Books.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/t/controller_Books.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/t/controller_Books.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Books;
+
+ok( request('/books')->is_redirect, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/t/controller_Login.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/t/controller_Login.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/t/controller_Login.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Login;
+
+ok( request('/login')->is_success, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/t/controller_Logout.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/t/controller_Logout.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/t/controller_Logout.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Logout;
+
+ok( request('/logout')->is_redirect, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/t/live_app01.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/t/live_app01.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/t/live_app01.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,101 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use Test::More;
+
+# Need to specify the name of your app as arg on next line
+# Can also do:
+#   use Test::WWW::Mechanize::Catalyst "MyApp";
+
+BEGIN { use_ok("Test::WWW::Mechanize::Catalyst" => "MyApp") }
+
+# Create two 'user agents' to simulate two different users ('test01' & 'test02')
+my $ua1 = Test::WWW::Mechanize::Catalyst->new;
+my $ua2 = Test::WWW::Mechanize::Catalyst->new;
+
+# Use a simplified for loop to do tests that are common to both users
+# Use get_ok() to make sure we can hit the base URL
+# Second arg = optional description of test (will be displayed for failed tests)
+# Note that in test scripts you send everything to 'http://localhost'
+$_->get_ok("http://localhost/", "Check redirect of base URL") for $ua1, $ua2;
+# Use title_is() to check the contents of the <title>...</title> tags
+$_->title_is("Login", "Check for login title") for $ua1, $ua2;
+# Use content_contains() to match on text in the html body
+$_->content_contains("You need to log in to use this application",
+    "Check we are NOT logged in") for $ua1, $ua2;
+
+# Log in as each user
+# Specify username and password on the URL
+$ua1->get_ok("http://localhost/login?username=test01&password=mypass", "Login 'test01'");
+# Could make user2 like user1 above, but use the form to show another way
+$ua2->submit_form(
+    fields => {
+        username => 'test02',
+        password => 'mypass',
+    });
+
+# Go back to the login page and it should show that we are already logged in
+$_->get_ok("http://localhost/login", "Return to '/login'") for $ua1, $ua2;
+$_->title_is("Login", "Check for login page") for $ua1, $ua2;
+$_->content_contains("Please Note: You are already logged in as ",
+    "Check we ARE logged in" ) for $ua1, $ua2;
+
+# 'Click' the 'Logout' link (see also 'text_regex' and 'url_regex' options)
+$_->follow_link_ok({n => 4}, "Logout via first link on page") for $ua1, $ua2;
+$_->title_is("Login", "Check for login title") for $ua1, $ua2;
+$_->content_contains("You need to log in to use this application",
+    "Check we are NOT logged in") for $ua1, $ua2;
+
+# Log back in
+$ua1->get_ok("http://localhost/login?username=test01&password=mypass", "Login 'test01'");
+$ua2->get_ok("http://localhost/login?username=test02&password=mypass", "Login 'test02'");
+# Should be at the Book List page... do some checks to confirm
+$_->title_is("Book List", "Check for book list title") for $ua1, $ua2;
+
+$ua1->get_ok("http://localhost/books/list", "'test01' book list");
+$ua1->get_ok("http://localhost/login", "Login Page");
+$ua1->get_ok("http://localhost/books/list", "'test01' book list");
+
+$_->content_contains("Book List", "Check for book list title") for $ua1, $ua2;
+# Make sure the appropriate logout buttons are displayed
+$_->content_contains("/logout\">User Logout</a>",
+    "Both users should have a 'User Logout'") for $ua1, $ua2;
+$ua1->content_contains("/books/form_create\">Admin Create</a>",
+    "'test01' should have a create link");
+$ua2->content_lacks("/books/form_create\">Admin Create</a>",
+    "'test02' should NOT have a create link");
+
+$ua1->get_ok("http://localhost/books/list", "View book list as 'test01'");
+
+# User 'test01' should be able to create a book with the "formless create" URL
+$ua1->get_ok("http://localhost/books/url_create/TestTitle/2/4",
+    "'test01' formless create");
+$ua1->title_is("Book Created", "Book created title");
+$ua1->content_contains("Added book 'TestTitle'", "Check title added OK");
+$ua1->content_contains("by 'Stevens'", "Check author added OK");
+$ua1->content_contains("with a rating of 2.", "Check rating added");
+# Try a regular expression to combine the previous 3 checks & account for whitespace
+$ua1->content_like(qr/Added book 'TestTitle'\s+by 'Stevens'\s+with a rating of 2./, "Regex check");
+
+# Make sure the new book shows in the list
+$ua1->get_ok("http://localhost/books/list", "'test01' book list");
+$ua1->title_is("Book List", "Check logged in and at book list");
+$ua1->content_contains("Book List", "Book List page test");
+$ua1->content_contains("TestTitle", "Look for 'TestTitle'");
+
+# Make sure the new book can be deleted
+# Get all the Delete links on the list page
+my @delLinks = $ua1->find_all_links(text => 'Delete');
+# Use the final link to delete the last book
+$ua1->get_ok($delLinks[$#delLinks]->url, 'Delete last book');
+# Check that delete worked
+$ua1->content_contains("Book List", "Book List page test");
+$ua1->content_like(qr/Deleted book \d+/, "Deleted book #");
+
+# User 'test02' should not be able to add a book
+$ua2->get_ok("http://localhost/books/url_create/TestTitle2/2/5", "'test02' add");
+$ua2->content_contains("Unauthorized!", "Check 'test02' cannot add");
+
+done_testing;
+

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/t/model_DB.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/t/model_DB.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/t/model_DB.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+BEGIN { use_ok 'MyApp::Model::DB' }
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter08/MyApp/t/view_HTML.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter08/MyApp/t/view_HTML.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter08/MyApp/t/view_HTML.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,9 @@
+use strict;
+use warnings;
+use Test::More;
+
+use MyApp;
+
+BEGIN { use_ok 'MyApp::View::HTML' }
+
+done_testing();


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/Changes
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/Changes	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/Changes	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,4 @@
+This file documents the revision history for Perl extension MyApp.
+
+0.01  2011-08-30 17:51:40
+        - initial revision, generated by Catalyst

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/Makefile.PL
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/Makefile.PL	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/Makefile.PL	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,42 @@
+#!/usr/bin/env perl
+# IMPORTANT: if you delete this file your app will not work as
+# expected.  You have been warned.
+use inc::Module::Install;
+use Module::Install::Catalyst; # Complain loudly if you don't have
+                               # Catalyst::Devel installed or haven't said
+                               # 'make dist' to create a standalone tarball.
+
+name 'MyApp';
+all_from 'lib/MyApp.pm';
+
+requires 'Catalyst::Runtime' => '5.90002';
+requires 'Catalyst::Plugin::ConfigLoader';
+requires 'Catalyst::Plugin::Static::Simple';
+requires 'Catalyst::Action::RenderView';
+requires 'Moose';
+requires 'namespace::autoclean';
+requires 'Config::General'; # This should reflect the config file format you've chosen
+                 # See Catalyst::Plugin::ConfigLoader for supported formats
+requires 'Catalyst::Plugin::StackTrace';
+requires 'Catalyst::Plugin::Authentication';
+requires 'Catalyst::Plugin::Session';
+requires 'Catalyst::Plugin::Session::Store::File';
+requires 'Catalyst::Plugin::Session::State::Cookie';
+requires 'Catalyst::Plugin::StatusMessage';
+requires 'Catalyst::Plugin::Authorization::Roles';
+requires 'Catalyst::Authentication::Store::DBIx::Class';
+requires 'DBIx::Class::PassphraseColumn';
+requires 'Sub::Identify';
+requires 'DBIx::Class::Schema::Loader';
+requires 'DBIx::Class::TimeStamp';
+requires 'MooseX::NonMoose';
+requires 'Perl6::Junction';
+requires 'HTML::FormFu';
+requires 'HTML::FormFu::Model::DBIC';
+requires 'Catalyst::Controller::HTML::FormFu';
+test_requires 'Test::More' => '0.88';
+catalyst;
+
+install_script glob('script/*.pl');
+auto_install;
+WriteAll;

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/README
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/README	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/README	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1 @@
+Run script/myapp_server.pl to test the application.


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Books.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Books.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Books.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,366 @@
+package MyApp::Controller::Books;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller::HTML::FormFu'; }
+
+=head1 NAME
+
+MyApp::Controller::Books - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    $c->response->body('Matched MyApp::Controller::Books in Books.');
+}
+
+
+=head2 base
+
+Can place common logic to start chained dispatch here
+
+=cut
+
+sub base :Chained('/') :PathPart('books') :CaptureArgs(0) {
+    my ($self, $c) = @_;
+
+    # Store the ResultSet in stash so it's available for other methods
+    $c->stash(resultset => $c->model('DB::Book'));
+
+    # Print a message to the debug log
+    $c->log->debug('*** INSIDE BASE METHOD ***');
+
+    # Load status messages
+    $c->load_status_msgs;
+}
+
+
+=head2 list
+
+Fetch all book objects and pass to books/list.tt2 in stash to be displayed
+
+=cut
+
+sub list :Chained('base') :PathParth('list') :Args(0) {
+    # Retrieve the usual Perl OO '$self' for this object. $c is the Catalyst
+    # 'Context' that's used to 'glue together' the various components
+    # that make up the application
+    my ($self, $c) = @_;
+
+    # Retrieve all of the book records as book model objects and store
+    # in the stash where they can be accessed by the TT template
+    $c->stash(books => [$c->model('DB::Book')->all]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head2 url_create
+
+Create a book with the supplied title and rating,
+with manual authorization
+
+=cut
+
+sub url_create :Chained('base') :PathPart('url_create') :Args(3) {
+    # In addition to self & context, get the title, rating & author_id args
+    # from the URL.  Note that Catalyst automatically puts extra information
+    # after the "/<controller_name>/<action_name/" into @_
+    my ($self, $c, $title, $rating, $author_id) = @_;
+
+    # Check the user's roles
+    if ($c->check_user_roles('admin')) {
+        # Call create() on the book model object. Pass the table
+        # columns/field values we want to set as hash values
+        my $book = $c->model('DB::Book')->create({
+                title   => $title,
+                rating  => $rating
+            });
+
+        # Add a record to the join table for this book, mapping to
+        # appropriate author
+        $book->add_to_book_authors({author_id => $author_id});
+        # Note: Above is a shortcut for this:
+        # $book->create_related('book_authors', {author_id => $author_id});
+
+        # Assign the Book object to the stash and set template
+        $c->stash(book     => $book,
+                  template => 'books/create_done.tt2');
+    } else {
+        # Provide very simple feedback to the user.
+        $c->response->body('Unauthorized!');
+    }
+}
+
+
+=head2 form_create
+
+Display form to collect information for book to create
+
+=cut
+
+sub form_create :Chained('base') :PathPart('form_create') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Set the TT template to use
+    $c->stash(template => 'books/form_create.tt2');
+}
+
+
+=head2 form_create_do
+
+Take information from form and add to database
+
+=cut
+
+sub form_create_do :Chained('base') :PathPart('form_create_do') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Retrieve the values from the form
+    my $title     = $c->request->params->{title}     || 'N/A';
+    my $rating    = $c->request->params->{rating}    || 'N/A';
+    my $author_id = $c->request->params->{author_id} || '1';
+
+    # Create the book
+    my $book = $c->model('DB::Book')->create({
+            title   => $title,
+            rating  => $rating,
+        });
+    # Handle relationship with author
+    $book->add_to_book_authors({author_id => $author_id});
+    # Note: Above is a shortcut for this:
+    # $book->create_related('book_authors', {author_id => $author_id});
+
+    # Store new model object in stash and set template
+    $c->stash(book     => $book,
+              template => 'books/create_done.tt2');
+}
+
+
+=head2 object
+
+Fetch the specified book object based on the book ID and store
+it in the stash
+
+=cut
+
+sub object :Chained('base') :PathPart('id') :CaptureArgs(1) {
+    # $id = primary key of book to delete
+    my ($self, $c, $id) = @_;
+
+    # Find the book object and store it in the stash
+    $c->stash(object => $c->stash->{resultset}->find($id));
+
+    # Make sure the lookup was successful.  You would probably
+    # want to do something like this in a real app:
+    #   $c->detach('/error_404') if !$c->stash->{object};
+    die "Book $id not found!" if !$c->stash->{object};
+
+    # Print a message to the debug log
+    $c->log->debug("*** INSIDE OBJECT METHOD for obj id=$id ***");
+}
+
+
+=head2 delete
+
+Delete a book
+
+=cut
+
+sub delete :Chained('object') :PathPart('delete') :Args(0) {
+    my ($self, $c) = @_;
+
+    # Check permissions
+    $c->detach('/error_noperms')
+        unless $c->stash->{object}->delete_allowed_by($c->user->get_object);
+
+    # Saved the PK id for status_msg below
+    my $id = $c->stash->{object}->id;
+
+    # Use the book object saved by 'object' and delete it along
+    # with related 'book_authors' entries
+    $c->stash->{object}->delete;
+
+    # Redirect the user back to the list page
+    $c->response->redirect($c->uri_for($self->action_for('list'),
+        {mid => $c->set_status_msg("Deleted book $id")}));
+}
+
+
+=head2 list_recent
+
+List recently created books
+
+=cut
+
+sub list_recent :Chained('base') :PathPart('list_recent') :Args(1) {
+    my ($self, $c, $mins) = @_;
+
+    # Retrieve all of the book records as book model objects and store in the
+    # stash where they can be accessed by the TT template, but only
+    # retrieve books created within the last $min number of minutes
+    $c->stash(books => [$c->model('DB::Book')
+                            ->created_after(DateTime->now->subtract(minutes => $mins))]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head2 list_recent_tcp
+
+List recently created books
+
+=cut
+
+sub list_recent_tcp :Chained('base') :PathPart('list_recent_tcp') :Args(1) {
+    my ($self, $c, $mins) = @_;
+
+    # Retrieve all of the book records as book model objects and store in the
+    # stash where they can be accessed by the TT template, but only
+    # retrieve books created within the last $min number of minutes
+    # AND that have 'TCP' in the title
+    $c->stash(books => [
+            $c->model('DB::Book')
+                ->created_after(DateTime->now->subtract(minutes => $mins))
+                ->title_like('TCP')
+        ]);
+
+    # Set the TT template to use.  You will almost always want to do this
+    # in your action methods (action methods respond to user input in
+    # your controllers).
+    $c->stash(template => 'books/list.tt2');
+}
+
+
+=head2 formfu_create
+
+Use HTML::FormFu to create a new book
+
+=cut
+
+sub formfu_create :Chained('base') :PathPart('formfu_create') :Args(0) :FormConfig {
+    my ($self, $c) = @_;
+
+    # Get the form that the :FormConfig attribute saved in the stash
+    my $form = $c->stash->{form};
+
+    # Check if the form has been submitted (vs. displaying the initial
+    # form) and if the data passed validation.  "submitted_and_valid"
+    # is shorthand for "$form->submitted && !$form->has_errors"
+    if ($form->submitted_and_valid) {
+        # Create a new book
+        my $book = $c->model('DB::Book')->new_result({});
+        # Save the form data for the book
+        $form->model->update($book);
+        # Set a status message for the user & return to books list
+        $c->response->redirect($c->uri_for($self->action_for('list'),
+            {mid => $c->set_status_msg("Book created")}));
+        $c->detach;
+    } else {
+        # Get the authors from the DB
+        my @author_objs = $c->model("DB::Author")->all();
+        # Create an array of arrayrefs where each arrayref is an author
+        my @authors;
+        foreach (sort {$a->last_name cmp $b->last_name} @author_objs) {
+            push(@authors, [$_->id, $_->last_name]);
+        }
+        # Get the select added by the config file
+        my $select = $form->get_element({type => 'Select'});
+        # Add the authors to it
+        $select->options(\@authors);
+    }
+
+    # Set the template
+    $c->stash(template => 'books/formfu_create.tt2');
+}
+
+
+=head2 formfu_edit
+
+Use HTML::FormFu to update an existing book
+
+=cut
+
+sub formfu_edit :Chained('object') :PathPart('formfu_edit') :Args(0)
+        :FormConfig('books/formfu_create.yml') {
+    my ($self, $c) = @_;
+
+    # Get the specified book already saved by the 'object' method
+    my $book = $c->stash->{object};
+
+    # Make sure we were able to get a book
+    unless ($book) {
+        # Set an error message for the user & return to books list
+        $c->response->redirect($c->uri_for($self->action_for('list'),
+            {mid => $c->set_error_msg("Invalid book -- Cannot edit")}));
+        $c->detach;
+    }
+
+    # Get the form that the :FormConfig attribute saved in the stash
+    my $form = $c->stash->{form};
+
+    # Check if the form has been submitted (vs. displaying the initial
+    # form) and if the data passed validation.  "submitted_and_valid"
+    # is shorthand for "$form->submitted && !$form->has_errors"
+    if ($form->submitted_and_valid) {
+        # Save the form data for the book
+        $form->model->update($book);
+        # Set a status message for the user
+        # Set a status message for the user & return to books list
+        $c->response->redirect($c->uri_for($self->action_for('list'),
+            {mid => $c->set_status_msg("Book edited")}));
+        $c->detach;
+    } else {
+        # Get the authors from the DB
+        my @author_objs = $c->model("DB::Author")->all();
+        # Create an array of arrayrefs where each arrayref is an author
+        my @authors;
+        foreach (sort {$a->last_name cmp $b->last_name} @author_objs) {
+            push(@authors, [$_->id, $_->last_name]);
+        }
+        # Get the select added by the config file
+        my $select = $form->get_element({type => 'Select'});
+        # Add the authors to it
+        $select->options(\@authors);
+        # Populate the form with existing values from DB
+        $form->model->default_values($book);
+    }
+
+    # Set the template
+    $c->stash(template => 'books/formfu_create.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Login.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Login.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Login.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,70 @@
+package MyApp::Controller::Login;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Login - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+Login logic
+
+=cut
+
+sub index :Path :Args(0) {
+    my ($self, $c) = @_;
+
+    # Get the username and password from form
+    my $username = $c->request->params->{username};
+    my $password = $c->request->params->{password};
+
+    # If the username and password values were found in form
+    if ($username && $password) {
+        # Attempt to log the user in
+        if ($c->authenticate({ username => $username,
+                               password => $password  } )) {
+            # If successful, then let them use the application
+            $c->response->redirect($c->uri_for(
+                $c->controller('Books')->action_for('list')));
+            return;
+        } else {
+            # Set an error message
+            $c->stash(error_msg => "Bad username or password.");
+        }
+    } else {
+        # Set an error message
+        $c->stash(error_msg => "Empty username or password.")
+            unless ($c->user_exists);
+    }
+
+    # If either of above don't work out, send to the login page
+    $c->stash(template => 'login.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Logout.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Logout.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Logout.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,50 @@
+package MyApp::Controller::Logout;
+use Moose;
+use namespace::autoclean;
+
+BEGIN {extends 'Catalyst::Controller'; }
+
+=head1 NAME
+
+MyApp::Controller::Logout - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index
+
+Logout logic
+
+=cut
+
+sub index :Path :Args(0) {
+    my ($self, $c) = @_;
+
+    # Clear the user's state
+    $c->logout;
+
+    # Send the user to the starting point
+    $c->response->redirect($c->uri_for('/'));
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Root.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Root.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Controller/Root.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,120 @@
+package MyApp::Controller::Root;
+use Moose;
+use namespace::autoclean;
+
+BEGIN { extends 'Catalyst::Controller' }
+
+#
+# Sets the actions in this controller to be registered with no prefix
+# so they function identically to actions created in MyApp.pm
+#
+__PACKAGE__->config(namespace => '');
+
+=head1 NAME
+
+MyApp::Controller::Root - Root Controller for MyApp
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 METHODS
+
+=head2 index
+
+The root page (/)
+
+=cut
+
+sub index :Path :Args(0) {
+    my ( $self, $c ) = @_;
+
+    # Hello World
+    $c->response->body( $c->welcome_message );
+}
+
+=head2 default
+
+Standard 404 error page
+
+=cut
+
+sub default :Path {
+    my ( $self, $c ) = @_;
+    $c->response->body( 'Page not found' );
+    $c->response->status(404);
+}
+
+=head2 end
+
+Attempt to render a view, if needed.
+
+=cut
+
+sub end : ActionClass('RenderView') {}
+
+
+=head2 auto
+
+Check if there is a user and, if not, forward to login page
+
+=cut
+
+# Note that 'auto' runs after 'begin' but before your actions and that
+# 'auto's "chain" (all from application path to most specific class are run)
+# See the 'Actions' section of 'Catalyst::Manual::Intro' for more info.
+sub auto :Private {
+    my ($self, $c) = @_;
+
+    # Allow unauthenticated users to reach the login page.  This
+    # allows unauthenticated users to reach any action in the Login
+    # controller.  To lock it down to a single action, we could use:
+    #   if ($c->action eq $c->controller('Login')->action_for('index'))
+    # to only allow unauthenticated access to the 'index' action we
+    # added above.
+    if ($c->controller eq $c->controller('Login')) {
+        return 1;
+    }
+
+    # If a user doesn't exist, force login
+    if (!$c->user_exists) {
+        # Dump a log message to the development server debug output
+        $c->log->debug('***Root::auto User not found, forwarding to /login');
+        # Redirect the user to the login page
+        $c->response->redirect($c->uri_for('/login'));
+        # Return 0 to cancel 'post-auto' processing and prevent use of application
+        return 0;
+    }
+
+    # User found, so return 1 to continue with processing after this 'auto'
+    return 1;
+}
+
+
+=head2 error_noperms
+
+Permissions error screen
+
+=cut
+
+sub error_noperms :Chained('/') :PathPart('error_noperms') :Args(0) {
+    my ($self, $c) = @_;
+
+    $c->stash(template => 'error_noperms.tt2');
+}
+
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+__PACKAGE__->meta->make_immutable;
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Model
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Model/DB.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Model/DB.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Model/DB.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+package MyApp::Model::DB;
+
+use strict;
+use base 'Catalyst::Model::DBIC::Schema';
+
+my $dsn = $ENV{MYAPP_DSN} ||= 'dbi:SQLite:myapp.db';
+__PACKAGE__->config(
+    schema_class => 'MyApp::Schema',
+
+    connect_info => {
+        dsn => $dsn,
+        user => '',
+        password => '',
+        on_connect_do => q{PRAGMA foreign_keys = ON},
+    }
+);
+
+=head1 NAME
+
+MyApp::Model::DB - Catalyst DBIC Schema Model
+
+=head1 SYNOPSIS
+
+See L<MyApp>
+
+=head1 DESCRIPTION
+
+L<Catalyst::Model::DBIC::Schema> Model using schema L<MyApp::Schema>
+
+=head1 GENERATED BY
+
+Catalyst::Helper::Model::DBIC::Schema - 0.54
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/Author.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/Author.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/Author.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,98 @@
+package MyApp::Schema::Result::Author;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Author
+
+=cut
+
+__PACKAGE__->table("author");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 first_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 last_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "first_name",
+  { data_type => "text", is_nullable => 1 },
+  "last_name",
+  { data_type => "text", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 book_authors
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::BookAuthor>
+
+=cut
+
+__PACKAGE__->has_many(
+  "book_authors",
+  "MyApp::Schema::Result::BookAuthor",
+  { "foreign.author_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:R4dWFS+inIVXQOOhTbQr6A
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+# many_to_many():
+#   args:
+#     1) Name of relationship bridge, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(books => 'book_authors', 'book');
+
+
+#
+# Row-level helper methods
+#
+sub full_name {
+    my ($self) = @_;
+
+    return $self->first_name . ' ' . $self->last_name;
+}
+
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/Book.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/Book.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/Book.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,161 @@
+package MyApp::Schema::Result::Book;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Book
+
+=cut
+
+__PACKAGE__->table("book");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 title
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 rating
+
+  data_type: 'integer'
+  is_nullable: 1
+
+=head2 created
+
+  data_type: 'timestamp'
+  is_nullable: 1
+
+=head2 updated
+
+  data_type: 'timestamp'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "title",
+  { data_type => "text", is_nullable => 1 },
+  "rating",
+  { data_type => "integer", is_nullable => 1 },
+  "created",
+  { data_type => "timestamp", is_nullable => 1 },
+  "updated",
+  { data_type => "timestamp", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 book_authors
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::BookAuthor>
+
+=cut
+
+__PACKAGE__->has_many(
+  "book_authors",
+  "MyApp::Schema::Result::BookAuthor",
+  { "foreign.book_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:siH/HgXEpB/wjvd3w9eInw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+# many_to_many():
+#   args:
+#     1) Name of relationship bridge, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(authors => 'book_authors', 'author');
+
+#
+# Enable automatic date handling
+#
+__PACKAGE__->add_columns(
+    "created",
+    { data_type => 'timestamp', set_on_create => 1 },
+    "updated",
+    { data_type => 'timestamp', set_on_create => 1, set_on_update => 1 },
+);
+
+
+=head2 author_count
+
+Return the number of authors for the current book
+
+=cut
+
+sub author_count {
+    my ($self) = @_;
+
+    # Use the 'many_to_many' relationship to fetch all of the authors for the current
+    # and the 'count' method in DBIx::Class::ResultSet to get a SQL COUNT
+    return $self->authors->count;
+}
+
+
+=head2 author_list
+
+Return a comma-separated list of authors for the current book
+
+=cut
+
+sub author_list {
+    my ($self) = @_;
+
+    # Loop through all authors for the current book, calling all the 'full_name'
+    # Result Class method for each
+    my @names;
+    foreach my $author ($self->authors) {
+        push(@names, $author->full_name);
+    }
+
+    return join(', ', @names);
+}
+
+
+
+=head2 delete_allowed_by
+
+Can the specified user delete the current book?
+
+=cut
+
+sub delete_allowed_by {
+    my ($self, $user) = @_;
+
+    # Only allow delete if user has 'admin' role
+    return $user->has_role('admin');
+}
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/BookAuthor.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,87 @@
+package MyApp::Schema::Result::BookAuthor;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::BookAuthor
+
+=cut
+
+__PACKAGE__->table("book_author");
+
+=head1 ACCESSORS
+
+=head2 book_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 author_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "book_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+  "author_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("book_id", "author_id");
+
+=head1 RELATIONS
+
+=head2 author
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Author>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "author",
+  "MyApp::Schema::Result::Author",
+  { id => "author_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+=head2 book
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Book>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "book",
+  "MyApp::Schema::Result::Book",
+  { id => "book_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:YTOr2GyOa/ueirsn87uFCQ
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/Role.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/Role.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/Role.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,71 @@
+package MyApp::Schema::Result::Role;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::Role
+
+=cut
+
+__PACKAGE__->table("role");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 role
+
+  data_type: 'text'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "role",
+  { data_type => "text", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 user_roles
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::UserRole>
+
+=cut
+
+__PACKAGE__->has_many(
+  "user_roles",
+  "MyApp::Schema::Result::UserRole",
+  { "foreign.role_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:AeucUX0dvANOw59CvUt4Pw
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/User.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/User.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/User.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,146 @@
+package MyApp::Schema::Result::User;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::User
+
+=cut
+
+__PACKAGE__->table("users");
+
+=head1 ACCESSORS
+
+=head2 id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 username
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 password
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 email_address
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 first_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 last_name
+
+  data_type: 'text'
+  is_nullable: 1
+
+=head2 active
+
+  data_type: 'integer'
+  is_nullable: 1
+
+=cut
+
+__PACKAGE__->add_columns(
+  "id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "username",
+  { data_type => "text", is_nullable => 1 },
+  "password",
+  { data_type => "text", is_nullable => 1 },
+  "email_address",
+  { data_type => "text", is_nullable => 1 },
+  "first_name",
+  { data_type => "text", is_nullable => 1 },
+  "last_name",
+  { data_type => "text", is_nullable => 1 },
+  "active",
+  { data_type => "integer", is_nullable => 1 },
+);
+__PACKAGE__->set_primary_key("id");
+
+=head1 RELATIONS
+
+=head2 user_roles
+
+Type: has_many
+
+Related object: L<MyApp::Schema::Result::UserRole>
+
+=cut
+
+__PACKAGE__->has_many(
+  "user_roles",
+  "MyApp::Schema::Result::UserRole",
+  { "foreign.user_id" => "self.id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:wVIvJZy8mps8EDY+mkUjTA
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+
+
+# many_to_many():
+#   args:
+#     1) Name of relationship, DBIC will create accessor with this name
+#     2) Name of has_many() relationship this many_to_many() is shortcut for
+#     3) Name of belongs_to() relationship in model class of has_many() above
+#   You must already have the has_many() defined to use a many_to_many().
+__PACKAGE__->many_to_many(roles => 'user_roles', 'role');
+
+
+# Have the 'password' column use a SHA-1 hash and 20-byte salt
+# with RFC 2307 encoding; Generate the 'check_password" method
+__PACKAGE__->add_columns(
+    'password' => {
+        passphrase       => 'rfc2307',
+        passphrase_class => 'SaltedDigest',
+        passphrase_args  => {
+            algorithm   => 'SHA-1',
+            salt_random => 20.
+        },
+        passphrase_check_method => 'check_password',
+    },
+);
+
+
+=head2 has_role
+
+Check if a user has the specified role
+
+=cut
+
+use Perl6::Junction qw/any/;
+sub has_role {
+    my ($self, $role) = @_;
+
+    # Does this user posses the required role?
+    return any(map { $_->role } $self->roles) eq $role;
+}
+
+__PACKAGE__->meta->make_immutable;
+1;

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/UserRole.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/UserRole.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/Result/UserRole.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,87 @@
+package MyApp::Schema::Result::UserRole;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use Moose;
+use MooseX::NonMoose;
+use namespace::autoclean;
+extends 'DBIx::Class::Core';
+
+__PACKAGE__->load_components("InflateColumn::DateTime", "TimeStamp", "PassphraseColumn");
+
+=head1 NAME
+
+MyApp::Schema::Result::UserRole
+
+=cut
+
+__PACKAGE__->table("user_role");
+
+=head1 ACCESSORS
+
+=head2 user_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 role_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "user_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+  "role_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("user_id", "role_id");
+
+=head1 RELATIONS
+
+=head2 role
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::Role>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "role",
+  "MyApp::Schema::Result::Role",
+  { id => "role_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+=head2 user
+
+Type: belongs_to
+
+Related object: L<MyApp::Schema::Result::User>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "user",
+  "MyApp::Schema::Result::User",
+  { id => "user_id" },
+  { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 21:32:06
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:HK7ZoN6FJ7zdBAjCzqJZ8w
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable;
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/ResultSet
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/ResultSet/Book.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/ResultSet/Book.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema/ResultSet/Book.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,41 @@
+package MyApp::Schema::ResultSet::Book;
+
+use strict;
+use warnings;
+use base 'DBIx::Class::ResultSet';
+
+=head2 created_after
+
+A predefined search for recently added books
+
+=cut
+
+sub created_after {
+    my ($self, $datetime) = @_;
+
+    my $date_str = $self->result_source->schema->storage
+                          ->datetime_parser->format_datetime($datetime);
+
+    return $self->search({
+        created => { '>' => $date_str }
+    });
+}
+
+
+=head2 title_like
+
+A predefined search for books with a 'LIKE' search in the string
+
+=cut
+
+sub title_like {
+    my ($self, $title_str) = @_;
+
+    return $self->search({
+        title => { 'like' => "%$title_str%" }
+    });
+}
+
+
+1;
+

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/Schema.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,19 @@
+package MyApp::Schema;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use Moose;
+use namespace::autoclean;
+extends 'DBIx::Class::Schema';
+
+__PACKAGE__->load_namespaces;
+
+
+# Created by DBIx::Class::Schema::Loader v0.07010 @ 2011-08-30 18:09:34
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:0eebDopG0RPImUoVlRBhTg
+
+
+# You can replace this text with custom code or comments, and it will be preserved on regeneration
+__PACKAGE__->meta->make_immutable(inline_constructor => 0);
+1;


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/View
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/View/HTML.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/View/HTML.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp/View/HTML.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+package MyApp::View::HTML;
+
+use strict;
+use warnings;
+
+use base 'Catalyst::View::TT';
+
+__PACKAGE__->config(
+    # Change default TT extension
+    TEMPLATE_EXTENSION => '.tt2',
+    # Set the location for TT files
+    INCLUDE_PATH => [
+            MyApp->path_to( 'root', 'src' ),
+        ],
+    # Set to 1 for detailed timer stats in your HTML as comments
+    TIMER              => 0,
+    # This is your wrapper template located in the 'root/src'
+    WRAPPER => 'wrapper.tt2',
+);
+
+
+=head1 NAME
+
+MyApp::View::HTML - TT View for MyApp
+
+=head1 DESCRIPTION
+
+TT View for MyApp.
+
+=head1 SEE ALSO
+
+L<MyApp>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp.pm
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp.pm	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/lib/MyApp.pm	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,105 @@
+package MyApp;
+use Moose;
+use namespace::autoclean;
+
+use Catalyst::Runtime 5.80;
+
+# Set flags and add plugins for the application.
+#
+# Note that ORDERING IS IMPORTANT here as plugins are initialized in order,
+# therefore you almost certainly want to keep ConfigLoader at the head of the
+# list if you're using it.
+#
+#         -Debug: activates the debug mode for very useful log messages
+#   ConfigLoader: will load the configuration from a Config::General file in the
+#                 application's home directory
+# Static::Simple: will serve static files from the application's root
+#                 directory
+
+use Catalyst qw/
+    -Debug
+    ConfigLoader
+    Static::Simple
+
+    StackTrace
+
+    Authentication
+    Authorization::Roles
+    
+    Session
+    Session::Store::File
+    Session::State::Cookie
+
+    StatusMessage
+/;
+
+extends 'Catalyst';
+
+our $VERSION = '0.01';
+
+# Configure the application.
+#
+# Note that settings in myapp.conf (or other external
+# configuration file that you set up manually) take precedence
+# over this when using ConfigLoader. Thus configuration
+# details given here can function as a default configuration,
+# with an external configuration file acting as an override for
+# local deployment.
+
+__PACKAGE__->config(
+    name => 'MyApp',
+    # Disable deprecated behavior needed by old applications
+    disable_component_resolution_regex_fallback => 1,
+);
+__PACKAGE__->config(
+    # Configure the view
+    'View::HTML' => {
+        #Set the location for TT files
+        INCLUDE_PATH => [
+            __PACKAGE__->path_to( 'root', 'src' ),
+        ],
+    },
+);
+# Configure SimpleDB Authentication
+__PACKAGE__->config(
+    'Plugin::Authentication' => {
+        default => {
+            class           => 'SimpleDB',
+            user_model      => 'DB::User',
+            password_type   => 'self_check',
+        },
+    },
+);
+
+# Start the application
+__PACKAGE__->setup();
+
+
+=head1 NAME
+
+MyApp - Catalyst based application
+
+=head1 SYNOPSIS
+
+    script/myapp_server.pl
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 SEE ALSO
+
+L<MyApp::Controller::Root>, L<Catalyst>
+
+=head1 AUTHOR
+
+root
+
+=head1 LICENSE
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+1;

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp.conf
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp.conf	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp.conf	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,3 @@
+# rename this file to myapp.yml and put a ':' after 'name' if
+# you want to use YAML like in old versions of Catalyst
+name MyApp

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp.db
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp.db
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp.psgi
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp.psgi	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp.psgi	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+
+use MyApp;
+
+my $app = MyApp->apply_default_middlewares(MyApp->psgi_app);
+$app;
+

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp01.sql
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp01.sql	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp01.sql	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,45 @@
+--
+-- Create a very simple database to hold book and author information
+--
+PRAGMA foreign_keys = ON;
+CREATE TABLE book (
+        id          INTEGER PRIMARY KEY,
+        title       TEXT ,
+        rating      INTEGER
+);
+-- 'book_author' is a many-to-many join table between books & authors
+CREATE TABLE book_author (
+        book_id     INTEGER REFERENCES book(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        author_id   INTEGER REFERENCES author(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        PRIMARY KEY (book_id, author_id)
+);
+CREATE TABLE author (
+        id          INTEGER PRIMARY KEY,
+        first_name  TEXT,
+        last_name   TEXT
+);
+---
+--- Load some sample data
+---
+INSERT INTO book VALUES (1, 'CCSP SNRS Exam Certification Guide', 5);
+INSERT INTO book VALUES (2, 'TCP/IP Illustrated, Volume 1', 5);
+INSERT INTO book VALUES (3, 'Internetworking with TCP/IP Vol.1', 4);
+INSERT INTO book VALUES (4, 'Perl Cookbook', 5);
+INSERT INTO book VALUES (5, 'Designing with Web Standards', 5);
+INSERT INTO author VALUES (1, 'Greg', 'Bastien');
+INSERT INTO author VALUES (2, 'Sara', 'Nasseh');
+INSERT INTO author VALUES (3, 'Christian', 'Degu');
+INSERT INTO author VALUES (4, 'Richard', 'Stevens');
+INSERT INTO author VALUES (5, 'Douglas', 'Comer');
+INSERT INTO author VALUES (6, 'Tom', 'Christiansen');
+INSERT INTO author VALUES (7, 'Nathan', 'Torkington');
+INSERT INTO author VALUES (8, 'Jeffrey', 'Zeldman');
+INSERT INTO book_author VALUES (1, 1);
+INSERT INTO book_author VALUES (1, 2);
+INSERT INTO book_author VALUES (1, 3);
+INSERT INTO book_author VALUES (2, 4);
+INSERT INTO book_author VALUES (3, 5);
+INSERT INTO book_author VALUES (4, 6);
+INSERT INTO book_author VALUES (4, 7);
+INSERT INTO book_author VALUES (5, 8);
+

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp02.sql
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp02.sql	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myapp02.sql	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,36 @@
+--
+-- Add users and role tables, along with a many-to-many join table
+--
+PRAGMA foreign_keys = ON;
+CREATE TABLE users (
+        id            INTEGER PRIMARY KEY,
+        username      TEXT,
+        password      TEXT,
+        email_address TEXT,
+        first_name    TEXT,
+        last_name     TEXT,
+        active        INTEGER
+);
+CREATE TABLE role (
+        id   INTEGER PRIMARY KEY,
+        role TEXT
+);
+CREATE TABLE user_role (
+        user_id INTEGER REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        role_id INTEGER REFERENCES role(id) ON DELETE CASCADE ON UPDATE CASCADE,
+        PRIMARY KEY (user_id, role_id)
+);
+--
+-- Load up some initial test data
+--
+INSERT INTO users VALUES (1, 'test01', 'mypass', 't01 at na.com', 'Joe',  'Blow', 1);
+INSERT INTO users VALUES (2, 'test02', 'mypass', 't02 at na.com', 'Jane', 'Doe',  1);
+INSERT INTO users VALUES (3, 'test03', 'mypass', 't03 at na.com', 'No',   'Go',   0);
+INSERT INTO role VALUES (1, 'user');
+INSERT INTO role VALUES (2, 'admin');
+INSERT INTO user_role VALUES (1, 1);
+INSERT INTO user_role VALUES (1, 2);
+INSERT INTO user_role VALUES (2, 1);
+INSERT INTO user_role VALUES (3, 1);
+
+

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myappTEST.db
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/myappTEST.db
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/favicon.ico
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/favicon.ico
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/forms
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/forms/books
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/forms/books/formfu_create.yml
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/forms/books/formfu_create.yml	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/forms/books/formfu_create.yml	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,83 @@
+---
+# indicator is the field that is used to test for form submission
+indicator: submit
+# Start listing the form elements
+elements:
+    # The first element will be a text field for the title
+    - type: Text
+      name: title
+      label: Title
+      # This is an optional 'mouse over' title pop-up
+      attributes:
+        title: Enter a book title here
+      # Add constraints for the field
+      constraints:
+        # Force the length to be between 5 and 40 chars
+        - type: Length
+          min: 5
+          max: 40
+          # Override the default of 'Invalid input'
+          message: Length must be between 5 and 40 characters
+
+    # Another text field for the numeric rating
+    - type: Text
+      name: rating
+      label: Rating
+      attributes:
+        title: Enter a rating between 1 and 5 here
+      # Use Filter to clean up the input data
+      # Could use 'NonNumeric' below, but since Filters apply *before*
+      # constraints, it would conflict with the 'Integer' constraint below.
+      # So let's skip this and just use the constraint.
+      #filter:
+        # Remove everything except digits
+        #- NonNumeric
+      # Add constraints to the field
+      constraints:
+        # Make sure it's a number
+        - type: Integer
+          message: "Required. Digits only, please."
+        # Check the min & max values
+        - type: Range
+          min: 1
+          max: 5
+          message: "Must be between 1 and 5."
+
+    # Add a select list for the author selection.  Note that we will
+    # dynamically fill in all the authors from the controller but we
+    # could manually set items in the select by adding this YAML code:
+    # options:
+    #   - [ '1', 'Bastien' ]
+    #   - [ '2', 'Nasseh'  ]
+    - type: Select
+      name: authors
+      label: Author
+      # Convert the drop-down to a multi-select list
+      multiple: 1
+      # Display 3 entries (user can scroll to see others)
+      size: 3
+      # One could argue we don't need to do filters or constraints for
+      # a select list, but it's smart to do validation and sanity
+      # checks on this data in case a user "hacks" the input
+      # Add constraints to the field
+      constraints:
+        # Make sure it's a number
+        - Integer
+
+    # The submit button
+    - type: Submit
+      name: submit
+      value: Submit
+
+# Global filters and constraints.
+constraints:
+    # The user cannot leave any fields blank
+    - Required
+    # If not all fields are required, move the Required constraint to the
+    # fields that are
+filter:
+    # Remove whitespace at both ends
+    - TrimEdges
+    # Escape HTML characters for safety
+    - HTMLEscape
+


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/create_done.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/create_done.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/create_done.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,32 @@
+[% # Use the TT Dumper plugin to Data::Dumper variables to the browser   -%]
+[% # Not a good idea for production use, though. :-)  'Indent=1' is      -%]
+[% # optional, but prevents "massive indenting" of deeply nested objects -%]
+[% USE Dumper(Indent=1) -%]
+
+[% # Set the page title.  META can 'go back' and set values in templates -%]
+[% # that have been processed 'before' this template (here it's updating -%]
+[% # the title in the root/src/wrapper.tt2 wrapper template).  Note that -%]
+[% # META only works on simple/static strings (i.e. there is no variable -%]
+[% # interpolation -- if you need dynamic/interpolated content in your   -%]
+[% # title, set "$c->stash(title => $something)" in the controller).     -%]
+[% META title = 'Book Created' %]
+
+[% # Output information about the record that was added.  First title.   -%]
+<p>Added book '[% book.title %]'
+
+[% # Then, output the last name of the first author -%]
+by '[% book.authors.first.last_name %]'
+
+[% # Then, output the rating for the book that was added -%]
+with a rating of [% book.rating %].</p>
+
+[% # Provide a link back to the list page.  'c.uri_for' builds -%]
+[% # a full URI; e.g., 'http://localhost:3000/books/list'      -%]
+<p><a href="[% c.uri_for('/books/list') %]">Return to list</a></p>
+
+[% # Try out the TT Dumper (for development only!) -%]
+<pre>
+Dump of the 'book' variable:
+[% Dumper.dump(book) %]
+</pre>
+

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/form_create.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/form_create.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/form_create.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,11 @@
+[% META title = 'Manual Form Book Create' -%]
+
+<form method="post" action="[% c.uri_for('form_create_do') %]">
+<table>
+  <tr><td>Title:</td><td><input type="text" name="title"></td></tr>
+  <tr><td>Rating:</td><td><input type="text" name="rating"></td></tr>
+  <tr><td>Author ID:</td><td><input type="text" name="author_id"></td></tr>
+</table>
+<input type="submit" name="Submit" value="Submit">
+</form>
+

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/formfu_create.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/formfu_create.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/formfu_create.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+[% META title = 'Create/Update Book' %]
+
+[%# Render the HTML::FormFu Form %]
+[% form %]
+
+<p><a href="[% c.uri_for(c.controller.action_for('list'))
+    %]">Return to book list</a></p>
+

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/list.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/list.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/books/list.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,62 @@
+[% # This is a TT comment. -%]
+
+[%- # Provide a title -%]
+[% META title = 'Book List' -%]
+
+[% # Note That the '-' at the beginning or end of TT code  -%]
+[% # "chomps" the whitespace/newline at that end of the    -%]
+[% # output (use View Source in browser to see the effect) -%]
+
+[% # Some basic HTML with a loop to display books -%]
+<table>
+<tr><th>Title</th><th>Rating</th><th>Author(s)</th><th>Links</th></tr>
+[% # Display each book in a table row %]
+[% FOREACH book IN books -%]
+  <tr>
+    <td>[% book.title %]</td>
+    <td>[% book.rating %]</td>
+    <td>
+      [% # Print count and author list using Result Class methods -%]
+      ([% book.author_count | html %]) [% book.author_list | html %]
+    </td>
+    <td>
+      [% # Add a link to delete a book %]
+      <a href="[%
+        c.uri_for(c.controller.action_for('delete'), [book.id]) %]">Delete</a>
+      [% # Add a link to edit a book %]
+      <a href="[%
+        c.uri_for(c.controller.action_for('formfu_edit'), [book.id]) %]">Edit</a>
+    </td>
+  </tr>
+[% END -%]
+</table>
+<p>
+  <a href="[% c.uri_for('/login') %]">Login</a>
+  <a href="[% c.uri_for(c.controller.action_for('form_create')) %]">Create</a>
+</p>
+<p>Hello [% c.user.username %], you have the following roles:</p>
+
+<ul>
+  [% # Dump list of roles -%]
+  [% FOR role = c.user.roles %]<li>[% role %]</li>[% END %]
+</ul>
+
+<p>
+[% # Add some simple role-specific logic to template %]
+[% # Use $c->check_user_roles() to check authz -%]
+[% IF c.check_user_roles('user') %]
+  [% # Give normal users a link for 'logout' %]
+  <a href="[% c.uri_for('/logout') %]">User Logout</a>
+[% END %]
+
+[% # Can also use $c->user->check_roles() to check authz -%]
+[% IF c.check_user_roles('admin') %]
+  [% # Give admin users a link for 'create' %]
+  <a href="[% c.uri_for(c.controller.action_for('form_create')) %]">Admin Create</a>
+[% END %]
+</p>
+<p>
+  HTML::FormFu:
+  <a href="[% c.uri_for(c.controller.action_for('formfu_create')) %]">Create</a>
+</p>
+

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/error_noperms.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/error_noperms.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/error_noperms.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1 @@
+<span class="error">Permission Denied</span>

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/login.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/login.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/login.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,37 @@
+[% META title = 'Login' %]
+
+<!-- Login form -->
+<form method="post" action="[% c.uri_for('/login') %]">
+  <table>
+    <tr>
+      <td>Username:</td>
+      <td><input type="text" name="username" size="40" /></td>
+    </tr>
+    <tr>
+      <td>Password:</td>
+      <td><input type="password" name="password" size="40" /></td>
+    </tr>
+    <tr>
+      <td colspan="2"><input type="submit" name="submit" value="Submit" /></td>
+    </tr>
+  </table>
+</form>
+<p>
+[%
+   # This code illustrates how certain parts of the TT
+   # template will only be shown to users who have logged in
+%]
+[% IF c.user_exists %]
+    Please Note: You are already logged in as '[% c.user.username %]'.
+    You can <a href="[% c.uri_for('/logout') %]">logout</a> here.
+[% ELSE %]
+    You need to log in to use this application.
+[% END %]
+[%#
+   Note that this whole block is a comment because the "#" appears
+   immediate after the "[%" (with no spaces in between).  Although it
+   can be a handy way to temporarily "comment out" a whole block of
+   TT code, it's probably a little too subtle for use in "normal"
+   comments.
+%]
+</p>

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/wrapper.tt2
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/wrapper.tt2	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/src/wrapper.tt2	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" [%#
+    %]"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>[% template.title or "My Catalyst App!" %]</title>
+<link rel="stylesheet" href="[% c.uri_for('/static/css/main.css') %]" />
+</head>
+
+<body>
+<div id="outer">
+<div id="header">
+    [%# Your logo could go here -%]
+    <img src="[% c.uri_for('/static/images/btn_88x31_powered.png') %]" />
+    [%# Insert the page title -%]
+    <h1>[% template.title or site.title %]</h1>
+</div>
+
+<div id="bodyblock">
+<div id="menu">
+    Navigation:
+    <ul>
+        <li><a href="[% c.uri_for('/books/list') %]">Home</a></li>
+        <li><a href="[% c.uri_for('/')
+            %]" title="Catalyst Welcome Page">Welcome</a></li>
+    </ul>
+</div><!-- end menu -->
+
+<div id="content">
+    [%# Status and error messages %]
+    <span class="message">[% status_msg %]</span>
+    <span class="error">[% error_msg %]</span>
+    [%# This is where TT will stick all of your template's contents. -%]
+    [% content %]
+</div><!-- end content -->
+</div><!-- end bodyblock -->
+
+<div id="footer">Copyright (c) your name goes here</div>
+</div><!-- end outer -->
+
+</body>
+</html>
+


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static
___________________________________________________________________
Added: bugtraq:number
   + true


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/css
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/css/main.css
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/css/main.css	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/css/main.css	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,47 @@
+#header {
+    text-align: center;
+}
+#header h1 {
+    margin: 0;
+}
+#header img {
+    float: right;
+}
+#footer {
+    text-align: center;
+    font-style: italic;
+    padding-top: 20px;
+}
+#menu {
+    font-weight: bold;
+    background-color: #ddd;
+}
+#menu ul {
+    list-style: none;
+    float: left;
+    margin: 0;
+    padding: 0 0 50% 5px;
+    font-weight: normal;
+    background-color: #ddd;
+    width: 100px;
+}
+#content {
+    margin-left: 120px;
+}
+.message {
+    color: #390;
+}
+.error {
+    color: #f00;
+}
+input {
+    display: block;
+}
+select {
+    display: block;
+}
+.submit {
+    padding-top: .5em;
+    display: block;
+}
+


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_120x50_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_120x50_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_120x50_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_120x50_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_120x50_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_120x50_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_120x50_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_120x50_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_88x31_built.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_88x31_built.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_88x31_built_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_88x31_built_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_88x31_powered.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_88x31_powered.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_88x31_powered_shadow.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/btn_88x31_powered_shadow.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/catalyst_logo.png
===================================================================
(Binary files differ)


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/root/static/images/catalyst_logo.png
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_cgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_cgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_cgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,30 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'CGI');
+
+1;
+
+=head1 NAME
+
+myapp_cgi.pl - Catalyst CGI
+
+=head1 SYNOPSIS
+
+See L<Catalyst::Manual>
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as a cgi script.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_create.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_create.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_create.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Create');
+
+1;
+
+=head1 NAME
+
+myapp_create.pl - Create a new Catalyst Component
+
+=head1 SYNOPSIS
+
+myapp_create.pl [options] model|view|controller name [helper] [options]
+
+ Options:
+   --force        don't create a .new file where a file to be created exists
+   --mechanize    use Test::WWW::Mechanize::Catalyst for tests if available
+   --help         display this help and exits
+
+ Examples:
+   myapp_create.pl controller My::Controller
+   myapp_create.pl --mechanize controller My::Controller
+   myapp_create.pl view My::View
+   myapp_create.pl view HTML TT
+   myapp_create.pl model My::Model
+   myapp_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\
+   dbi:SQLite:/tmp/my.db
+   myapp_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static\
+   [Loader opts like db_schema, naming] dbi:Pg:dbname=foo root 4321
+   [connect_info opts like quote_char, name_sep]
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+   perldoc Catalyst::Helper::Model::DBIC::Schema
+   perldoc Catalyst::Model::DBIC::Schema
+   perldoc Catalyst::View::TT
+
+=head1 DESCRIPTION
+
+Create a new Catalyst Component.
+
+Existing component files are not overwritten.  If any of the component files
+to be created already exist the file will be written with a '.new' suffix.
+This behavior can be suppressed with the C<-force> option.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_fastcgi.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_fastcgi.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_fastcgi.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,48 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'FastCGI');
+
+1;
+
+=head1 NAME
+
+myapp_fastcgi.pl - Catalyst FastCGI
+
+=head1 SYNOPSIS
+
+myapp_fastcgi.pl [options]
+
+ Options:
+   -? -help      display this help and exits
+   -l --listen   Socket path to listen on
+                 (defaults to standard input)
+                 can be HOST:PORT, :PORT or a
+                 filesystem path
+   -n --nproc    specify number of processes to keep
+                 to serve requests (defaults to 1,
+                 requires -listen)
+   -p --pidfile  specify filename for pid file
+                 (requires -listen)
+   -d --daemon   daemonize (requires -listen)
+   -M --manager  specify alternate process manager
+                 (FCGI::ProcManager sub-class)
+                 or empty string to disable
+   -e --keeperr  send error messages to STDOUT, not
+                 to the webserver
+   --proc_title  Set the process title (is possible)
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as fastcgi.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_server.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_server.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_server.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,60 @@
+#!/usr/bin/env perl
+
+BEGIN {
+    $ENV{CATALYST_SCRIPT_GEN} = 40;
+}
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Server');
+
+1;
+
+=head1 NAME
+
+myapp_server.pl - Catalyst Test Server
+
+=head1 SYNOPSIS
+
+myapp_server.pl [options]
+
+   -d --debug           force debug mode
+   -f --fork            handle each request in a new process
+                        (defaults to false)
+   -? --help            display this help and exits
+   -h --host            host (defaults to all)
+   -p --port            port (defaults to 3000)
+   -k --keepalive       enable keep-alive connections
+   -r --restart         restart when files get modified
+                        (defaults to false)
+   -rd --restart_delay  delay between file checks
+                        (ignored if you have Linux::Inotify2 installed)
+   -rr --restart_regex  regex match files that trigger
+                        a restart when modified
+                        (defaults to '\.yml$|\.yaml$|\.conf|\.pm$')
+   --restart_directory  the directory to search for
+                        modified files, can be set multiple times
+                        (defaults to '[SCRIPT_DIR]/..')
+   --follow_symlinks    follow symlinks in search directories
+                        (defaults to false. this is a no-op on Win32)
+   --background         run the process in the background
+   --pidfile            specify filename for pid file
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst Testserver for this application.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_test.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_test.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/script/myapp_test.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,40 @@
+#!/usr/bin/env perl
+
+use Catalyst::ScriptRunner;
+Catalyst::ScriptRunner->run('MyApp', 'Test');
+
+1;
+
+=head1 NAME
+
+myapp_test.pl - Catalyst Test
+
+=head1 SYNOPSIS
+
+myapp_test.pl [options] uri
+
+ Options:
+   --help    display this help and exits
+
+ Examples:
+   myapp_test.pl http://localhost/some_action
+   myapp_test.pl /some_action
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst action from the command line.
+
+=head1 AUTHORS
+
+Catalyst Contributors, see Catalyst.pm
+
+=head1 COPYRIGHT
+
+This library is free software. You can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/set_hashed_passwords.pl
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/set_hashed_passwords.pl	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/set_hashed_passwords.pl	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,15 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use MyApp::Schema;
+
+my $schema = MyApp::Schema->connect('dbi:SQLite:myapp.db');
+
+my @users = $schema->resultset('User')->all;
+
+foreach my $user (@users) {
+    $user->password('mypass');
+    $user->update;
+}


Property changes on: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t
___________________________________________________________________
Added: bugtraq:number
   + true

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/01app.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/01app.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/01app.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+use Catalyst::Test 'MyApp';
+
+ok( request('/login')->is_success, 'Request should succeed' );
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/02pod.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/02pod.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/02pod.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+eval "use Test::Pod 1.14";
+plan skip_all => 'Test::Pod 1.14 required' if $@;
+
+all_pod_files_ok();

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/03podcoverage.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/03podcoverage.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/03podcoverage.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,14 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@;
+
+eval "use Pod::Coverage 0.20";
+plan skip_all => 'Pod::Coverage 0.20 required' if $@;
+
+all_pod_coverage_ok();

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/controller_Books.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/controller_Books.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/controller_Books.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Books;
+
+ok( request('/books')->is_redirect, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/controller_Login.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/controller_Login.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/controller_Login.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Login;
+
+ok( request('/login')->is_success, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/controller_Logout.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/controller_Logout.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/controller_Logout.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+use Catalyst::Test 'MyApp';
+use MyApp::Controller::Logout;
+
+ok( request('/logout')->is_redirect, 'Request should succeed' );
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/live_app01.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/live_app01.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/live_app01.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,101 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use Test::More;
+
+# Need to specify the name of your app as arg on next line
+# Can also do:
+#   use Test::WWW::Mechanize::Catalyst "MyApp";
+
+BEGIN { use_ok("Test::WWW::Mechanize::Catalyst" => "MyApp") }
+
+# Create two 'user agents' to simulate two different users ('test01' & 'test02')
+my $ua1 = Test::WWW::Mechanize::Catalyst->new;
+my $ua2 = Test::WWW::Mechanize::Catalyst->new;
+
+# Use a simplified for loop to do tests that are common to both users
+# Use get_ok() to make sure we can hit the base URL
+# Second arg = optional description of test (will be displayed for failed tests)
+# Note that in test scripts you send everything to 'http://localhost'
+$_->get_ok("http://localhost/", "Check redirect of base URL") for $ua1, $ua2;
+# Use title_is() to check the contents of the <title>...</title> tags
+$_->title_is("Login", "Check for login title") for $ua1, $ua2;
+# Use content_contains() to match on text in the html body
+$_->content_contains("You need to log in to use this application",
+    "Check we are NOT logged in") for $ua1, $ua2;
+
+# Log in as each user
+# Specify username and password on the URL
+$ua1->get_ok("http://localhost/login?username=test01&password=mypass", "Login 'test01'");
+# Could make user2 like user1 above, but use the form to show another way
+$ua2->submit_form(
+    fields => {
+        username => 'test02',
+        password => 'mypass',
+    });
+
+# Go back to the login page and it should show that we are already logged in
+$_->get_ok("http://localhost/login", "Return to '/login'") for $ua1, $ua2;
+$_->title_is("Login", "Check for login page") for $ua1, $ua2;
+$_->content_contains("Please Note: You are already logged in as ",
+    "Check we ARE logged in" ) for $ua1, $ua2;
+
+# 'Click' the 'Logout' link (see also 'text_regex' and 'url_regex' options)
+$_->follow_link_ok({n => 4}, "Logout via first link on page") for $ua1, $ua2;
+$_->title_is("Login", "Check for login title") for $ua1, $ua2;
+$_->content_contains("You need to log in to use this application",
+    "Check we are NOT logged in") for $ua1, $ua2;
+
+# Log back in
+$ua1->get_ok("http://localhost/login?username=test01&password=mypass", "Login 'test01'");
+$ua2->get_ok("http://localhost/login?username=test02&password=mypass", "Login 'test02'");
+# Should be at the Book List page... do some checks to confirm
+$_->title_is("Book List", "Check for book list title") for $ua1, $ua2;
+
+$ua1->get_ok("http://localhost/books/list", "'test01' book list");
+$ua1->get_ok("http://localhost/login", "Login Page");
+$ua1->get_ok("http://localhost/books/list", "'test01' book list");
+
+$_->content_contains("Book List", "Check for book list title") for $ua1, $ua2;
+# Make sure the appropriate logout buttons are displayed
+$_->content_contains("/logout\">User Logout</a>",
+    "Both users should have a 'User Logout'") for $ua1, $ua2;
+$ua1->content_contains("/books/form_create\">Admin Create</a>",
+    "'test01' should have a create link");
+$ua2->content_lacks("/books/form_create\">Admin Create</a>",
+    "'test02' should NOT have a create link");
+
+$ua1->get_ok("http://localhost/books/list", "View book list as 'test01'");
+
+# User 'test01' should be able to create a book with the "formless create" URL
+$ua1->get_ok("http://localhost/books/url_create/TestTitle/2/4",
+    "'test01' formless create");
+$ua1->title_is("Book Created", "Book created title");
+$ua1->content_contains("Added book 'TestTitle'", "Check title added OK");
+$ua1->content_contains("by 'Stevens'", "Check author added OK");
+$ua1->content_contains("with a rating of 2.", "Check rating added");
+# Try a regular expression to combine the previous 3 checks & account for whitespace
+$ua1->content_like(qr/Added book 'TestTitle'\s+by 'Stevens'\s+with a rating of 2./, "Regex check");
+
+# Make sure the new book shows in the list
+$ua1->get_ok("http://localhost/books/list", "'test01' book list");
+$ua1->title_is("Book List", "Check logged in and at book list");
+$ua1->content_contains("Book List", "Book List page test");
+$ua1->content_contains("TestTitle", "Look for 'TestTitle'");
+
+# Make sure the new book can be deleted
+# Get all the Delete links on the list page
+my @delLinks = $ua1->find_all_links(text => 'Delete');
+# Use the final link to delete the last book
+$ua1->get_ok($delLinks[$#delLinks]->url, 'Delete last book');
+# Check that delete worked
+$ua1->content_contains("Book List", "Book List page test");
+$ua1->content_like(qr/Deleted book \d+/, "Deleted book #");
+
+# User 'test02' should not be able to add a book
+$ua2->get_ok("http://localhost/books/url_create/TestTitle2/2/5", "'test02' add");
+$ua2->content_contains("Unauthorized!", "Check 'test02' cannot add");
+
+done_testing;
+

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/model_DB.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/model_DB.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/model_DB.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,8 @@
+use strict;
+use warnings;
+use Test::More;
+
+
+BEGIN { use_ok 'MyApp::Model::DB' }
+
+done_testing();

Added: trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/view_HTML.t
===================================================================
--- trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/view_HTML.t	                        (rev 0)
+++ trunk/examples/Tutorial/Final/Chapter09_FormFu/MyApp/t/view_HTML.t	2011-09-03 19:35:03 UTC (rev 14090)
@@ -0,0 +1,9 @@
+use strict;
+use warnings;
+use Test::More;
+
+use MyApp;
+
+BEGIN { use_ok 'MyApp::View::HTML' }
+
+done_testing();

Deleted: trunk/examples/Tutorial/Hello_Chapter2.tar.gz
===================================================================
(Binary files differ)

Deleted: trunk/examples/Tutorial/MyApp_Chapter3.tar.gz
===================================================================
(Binary files differ)

Deleted: trunk/examples/Tutorial/MyApp_Chapter4.tar.gz
===================================================================
(Binary files differ)

Deleted: trunk/examples/Tutorial/MyApp_Chapter5.tar.gz
===================================================================
(Binary files differ)

Deleted: trunk/examples/Tutorial/MyApp_Chapter6.tar.gz
===================================================================
(Binary files differ)

Deleted: trunk/examples/Tutorial/MyApp_Chapter7.tar.gz
===================================================================
(Binary files differ)

Deleted: trunk/examples/Tutorial/MyApp_Chapter8.tar.gz
===================================================================
(Binary files differ)

Deleted: trunk/examples/Tutorial/MyApp_Chapter9_FormFu.tar.gz
===================================================================
(Binary files differ)




More information about the Catalyst-commits mailing list