[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