Catalyst-Manual-5.9010/0000755000000000000000000000000013460431141014723 5ustar00rootwheel00000000000000Catalyst-Manual-5.9010/LICENSE0000644000000000000000000004354713460431141015745 0ustar00rootwheel00000000000000Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2019 by Catalyst Contributors, see Catalyst.pm, Kieren Diment . This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2019 by Catalyst Contributors, see Catalyst.pm, Kieren Diment . This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End Catalyst-Manual-5.9010/Changes0000644000000000000000000003010013460431072016213 0ustar00rootwheel00000000000000Revision history for Catalyst-Manual 5.9010 - 2019-04-25 - updated stale urls - numerous typo fixes - many pod syntax fixes - other pod syntax cleanup - added references to the RT issues queue, mailing list, and irc channel 5.9009 - 2014-12-13 - fixed a new error in the manual regarding "component" -> "components" (RT#99131, RT#100597, RT#100767) - typo fix in 09_FormFu.pod (Jason McVeigh, RT#104585) 5.9008 - 2014-07-26 - misc small documentation tweaks (thanks Karen Etheridge, Matthew Horsfall, Andreas Marienborg, Dagfinn Ilmari Mannsåker, David Steinbrunner, Gerda Shank, Jesse) 5.9007 2013-05-06 - Fix pod warnings (RT#83398 - thanks, Paul Waring!) - Fix repository metadata (thanks, Lars Dɪᴇᴄᴋᴏᴡ) 5.9006 2012-11-08 - fix bad =head1 (Kennedy Clark) 5.9005 2012-11-01 - Fix minor typos RT 78545, thanks Joe Axford - Update auth class name RT 77322 Thanks Joe Axford - Fix typo RT #77247 Thanks John Deighan 5.9004 4th May 2012 - PSGI Compat changes - Small code changes (thanks sockmonk) - Small changes to Manual::Components 5.9003 17 Feb 2012 - Mention PSGI in Manual::Ingro RT 74872 (thanks William Blunn). - Better docs of :Global inspired by RT 74869 (thanks William Blunn) - Highlight the importance of uncommenting the template line in the list action - Clarify docs for nginx in non-root - a trailing slash on the location block is very much needed. - Clarified Data::Dumper usage. RT#71410 Thanks to Bill Corr - Mention Chef deployment in Manual::Deployment (thanks to Alexey Melezhik) 5.9002 3 Sept 2011 - Tutorial - Switch to 'catalyst' vs. 'root' user in VM - Add notes about X Windows installation - Misc small adjustments 5.9001 2 Sept 2011 - Tutorial: - Migrate Tutorial to use of a downloadable Virtual Machine - Switch tutorial to Catalyst::Plugin::StatusMessage (vs. flash and query parameters) - Switch to use of local::lib - Add "next chapter links" (RT #31164) - Test all the tutorial code and make sure it's all working - Lots of other Tutorial cleanup - Update to Catalyst 5.9 and latest versions of all modules - Clean up RT ticket queue (#68379, 68377, 68376, etc.) - Other misc fixes/changes 5.9000 16 Aug 2011 - Copy editing to make more sense in deployment documentation. 5.8901 7 Aug 2011 - TRIAL RELEASE - Added Catalyst::Manual::Deployment containing the documentation which used to be in Catalyst::Engine::*, updated for the new PSGI engine. 5.8008 2 Aug 2011 - Tutorial chaper 3 - Remove note about hacking tests to require MyApp so that MyApp->path_to works. Application components should compile independently, and therefore explain this and show configuring components from the app class. - Tutorial appendix - Fix confusing mix of singular and plural table names in the MySQL section to be plural, as per the rest of the tutorial. - Cookbook - Remove suggestion to generate RSS feeds using Template Toolkit. This is a horrible idea, and it's very very easy to generate an invalid feed. 5.8007 29 Feb 2011 - Tutorial - Switch to use of DBIx::Class::PassphraseColumn for hashed & salted passwords. It's much more flexible than the previously used DBIx::Class::EncodedColumn. 5.8006 29 Feb 2011 - Fix metadata to refer to the new git repository - Cookbook - Small fixes and typos - Tutorial - Various typo fixes. - RT #57989: typo - RT #61486: correct instructions for MySQL - RT #62095: prevent XSS - RT #62095: persistent message on /login - RT #63057: typo - RT #64087: typos - RT #64126: Use precise name of licence - RT #64126: typos - RT #67820: fix relationship decleration - Do not recommend FastMmap - DevelopmentProcess - RT #62610: typo 5.8005 27 Oct 2010 - Tutorial: - Add DATABASE CONFIG SWITCHING USING MULTIPLE CONFIG FILES section - Critical bugfix on index page. 5.8004 17 Feb 2010 - Tutorial: - Add foreign key support for SQLite (huge thanks to Caelum for that and other good edits!) - Add "Quick Start" to Intro (Chapter 1) - Switch to use of "-r" to auto-restart the dev svr - Update for latest available Debian package versions - Switch to individual files for example code vs. tarballs - Switch to 'done_testing' and shorter 'prove' args for testing chapter - Misc typo fixes - Other: - Minor Cookbook edits 5.8003 28 Dec 2009 - Variety of typo fixes - Fix incorrectness re :Global and :Local - Update DevelopmentProcess.pod 5.8002 15 Nov 2009 - Update tutorial to match latest prepacked versions in Debian 5 - Add FormHandler branch (with thanks to gshank!) - Misc cleanup/freshing up of tutorial. - Fix indenting issue (with thanks to Kiffin Gish) - Integrate tome fix branch (with thanks to tome!) - Add a "negative" test to confirm that test02 does not have an admin create link - Integrate sqlite3 clarification and link by wolfman2000 from tutorial_role_updates branch - Fix Pod typos in ::Internals (RT#51488) - Fix Pod typos in the Cookbook (RT#51466) - Fix a Test::Pod failure and make Debian happier. - Typo fixes from garu - Misc minor and/or typo fixes 5.8001 06 Oct 2009 - Tutorial - Fix RT #46760 - Fix RT #46618 - Fix cat-install script URL - Fix typos - Replace reference to deprecated CatalystX::ListFramework::Builder with Catalyst::Plugin::AutoCRUD - Other - Lots of updates thanks to t0m - Update development process / core team docs - Cookbook fixes WRT authorization - Better description of application setup process - Fix some links - Normalise spacing 5.8000 27 May 2009 - Tutorial: - Update for Catalyst 5.80 - Update to "depluralize" the database names (big thanks to Kiffin Gish!) - Switch back to including numbers in chapter names (for proper sorting) - Add section to Ch 4: "Moving Complicated View Code to the Model" - Add section to Ch 3: "RenderView's 'dump_info' Feature" - Misc fixes and updates (thanks to Anne Wainwright) - Other: - Add some 5.8 and Moose-specific material to the new CatalystAndMoose.pod (thanks to t0m and Sebastian Willert) 5.7021 8 May 2009 - Tutorial: - Switch to SimpleDB for auth - Switch to use of DBIx::Class::EncodedColumn for hashed & salted passwords - Re-write PostgreSQL section in appendix - Remove "create=dynamic" and only cover "create=static" for DBIC helper - Other: - Rewrite / clean up a big chunk of Catalyst::Manual::Intro (Ian Wells) - There is no Catalyst::Config, fix reference to it. (t0m) - Misc minor adjustments 5.7020 12 Mar 2009 - Tutorial: - Fix errors in FormFu chapter 5.7019 11 Mar 2009 - Tutorial: - Change from the use of "part" to refer to each .pod file for the tutorial in favor of the more intuitive word "chapter." "Part" was just to ambiguous (e.g., does "prior part" refer to the prior .pod file or the prior section in the current .pod file). - Move use of "load_namespaces" for DBIC from BasicCRUD to MoreCatalystBasics - Update the "Table of Contents" in Tutorial.pod to match the current sections - Fix a few typos 5.7018 8 Mar 2009 - Tutorial: - Add a new section to BasicCRUD covering more advanced features of DBIC ("EXPLORING THE POWER OF DBIC") - Convert from Ubuntu to Debian 5 live CD as the recommended way to do the tutorial (all code and examples updated and tested to match) - Removed Catalyst::Plugin::Authorization::ACL from Authorization.pod in favor of a "chained and model-based" approach - More conversion to Chained dispatch - Suggestions and fixes with thanks to mintywalker@gmail.com - DBIC-related updates in MoreCatalystBasics - Fix misplaced "=over 4" in previous release - Reword warning about not using GET for delete based on input from kd - Lots of other small adjustments 5.7017 28 Feb 2009 - Tutorial: - Main change = adding Chained dispatch starting in BasicCRUD (Part 4) - Change FormFu tutorial to not use deprecated methods (Del Merritt) - MoreCatalystBasics - Additional clarification about TTSite (Del Merritt) - Tutorial::Authorization - Corrects the ACL for "/books/delete" - Additional comments (Del Merritt) - Tutorial::AdvancedCRUD::FormFu - suggest how the intrepid user might now proceed, having completed the Tutorial. (Del Merritt) - Fix typo in Authorization section (RT #42091) - Fix typo in BasicCRUD (RT #42034) - Resolve outstanding typo and suggestions in MoreCatalystBasics (RT #41491) - Fix DBIC create for MySQL in Appendix (Jarom) - Other: - Misc minor updates - Add some "getting started" links to the Catalyst::Manual page 5.7016 28 Dec 2008 - Tutorial: - Updates to make Parts 8 & 9 run correctly - Adjust URLs for final config tarballs - Add note about changes across different C::Devel on how plugins enabled - Misc minor updates 5.7015 15 Dec 2008 - Tutorial: - Remove TTSite from Tutorial (thanks to dhoss for the help) - Update Tutorial for Ubuntu 8.10 (therefore update to Cat v5.7014, C::Devel v1.07, DBIC v0.08010, etc.) - Reorganize MoreCatalystBasics.pod so user is able to run the app the first time much earlier and build on it from there (running the app each time along the way) - Update URL for latest copy in SVN to match new location of repo - Other: - Misc typo fix - Change use of Class::C3 to MRO::Compat, as rafl tells me this is best practice, and gives you native next::method in 5.10. (t0m) 5.7014 04 Nov 2008 - Remove a reference to a FOREACH loop that did not exist (RT #39046) - Changed some Template Toolkit links to perldoc links (RT #38354) - Fix Template Toolkit website link (RT #37574) - Fix part numbering (RT #37963) - Improvements to the ACCEPT_CONTEXT docs in Manual::Intro - Happy Election Day, America! 5.7013 09 Jul 2008 - revert to use Catalyst qw/@plugins/ style 5.7012 29 May 2008 - Expurgation of all use of default :Private and index :Private in favour of default :Path and index :Path :Args(0) - Expurgation of all yaml as configuration format - Major updates to tutorial, thanks hkclark and gerda 5.7011 16 May 2008 - added warnings and poiinters to newer examples in HTML::Widget, and Authentication parts of the tutorial. - pod fix (RT #32636) 5.701004 09 Apr 2008 - rename placeholder back to Manual.pm, this will probably trigger a ppm bug but that's ppm's problem 5.701003 08 Oct 2007 - Patch to Cookbook from bits. 5.701002 25 Aug 2007 5.701001 17 Aug 2007 - Changes to mocation of lib/Catalyst/Manual.pod to lib/Catalyst/ManualPlaceholder.pm to keep cpan indexer happy. 5.700704 08 Aug 2007 - Updated mailing list addresses to scsys.co.uk domains 5.700703 - Cookbook: Updated development server deployment instructions, and included a better description of the POE engine. 5.700702 26 April 2007 - Switch to Module::Install 5.700701 19 April 2007 - Removal of WritingPlugins. ExtendingCatalyst is now the main resource for extensions to the framework and the application. 5.700501 10 November 2006 - Matches Catalyst-Runtime 5.7005 5.700401 07 November 2006 - First release to CPAN; matches Catalyst-Runtime 5.7004. 5.700301 20 October 2006 - Splitting manual into its own distro Catalyst-Manual-5.9010/MANIFEST0000644000000000000000000000353713460431141016064 0ustar00rootwheel00000000000000Changes lib/Catalyst/Manual.pm lib/Catalyst/Manual/About.pod lib/Catalyst/Manual/Actions.pod lib/Catalyst/Manual/CatalystAndMoose.pod lib/Catalyst/Manual/Components.pod lib/Catalyst/Manual/Cookbook.pod lib/Catalyst/Manual/Deployment.pod lib/Catalyst/Manual/Deployment/Apache/FastCGI.pod lib/Catalyst/Manual/Deployment/Apache/mod_perl.pod lib/Catalyst/Manual/Deployment/DevelopmentServer.pod lib/Catalyst/Manual/Deployment/FastCGI.pod lib/Catalyst/Manual/Deployment/IIS/FastCGI.pod lib/Catalyst/Manual/Deployment/lighttpd/FastCGI.pod lib/Catalyst/Manual/Deployment/nginx/FastCGI.pod lib/Catalyst/Manual/Deployment/SharedHosting.pod lib/Catalyst/Manual/DevelopmentProcess.pod lib/Catalyst/Manual/ExtendingCatalyst.pod lib/Catalyst/Manual/Internals.pod lib/Catalyst/Manual/Intro.pod lib/Catalyst/Manual/Tutorial.pod lib/Catalyst/Manual/Tutorial/01_Intro.pod lib/Catalyst/Manual/Tutorial/02_CatalystBasics.pod lib/Catalyst/Manual/Tutorial/03_MoreCatalystBasics.pod lib/Catalyst/Manual/Tutorial/04_BasicCRUD.pod lib/Catalyst/Manual/Tutorial/05_Authentication.pod lib/Catalyst/Manual/Tutorial/06_Authorization.pod lib/Catalyst/Manual/Tutorial/07_Debugging.pod lib/Catalyst/Manual/Tutorial/08_Testing.pod lib/Catalyst/Manual/Tutorial/09_AdvancedCRUD.pod lib/Catalyst/Manual/Tutorial/09_AdvancedCRUD/09_FormBuilder.pod lib/Catalyst/Manual/Tutorial/09_AdvancedCRUD/09_FormFu.pod lib/Catalyst/Manual/Tutorial/09_AdvancedCRUD/09_FormHandler.pod lib/Catalyst/Manual/Tutorial/10_Appendices.pod lib/Catalyst/Manual/WritingPlugins.pod maint/Makefile.PL.include Makefile.PL MANIFEST This list of files README t/01-use.t xt/author/pod-coverage.t xt/author/pod.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) LICENSE LICENSE file (added by Distar) Catalyst-Manual-5.9010/t/0000755000000000000000000000000013460431137015173 5ustar00rootwheel00000000000000Catalyst-Manual-5.9010/t/01-use.t0000644000000000000000000000007413454703351016376 0ustar00rootwheel00000000000000use strict; use warnings; use Test::More tests => 1; ok(1); Catalyst-Manual-5.9010/xt/0000755000000000000000000000000013460431137015363 5ustar00rootwheel00000000000000Catalyst-Manual-5.9010/xt/author/0000755000000000000000000000000013460431137016665 5ustar00rootwheel00000000000000Catalyst-Manual-5.9010/xt/author/pod.t0000644000000000000000000000007113454703351017635 0ustar00rootwheel00000000000000use Test::More; use Test::Pod 1.14; all_pod_files_ok(); Catalyst-Manual-5.9010/xt/author/pod-coverage.t0000644000000000000000000000010713454703351021426 0ustar00rootwheel00000000000000use Test::More; use Test::Pod::Coverage 1.04; all_pod_coverage_ok(); Catalyst-Manual-5.9010/README0000644000000000000000000000060113454703345015613 0ustar00rootwheel00000000000000Catalyst-Manual This is just the Catalyst manual. If you want to develop Catalyst apps, please install Catalyst::Devel. If you'd like a tutorial and a full example Catalyst application, please install Task::Catalyst::Tutorial. If you just want to run Catalyst applications, you probably don't need this manual, but you do need Catalyst::Runtime. http://dev.catalystframework.org/ Catalyst-Manual-5.9010/META.yml0000644000000000000000000000651113460431140016176 0ustar00rootwheel00000000000000--- abstract: "The Catalyst developer's manual" author: - 'Catalyst Contributors, see Catalyst.pm' - 'Kieren Diment ' build_requires: ExtUtils::MakeMaker: '0' Test::More: '0.88' configure_requires: ExtUtils::MakeMaker: '0' dynamic_config: 0 generated_by: 'ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Catalyst-Manual no_index: directory: - t - inc requires: perl: '5.006' resources: IRC: irc://irc.perl.org/#catalyst MailingList: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst bugtracker: https://rt.cpan.org/Public/Dist/Display.html?Name=Catalyst-Manual repository: git://git.shadowcat.co.uk/catagits/Catalyst-Manual.git version: '5.9010' x_contributors: - 'Kennedy Clark ' - 'Tomas Doran ' - 'Kieren Diment ' - 'Karen Etheridge ' - 'Jonathan Rockway ' - 'Jesse Sheidlower ' - 'Brian Cassidy ' - 'Caleb Cushing ' - 'Dan Dascalescu ' - 'Rafael Kitover ' - 'Jason Felds ' - 'Frederik Schwarzer ' - 'Jonathan Yu ' - 'Breno G. de Oliveira ' - 'Matt S Trout ' - "Lars Dɪá´á´á´á´á´¡ è¿ªææ¯ " - 'Jess Robinson ' - 'Florian Ragwitz ' - 'Eden Cardim ' - 'Andrew Rodland ' - 'Justin Hunter ' - 'Matthew Horsfall ' - 'Ricardo SIGNES ' - "Robert 'phaylon' Sedlacek " - 'Shlomi Fish ' - 'Jay Kuri ' - 'u-foka ' - 'Dagfinn Ilmari MannsÃ¥ker ' - 'Alexander Hartmaier ' - 'Peter Karman ' - 'Andreas Marienborg ' - 'Gerda Shank ' - 'Ian Wells ' - 'Mark A. Stratman ' - 'sockmonk ' - 'Mateu X. Hunter ' - 'Ash Berlin ' - 'Marcus Ramberg ' - 'Andy Grundman ' - 'kostya ' - 'Matthias Dietrich ' - 'Paul Waring ' - 'kthakore ' - 'Ronald J Kimball ' - 'Tom Feist ' - 'Ton Voon ' - 'Zsolt Zemancsik ' - 'Iñigo Tejedor Arrondo ' - 'Eric A. Zarko ' - 'Jay Hannah ' - 'Eisenberger Tamás ' - 'Edwin de Graaf ' - 'Jonathan "Duke" Leto ' - 'Jonathan Otsuka ' - 'Alastair McGowan-Douglas ' - 'Dominic Humphries ' - 'antgel ' - 'KMX ' - 'David Steinbrunner ' - 'David Schmidt ' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' Catalyst-Manual-5.9010/lib/0000755000000000000000000000000013460431137015476 5ustar00rootwheel00000000000000Catalyst-Manual-5.9010/lib/Catalyst/0000755000000000000000000000000013460431137017262 5ustar00rootwheel00000000000000Catalyst-Manual-5.9010/lib/Catalyst/Manual.pm0000644000000000000000000000446113460431065021042 0ustar00rootwheel00000000000000# Manual.pm # Copyright (c) 2006 Jonathan Rockway package Catalyst::Manual; use strict; use warnings; our $VERSION = '5.9010'; 1; __END__ =head1 NAME Catalyst::Manual - The Catalyst developer's manual =head1 SYNOPSIS perldoc Catalyst::Manual::Intro perldoc Catalyst::Manual::Tutorial =head1 SEE ALSO Install L to install all the dependencies you need to follow along with the Tutorial. You can also refer to L for more information on installation options. Some "Getting Started" Links: =over 4 =item * L =item * L =item * L =item * L =item * L =item * L =item * L =item * L =back =head2 Books For additional information on Catalyst, there are currently two books available: =over 4 =item * The Definitive Guide to Catalyst: Writing Extendable, Scalable and Maintainable Perl-Based Web Applications By: Kieren Diment, Matt Trout Available July 12, 2009 ISBN 10: 1-4302-2365-0 ISBN 13: 978-1-4302-2365-8 http://www.apress.com/book/view/9781430223658 =item * Accelerating Perl Web Application Development By: Jonathan Rockway Published December, 2007 ISBN 10: 1847190952 ISBN 13: 978-1-847190-95-6 http://www.packtpub.com/catalyst-perl-web-application/book =back =head1 SUPPORT Corrections or amendments may be submitted through L (or L). There is also a mailing list available for users of this distribution, at L. There is also an irc channel available for users of this distribution, at L. =head1 AUTHORS Catalyst Contributors, see L =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2006 by the Catalyst contributors. This is free software and content; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut Catalyst-Manual-5.9010/lib/Catalyst/Manual/0000755000000000000000000000000013460431137020477 5ustar00rootwheel00000000000000Catalyst-Manual-5.9010/lib/Catalyst/Manual/DevelopmentProcess.pod0000644000000000000000000001271513454703351025035 0ustar00rootwheel00000000000000=head1 NAME Catalyst::Manual::DevelopmentProcess - Administrative structure of the Catalyst Development Process =head1 Contributing to Catalyst The main philosophy behind Catalyst development can be summarized as: Patches welcome! Everyone is welcome (and will be encouraged) to contribute to Catalyst in whatever capacity they're able to. People in #catalyst-dev will be more than happy to talk newcomers through contributing their first patch, or how best to go about their first CPAN extension module.... =head1 Catalyst development =head2 Discussing proposed bugfixes or improvements L has information about how to get in touch with the Catalyst "community". In particular, you would want to discuss a proposed change on the mailing list: http://lists.scsys.co.uk/cgi-bin/mailman/listinfo/catalyst-dev or on IRC: irc://irc.perl.org/catalyst-dev http://wiki.catalystframework.org/wiki/livegroups Usually, the core team will be more than happy for you to contribute, and will talk you through how to submit a patch, or get a "commit bit". =head2 Repositories The Catalyst git repository can be found at: read: git://git.shadowcat.co.uk/catagits/PROJECTNAME write: catagits@git.shadowcat.co.uk:PROJECTNAME browser: https://git.shadowcat.co.uk/gitweb/gitweb.cgi =head2 Schedule There is no dated release cycle for Catalyst. New releases will be made when sufficient small fixes have accumulated; or an important bugfix, or significant feature addition, is completed. =head1 The Catalyst Core Team The intention of the Catalyst Core Team is to maintain and support the Catalyst framework, in order for it to be a viable and stable framework for developing web-based MVC applications. This includes both technical decisions about the Catalyst core distribution, and public relations relating to the Catalyst framework as a whole. The current goals of the Catalyst core development team are stability, performance, and a properly paced addition of features, with a focus on extensibility. The core team is concerned with the 'core' Catalyst distributions (i.e. L, L and L), and also tries to encourage best practices for extension authors, and cooperation and shared vision within the Catalyst community. =head2 Membership The Catalyst Core Team consists of the developers who have full commit privileges to the entire Catalyst source tree, and who have made a significant contribution to the core Catalyst distributions, and various extensions and plugins. In addition, the core team includes members that have non-technical roles, such as marketing, legal, or economic responsibilities. Currently, the Core Team consists of the following people: =over 4 =item Brian Cassidy =item Andy Grundman =item Christian Hansen =item Yuval Kogman =item Marcus Ramberg =item Jonathan Rockway =item Jesse Sheidlower =item Matt S. Trout =item Florian Ragwitz =item Tomas Doran =back New members of the Core Team must be accepted by a 2/3 majority by the current members. =head2 Technical Decisions. Any change to the Catalyst core which can not be conceived as a correction of an error in the current feature set will need to be accepted by at least 3 members of the Core Team before it can be committed to master (which is the basis for CPAN releases). Anyone with access is at any time free to make a branch to develop a proof of concept for a feature to be committed to master. =head2 Organizational and Philosophical Decisions. Any organizational or philosophical decision should be decided by majority vote. Thus it should be a goal of the organization that its membership number should at any time be an odd number, to render it effective with regards to decision making. The exceptions to this rule are changes to this charter and additions to the membership of the Core Team, which require a 2/3 majority. =head2 CPAN Releases Planned releases to CPAN should be performed by the release manager, at the time of writing Marcus Ramberg, or the deputy release manager, at the time of writing Florian Ragwitz. In the case of critical error correction, any member of the Core Team can perform a rescue release. =head2 Public statements from the Core Team The Core Team should strive to appear publicly as a group when answering questions or other correspondence. In cases where this is not possible, the same order as for CPAN releases applies. =head2 New Catalyst Extensions As Catalyst is deliberately designed for extension, there is an ecosystem of several hundred Catalyst extensions that can be found on CPAN. See L for more information on how to extend Catalyst in various ways and how to write CPANable components for Catalyst which can be reused in many applications. It is recommended to post a request for comments to the Catalyst mailing list, or ask around in the #catalyst IRC channel before starting to implement something, as another member of the community is likely to have example or prototype code that you can reuse, and members of the community and core team are happy to advise on the best way to implement a generic solution to a particular problem. This could save you duplicate work, and will help you produce a better thought out and designed extension. =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 Catalyst-Manual-5.9010/lib/Catalyst/Manual/Tutorial.pod0000644000000000000000000002567113454703351023024 0ustar00rootwheel00000000000000=head1 NAME Catalyst::Manual::Tutorial - Catalyst Tutorial: Overview =head1 DESCRIPTION The Catalyst framework is a flexible and comprehensive environment for quickly building high-functionality web applications. This tutorial is designed to provide a rapid introduction to its basics and its most commonly used features while focusing on real-world best practices. We suggest that you read this introduction on the web. Make sure you are reading the latest version of the tutorial by visiting L. Alternatively you can use CPAN modules like L, L, L (Catalyst based), or L to read a local copy of the tutorial. The tutorial is divided into the following sections: B (the index links above only navigate inside this page). =over 4 =item 1 L =item 2 L =item 3 L =item 4 L =item 5 L =item 6 L =item 7 L =item 8 L =item 9 L =item 10 L =back Final code tarballs for each chapter of the tutorial are available at L. =head1 Detailed Table of Contents =head2 L Note: Click on the heading in the previous line to jump to the actual chapter. Below is a "table of contents" for this chapter. =over 4 =item * VERSIONS AND CONVENTIONS USED IN THIS TUTORIAL =item * CATALYST INSTALLATION =item * DATABASES =item * WHERE TO GET WORKING CODE =back =head2 L Note: Click on the heading in the previous line to jump to the actual chapter. Below is a "table of contents" for this chapter. =over 4 =item * CREATE A CATALYST PROJECT =item * HELLO WORLD =over 4 =item * The Simplest Way =item * Hello, World! Using a View and a Template =back =item * CREATE A SIMPLE CONTROLLER AND AN ACTION =back =head2 L Note: Click on the heading in the previous line to jump to the actual chapter. Below is a "table of contents" for this chapter. =over 4 =item * CREATE A NEW APPLICATION =item * EDIT THE LIST OF CATALYST PLUGINS =item * CREATE A CATALYST CONTROLLER =item * CATALYST VIEWS =over 4 =item * Create a Catalyst View =item * Create a TT Template Page =item * Test Run The Application =back =item * CREATE A SQLITE DATABASE =item * DATABASE ACCESS WITH DBIx::Class =over 4 =item * Create a Dynamic DBIC Model =back =item * ENABLE THE MODEL IN THE CONTROLLER =over 4 =item * Test Run The Application =back =item * CREATE A WRAPPER FOR THE VIEW =over 4 =item * Configure TT.pm For The Wrapper =item * Create the Wrapper Template File and Stylesheet =item * Test Run The Application =back =item * A STATIC DATABASE MODEL WITH DBIx::Class =over 4 =item * Create Static DBIC Schema Files =item * Updating the Generated DBIC Schema Files =item * Run The Application =back =item * UPDATING THE VIEW =item * RUNNING THE APPLICATION FROM THE COMMAND LINE =item * OPTIONAL INFORMATION =over 4 =item * Using RenderView for the Default View =item * Using The Default Template Name =item * Return To A Manually-Specified Template =back =back =head2 L Note: Click on the heading in the previous line to jump to the actual chapter. Below is a "table of contents" for this chapter. =over 4 =item * FORMLESS SUBMISSION =over 4 =item * Include a Create Action in the Books Controller =item * Include a Template for the url_create Action: =item * Try the url_create Feature =back =item * CONVERT TO A CHAINED ACTION =over 4 =item * Try the Chained Action =item * Refactor to Use a "Base" Method to Start the Chains =back =item * MANUALLY BUILDING A CREATE FORM =over 4 =item * Add a Method to Display the Form =item * Add a Template for the Form =item * Add Method to Process Form Values and Update Database =item * Test Out the Form =back =item * A SIMPLE DELETE FEATURE =over 4 =item * Include a Delete Link in the List =item * Add a Common Method to Retrieve a Book for the Chain =item * Add a Delete Action to the Controller =item * Try the Delete Feature =item * Fixing a Dangerous URL =item * Try the Delete and Redirect Logic =item * Using uri_for to Pass Query Parameters =item * Try the Delete and Redirect With Query Param Logic =back =item * EXPLORING THE POWER OF DBIC =over 4 =item * Add Datetime Columns to Our Existing Books Table =item * Update DBIC to Automatically Handle the Datetime Columns =item * Create a ResultSet Class =item * Chaining ResultSets =item * Adding Methods to Result Classes =back =back =head2 L Note: Click on the heading in the previous line to jump to the actual chapter. Below is a "table of contents" for this chapter. =over 4 =item * BASIC AUTHENTICATION =over 4 =item * Add Users and Roles to the Database =item * Add User and Role Information to DBIC Schema =item * Sanity-Check Reload of Development Server =item * Include Authentication and Session Plugins =item * Configure Authentication =item * Add Login and Logout Controllers =item * Add a Login Form TT Template Page =item * Add Valid User Check =item * Displaying Content Only to Authenticated Users =item * Try Out Authentication =back =item * USING PASSWORD HASHES =over 4 =item * Get a SHA-1 Hash for the Password =item * Switch to SHA-1 Password Hashes in the Database =item * Enable SHA-1 Hash Passwords in Catalyst::Plugin::Authentication::Store::DBIC =item * Try Out the Hashed Passwords =back =item * USING THE SESSION FOR FLASH =over 4 =item * Try Out Flash =item * Switch To Flash-To-Stash =back =back =head2 L Note: Click on the heading in the previous line to jump to the actual chapter. Below is a "table of contents" for this chapter. =over 4 =item * BASIC AUTHORIZATION =over 4 =item * Update Plugins to Include Support for Authorization =item * Add Config Information for Authorization =item * Add Role-Specific Logic to the ``Book List'' Template =item * Limit Books::add to admin Users =item * Try Out Authentication And Authorization =back =item * ENABLE MODEL-BASED AUTHORIZATION =back =head2 L Note: Click on the heading in the previous line to jump to the actual chapter. Below is a "table of contents" for this chapter. =over 4 =item * LOG STATEMENTS =item * RUNNING CATALYST UNDER THE PERL DEBUGGER =item * DEBUGGING MODULES FROM CPAN =item * TT DEBUGGING =back =head2 L Note: Click on the heading in the previous line to jump to the actual chapter. Below is a "table of contents" for this chapter. =over 4 =item * RUNNING THE "CANNED" CATALYST TESTS =item * RUNNING A SINGLE TEST =item * ADDING YOUR OWN TEST SCRIPT =item * SUPPORTING BOTH PRODUCTION AND TEST DATABASES =back =head2 L Note: Click on the heading in the previous line to jump to the actual chapter. Below is a "table of contents" for this chapter. =over 4 =item * ADVANCED CRUD OPTIONS =back =head2 L Note: Click on the heading in the previous line to jump to the actual chapter. Below is a "table of contents" for this chapter. =over 4 =item * APPENDIX 1: CUT AND PASTE FOR POD-BASED EXAMPLES =over 4 =item * "Un-indenting" with Vi/Vim =item * "Un-indenting" with Emacs =back =item * APPENDIX 2: USING MYSQL AND POSTGRESQL =over 4 =item * MySQL =item * PostgreSQL =back =item * APPENDIX 3: IMPROVED HASHING SCRIPT =back =head1 THANKS This tutorial would not have been possible without the input of many different people in the Catalyst community. In particular, the primary author would like to thank: =over 4 =item * Sebastian Riedel for founding the Catalyst project. =item * The members of the Catalyst Core Team for their tireless efforts to advance the Catalyst project. Although all of the Core Team members have played a key role in this tutorial, it would have never been possible without the critical contributions of: Matt Trout, for his unfathomable knowledge of all things Perl and Catalyst (and his willingness to answer lots of my questions); Jesse Sheidlower, for his incredible skill with the written word and dedication to improving the Catalyst documentation; and Yuval Kogman, for his work on the Catalyst "Auth & Authz" plugins (the original focus of the tutorial) and other key Catalyst modules. =item * Other Catalyst documentation folks like Kieren Diment, Gavin Henry, and Jess Robinson (including their work on the original Catalyst tutorial). =item * Kieren Diment for his oversight of Catalyst-related documentation. =item * Everyone on #catalyst and #catalyst-dev. =item * Louis Moore (who thanks Marcello Romani and Tom Lanyon) for the PostgreSQL content in the Appendix. =item * People who have emailed me with corrections and suggestions on the tutorial. As of the most recent release, this include: Florian Ragwitz, Mauro Andreolini, Jim Howard, Giovanni Gigante, William Moreno, Bryan Roach, Ashley Berlin, David Kamholz, Kevin Old, Henning Sprang, Jeremy Jones, David Kurtz, Ingo Wichmann, Shlomi Fish, Murray Walker, Adam Witney and xenoterracide (Caleb Cushing). Thanks to Devin Austin for coming up with an initial version of a non-TTSite wrapper page. Also, a huge thank you to Kiffin Gish for all the hard work on the "database depluralization" effort and Rafael Kitover for the work on updating the tutorial to include foreign key support for SQLite. I'm sure I am missing some names here... apologies for that (please let me know if you name should be here). =back =head1 AUTHOR Kennedy Clark, C Feel free to contact the author for any errors or suggestions, but the best way to report issues is via the CPAN RT Bug system at L. Copyright 2006-2010, Kennedy Clark, under the Creative Commons Attribution Share-Alike License Version 3.0 (L). Catalyst-Manual-5.9010/lib/Catalyst/Manual/Tutorial/0000755000000000000000000000000013460431137022302 5ustar00rootwheel00000000000000Catalyst-Manual-5.9010/lib/Catalyst/Manual/Tutorial/08_Testing.pod0000644000000000000000000003755613454703351024755 0ustar00rootwheel00000000000000=head1 NAME Catalyst::Manual::Tutorial::08_Testing - Catalyst Tutorial - Chapter 8: Testing =head1 OVERVIEW This is B for the Catalyst tutorial. L =over 4 =item 1 L =item 2 L =item 3 L =item 4 L =item 5 L =item 6 L =item 7 L =item 8 B<08_Testing> =item 9 L =item 10 L =back =head1 DESCRIPTION You may have noticed that the Catalyst Helper scripts automatically create basic C<.t> test scripts under the C directory. This chapter of the tutorial briefly looks at how these tests can be used not only to ensure that your application is working correctly at the present time, but also provide automated regression testing as you upgrade various pieces of your application over time. Source code for the tutorial in included in the F directory of the Tutorial Virtual machine (one subdirectory per chapter). There are also instructions for downloading the code in L. For an excellent introduction to learning the many benefits of testing your Perl applications and modules, you might want to read 'Perl Testing: A Developer's Notebook' by Ian Langworth and chromatic. =head1 RUNNING THE "CANNED" CATALYST TESTS There are a variety of ways to run Catalyst and Perl tests (for example, C and C), but one of the easiest is with the C command. For example, to run all of the tests in the C directory, enter: $ prove -wl t There will be a lot of output because we have the C<-Debug> flag enabled in F (see the C tip below for a quick and easy way to reduce the clutter). Look for lines like this for errors: # Failed test 'Request should succeed' # at t/controller_Books.t line 8. # Looks like you failed 1 test of 3. The redirection used by the Authentication plugins will cause several failures in the default tests. You can fix this by making the following changes: 1) Change the line in F that reads: ok( request('/')->is_success, 'Request should succeed' ); to: ok( request('/login')->is_success, 'Request should succeed' ); 2) Change the line in F that reads: ok( request('/logout')->is_success, 'Request should succeed' ); to: ok( request('/logout')->is_redirect, 'Request should succeed' ); 3) Change the line in F that reads: ok( request('/books')->is_success, 'Request should succeed' ); to: ok( request('/books')->is_redirect, 'Request should succeed' ); 4) Add the following statement to the top of F: use MyApp; As you can see in the C command line above, the C<-l> option (or C<--lib> if you prefer) is used to set the location of the Catalyst C directory. With this command, you will get all of the usual development server debug output, something most people prefer to disable while running tests cases. Although you can edit the F to comment out the C<-Debug> plugin, it's generally easier to simply set the C environment variable. For example: $ CATALYST_DEBUG=0 prove -wl t During the F and F tests, you might notice the C warning message. To execute the Pod-related tests, add C to the C command: $ CATALYST_DEBUG=0 TEST_POD=1 prove -wl t If you omitted the Pod comments from any of the methods that were inserted, you might have to go back and fix them to get these tests to pass. :-) Another useful option is the C (C<-v>) option to C. It prints the name of each test case as it is being run: $ CATALYST_DEBUG=0 prove -vwl t =head1 RUNNING A SINGLE TEST You can also run a single script by appending its name to the C command. For example: $ CATALYST_DEBUG=0 prove -wl t/01app.t Also note that you can also run tests directly from Perl without C. For example: $ CATALYST_DEBUG=0 perl -w -Ilib t/01app.t =head1 ADDING YOUR OWN TEST SCRIPT Although the Catalyst helper scripts provide a basic level of checks "for free," testing can become significantly more helpful when you write your own tests to exercise the various parts of your application. The L module is very popular for writing these sorts of test cases. This module extends L (and therefore L) to allow you to automate the action of a user "clicking around" inside your application. It gives you all the benefits of testing on a live system without the messiness of having to use an actual web server, and a real person to do the clicking. To create a sample test case, open the F file in your editor and enter the following: #!/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 ... 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", "Both users should have a 'User Logout'") for $ua1, $ua2; $ua1->content_contains("/books/form_create\">Admin Create", "'test01' should have a create link"); $ua2->content_lacks("/books/form_create\">Admin Create", "'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; The F test cases uses copious comments to explain each step of the process. In addition to the techniques shown here, there are a variety of other methods available in L (for example, regex-based matching). Consult L, L, L, and L for more detail. B: For I vs. the "full application tests" approach used by L, see L. B The test script does not test the C and C actions. That is left as an exercise for the reader (you should be able to complete that logic using the existing code as a template). To run the new test script, use a command such as: $ CATALYST_DEBUG=0 prove -vwl t/live_app01.t or $ DBIC_TRACE=0 CATALYST_DEBUG=0 prove -vwl t/live_app01.t Experiment with the C, C and C<-v> settings. If you find that there are errors, use the techniques discussed in the "Catalyst Debugging" section (Chapter 7) to isolate and fix any problems. If you want to run the test case under the Perl interactive debugger, try a command such as: $ DBIC_TRACE=0 CATALYST_DEBUG=0 perl -d -Ilib t/live_app01.t Note that although this tutorial uses a single custom test case for simplicity, you may wish to break your tests into different files for better organization. B If you have a test case that fails, you will receive an error similar to the following: # Failed test 'Check we are NOT logged in' # in t/live_app01.t at line 31. # searched: "\x{0a}content; This will cause the full HTML returned by the request to be displayed. Another approach to see the full HTML content at the failure point in a series of tests would be to insert a "C<$DB::single=1;> right above the location of the failure and run the test under the Perl debugger (with C<-d>) as shown above. Then you can use the debugger to explore the state of the application right before or after the failure. =head1 SUPPORTING BOTH PRODUCTION AND TEST DATABASES You may wish to leverage the techniques discussed in this tutorial to maintain both a "production database" for your live application and a "testing database" for your test cases. One advantage to L is that it runs your full application; however, this can complicate things when you want to support multiple databases. =head2 DATABASE CONFIG SWITCHING IN YOUR MODEL CLASS One solution is to allow the database specification to be overridden with an environment variable. For example, open F in your editor and change the C<< __PACKAGE__->config(... >> declaration to resemble: 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}, } ); Then, when you run your test case, you can use commands such as: $ cp myapp.db myappTEST.db $ CATALYST_DEBUG=0 MYAPP_DSN="dbi:SQLite:myappTEST.db" prove -vwl t/live_app01.t This will modify the DSN only while the test case is running. If you launch your normal application without the C environment variable defined, it will default to the same C as before. =head2 DATABASE CONFIG SWITCHING USING MULTIPLE CONFIG FILES L has functionality to load multiple config files based on environment variables, allowing you to override your default (production) database connection settings during development (or vice versa). Setting C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> to 'testing' in your test script results in loading of an additional config file named F after F which will override any parameters in F. You should set the environment variable in the BEGIN block of your test script to make sure it's set before your Catalyst application is started. The following is an example for a config and test script for a L model named MyDB and a controller named Foo: myapp_testing.conf: dsn dbi:SQLite:myapp.db t/controller_Foo.t: use strict; use warnings; use Test::More; BEGIN { $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX } = 'testing'; } eval "use Test::WWW::Mechanize::Catalyst 'MyApp'"; plan $@ ? ( skip_all => 'Test::WWW::Mechanize::Catalyst required' ) : ( tests => 2 ); ok( my $mech = Test::WWW::Mechanize::Catalyst->new, 'Created mech object' ); $mech->get_ok( 'http://localhost/foo' ); You can jump to the next chapter of the tutorial here: L =head1 AUTHOR Kennedy Clark, C Feel free to contact the author for any errors or suggestions, but the best way to report issues is via the CPAN RT Bug system at L. Copyright 2006-2011, Kennedy Clark, under the Creative Commons Attribution Share-Alike License Version 3.0 (L). Catalyst-Manual-5.9010/lib/Catalyst/Manual/Tutorial/04_BasicCRUD.pod0000644000000000000000000015417113454703351025024 0ustar00rootwheel00000000000000=head1 NAME Catalyst::Manual::Tutorial::04_BasicCRUD - Catalyst Tutorial - Chapter 4: Basic CRUD =head1 OVERVIEW This is B for the Catalyst tutorial. L =over 4 =item 1 L =item 2 L =item 3 L =item 4 B<04_Basic CRUD> =item 5 L =item 6 L =item 7 L =item 8 L =item 9 L =item 10 L =back =head1 DESCRIPTION This chapter of the tutorial builds on the fairly primitive application created in L to add basic support for Create, Read, Update, and Delete (CRUD) of C objects. Note that the 'list' function in L already implements the Read portion of CRUD (although Read normally refers to reading a single object; you could implement full Read functionality using the techniques introduced below). This section will focus on the Create and Delete aspects of CRUD. More advanced capabilities, including full Update functionality, will be addressed in L. Although this chapter of the tutorial will show you how to build CRUD functionality yourself, another option is to use a "CRUD builder" type of tool to automate the process. You get less control, but it can be quick and easy. For example, see L, L, and L. Source code for the tutorial in included in the F directory of the Tutorial Virtual machine (one subdirectory per chapter). There are also instructions for downloading the code in L. =head1 FORMLESS SUBMISSION Our initial attempt at object creation will utilize the "URL arguments" feature of Catalyst (we will employ the more common form-based submission in the sections that follow). =head2 Include a Create Action in the Books Controller Edit F and enter the following method: =head2 url_create Create a book with the supplied title, rating, and author =cut sub url_create :Local { # In addition to self & context, get the title, rating, & # author_id args from the URL. Note that Catalyst automatically # puts extra information after the "//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'); } Notice that Catalyst takes "extra slash-separated information" from the URL and passes it as arguments in C<@_> (as long as the number of arguments is not "fixed" using an attribute like C<:Args(0)>). The C action then uses a simple call to the DBIC C method to add the requested information to the database (with a separate call to C to update the join table). As do virtually all controller methods (at least the ones that directly handle user input), it then sets the template that should handle this request. Also note that we are explicitly setting a C "Cache-Control" header to force browsers using the page to get a fresh copy every time. You could even move this to a C method in F and it would automatically get applied to every page in the whole application via a single line of code (remember from Chapter 3, that every C method gets run in the Controller hierarchy). =head2 Include a Template for the 'url_create' Action: Edit F and then enter: [% # 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. -%]

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 %].

[% # Provide a link back to the list page. 'c.uri_for' builds -%] [% # a full URI; e.g., 'http://localhost:3000/books/list' -%]

Return to list

[% # Try out the TT Dumper (for development only!) -%]
    Dump of the 'book' variable:
    [% Dumper.dump(book) %]
    
The TT C directive allows access to a variety of plugin modules (TT plugins, that is, not Catalyst plugins) to add extra functionality to the base TT capabilities. Here, the plugin allows L "pretty printing" of objects and variables. Other than that, the rest of the code should be familiar from the examples in Chapter 3. =head2 Try the 'url_create' Feature Make sure the development server is running with the "-r" restart option: $ DBIC_TRACE=1 script/myapp_server.pl -r Note that new path for C appears in the startup debug output. Next, use your browser to enter the following URL: http://localhost:3000/books/url_create/TCPIP_Illustrated_Vol-2/5/4 Your browser should display "Added book 'TCPIP_Illustrated_Vol-2' by 'Stevens' with a rating of 5." along with a dump of the new book model object as it was returned by DBIC. You should also see the following DBIC debug messages displayed in the development server log messages if you have DBIC_TRACE set: INSERT INTO book (rating, title) VALUES (?, ?): `5', `TCPIP_Illustrated_Vol-2' INSERT INTO book_author (author_id, book_id) VALUES (?, ?): `4', `6' The C statements are obviously adding the book and linking it to the existing record for Richard Stevens. The C Rating: Author ID: Note that we have specified the target of the form data as C, the method created in the section that follows. =head2 Add a Method to Process Form Values and Update Database Edit F and add the following method to save the form information to the database: =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 Test Out The Form Notice that the server startup log reflects the two new chained methods that we added: [debug] Loaded Chained actions: .-------------------------------------+--------------------------------------. | Path Spec | Private | +-------------------------------------+--------------------------------------+ | /books/form_create | /books/base (0) | | | => /books/form_create | | /books/form_create_do | /books/base (0) | | | => /books/form_create_do | | /books/url_create/*/*/* | /books/base (0) | | | => /books/url_create | '-------------------------------------+--------------------------------------' Point your browser to L and enter "TCP/IP Illustrated, Vol 3" for the title, a rating of 5, and an author ID of 4. You should then see the output of the same F template seen in earlier examples. Finally, click "Return to list" to view the full list of books. B Having the user enter the primary key ID for the author is obviously crude; we will address this concern with a drop-down list and add validation to our forms in L. =head1 A SIMPLE DELETE FEATURE Turning our attention to the Delete portion of CRUD, this section illustrates some basic techniques that can be used to remove information from the database. =head2 Include a Delete Link in the List Edit F and update it to match the following (two sections have changed: 1) the additional 'Links' table header, and 2) the five lines for the Delete link near the bottom): [% # 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 -%] [% # Display each book in a table row %] [% FOREACH book IN books -%] [% END -%]
TitleRatingAuthor(s)Links
[% book.title %] [% book.rating %] [% # 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 %] [% # Add a link to delete a book %] Delete
The additional code is obviously designed to add a new column to the right side of the table with a C "button" (for simplicity, links will be used instead of full HTML buttons; but, in practice, anything that modifies data should be handled with a form sending a POST request). Also notice that we are using a more advanced form of C than we have seen before. Here we use C<< $c->controller->action_for >> to automatically generate a URI appropriate for that action based on the method we want to link to while inserting the C value into the appropriate place. Now, if you ever change C<:PathPart('delete')> in your controller method to something like C<:PathPart('kill')>, then your links will automatically update without any changes to your .tt2 template file. As long as the name of your method does not change (here, "delete"), then your links will still be correct. There are a few shortcuts and options when using C: =over 4 =item * If you are referring to a method in the current controller, you can use C<< $self->action_for('_method_name_') >>. =item * If you are referring to a method in a different controller, you need to include that controller's name as an argument to C, as in C<< $c->controller('_controller_name_')->action_for('_method_name_') >>. =back B In practice you should B use a GET request to delete a record -- always use POST for actions that will modify data. We are doing it here for illustrative and simplicity purposes only. =head2 Add a Common Method to Retrieve a Book for the Chain As mentioned earlier, since we have a mixture of actions that operate on a single book ID and others that do not, we should not have C capture the book ID, find the corresponding book in the database and save it in the stash for later links in the chain. However, just because that logic does not belong in C doesn't mean that we can't create another location to centralize the book lookup code. In our case, we will create a method called C that will store the specific book in the stash. Chains that always operate on a single existing book can chain off this method, but methods such as C that don't operate on an existing book can chain directly off base. To add the C method, edit F and add the following code: =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 ***"); } Now, any other method that chains off C will automatically have the appropriate book waiting for it in C<< $c->stash->{object} >>. =head2 Add a Delete Action to the Controller Open F in your editor and add the following method: =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; # Set a status message to be displayed at the top of the view $c->stash->{status_msg} = "Book deleted."; # Forward to the list action/method in this controller $c->forward('list'); } This method first deletes the book object saved by the C method. However, it also removes the corresponding entry from the C table with a cascading delete. Then, rather than forwarding to a "delete done" page as we did with the earlier create example, it simply sets the C to display a notification to the user as the normal list view is rendered. The C action uses the context C method to return the user to the book list. The C method could have also been used. Whereas C I to the original action once it is completed, C does I return. Other than that, the two are equivalent. =head2 Try the Delete Feature Once you save the Books controller, the server should automatically restart. The C method should now appear in the "Loaded Chained actions" section of the startup debug output: [debug] Loaded Chained actions: .-------------------------------------+--------------------------------------. | Path Spec | Private | +-------------------------------------+--------------------------------------+ | /books/id/*/delete | /books/base (0) | | | -> /books/object (1) | | | => /books/delete | | /books/form_create | /books/base (0) | | | => /books/form_create | | /books/form_create_do | /books/base (0) | | | => /books/form_create_do | | /books/url_create/*/*/* | /books/base (0) | | | => /books/url_create | '-------------------------------------+--------------------------------------' Then point your browser to L and click the "Delete" link next to the first "TCPIP_Illustrated_Vol-2". A green "Book deleted" status message should display at the top of the page, along with a list of the eight remaining books. You will also see the cascading delete operation via the DBIC_TRACE output: SELECT me.id, me.title, me.rating FROM book me WHERE ( ( me.id = ? ) ): '6' DELETE FROM book WHERE ( id = ? ): '6' If you get the error C then you probably forgot to uncomment the template line in C at the end of chapter 3. =head2 Fixing a Dangerous URL Note the URL in your browser once you have performed the deletion in the prior step -- it is still referencing the delete action: http://localhost:3000/books/id/6/delete What if the user were to press reload with this URL still active? In this case the redundant delete is harmless (although it does generate an exception screen, it doesn't perform any undesirable actions on the application or database), but in other cases this could clearly lead to trouble. We can improve the logic by converting to a redirect. Unlike C<< $c->forward('list')) >> or C<< $c->detach('list')) >> that perform a server-side alteration in the flow of processing, a redirect is a client-side mechanism that causes the browser to issue an entirely new request. As a result, the URL in the browser is updated to match the destination of the redirection URL. To convert the forward used in the previous section to a redirect, open F and edit the existing C method to match: =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; # Set a status message to be displayed at the top of the view $c->stash->{status_msg} = "Book deleted."; # Redirect the user back to the list page. Note the use # of $self->action_for as earlier in this section (BasicCRUD) $c->response->redirect($c->uri_for($self->action_for('list'))); } =head2 Try the Delete and Redirect Logic Point your browser to L (don't just hit "Refresh" in your browser since we left the URL in an invalid state in the previous section!) and delete the first copy of the remaining two "TCPIP_Illustrated_Vol-2" books. The URL in your browser should return to the L URL, so that is an improvement, but notice that I. Because the stash is reset on every request (and a redirect involves a second request), the C is cleared before it can be displayed. =head2 Using 'uri_for' to Pass Query Parameters There are several ways to pass information across a redirect. One option is to use the C technique that we will see in L of this tutorial; however, here we will pass the information via query parameters on the redirect itself. Open F and update the existing C method to match the following: =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."})); } This modification simply leverages the ability of C to include an arbitrary number of name/value pairs in a hash reference. Next, we need to update F to handle C as a query parameter: ...
[%# Status and error messages %] [% status_msg || c.request.params.status_msg | html %] [% error_msg %] [%# This is where TT will stick all of your template's contents. -%] [% content %]
... Although the sample above only shows the C div, leave the rest of the file intact -- the only change we made to the F was to add "C<|| c.request.params.status_msg>" to the C<< >> line. Note that we definitely want the "C<| html>" TT filter here since it would be easy for users to modify the message on the URL and possibly inject harmful code into the application if we left that off. =head2 Try the Delete and Redirect With Query Param Logic Point your browser to L (you should now be able to safely hit "refresh" in your browser). Then delete the remaining copy of "TCPIP_Illustrated_Vol-2". The green "Book deleted" status message should return. But notice that you can now hit the "Reload" button in your browser and it just redisplays the book list (and it correctly shows it without the "Book deleted" message on redisplay). B Be sure to check out L where we use an improved technique that is better suited to your real world applications. =head1 EXPLORING THE POWER OF DBIC In this section we will explore some additional capabilities offered by L. Although these features have relatively little to do with Catalyst per se, you will almost certainly want to take advantage of them in your applications. =head2 Add Datetime Columns to Our Existing Books Table Let's add two columns to our existing C table to track when each book was added and when each book is updated: $ sqlite3 myapp.db sqlite> ALTER TABLE book ADD created TIMESTAMP; sqlite> ALTER TABLE book ADD updated TIMESTAMP; sqlite> UPDATE book SET created = DATETIME('NOW'), updated = DATETIME('NOW'); sqlite> SELECT * FROM book; 1|CCSP SNRS Exam Certification Guide|5|2010-02-16 04:15:45|2010-02-16 04:15:45 2|TCP/IP Illustrated, Volume 1|5|2010-02-16 04:15:45|2010-02-16 04:15:45 3|Internetworking with TCP/IP Vol.1|4|2010-02-16 04:15:45|2010-02-16 04:15:45 4|Perl Cookbook|5|2010-02-16 04:15:45|2010-02-16 04:15:45 5|Designing with Web Standards|5|2010-02-16 04:15:45|2010-02-16 04:15:45 9|TCP/IP Illustrated, Vol 3|5|2010-02-16 04:15:45|2010-02-16 04:15:45 sqlite> .quit $ Here are the commands without the surrounding sqlite3 prompt and output in case you want to cut and paste them as a single block (but still start sqlite3 before you paste these in): ALTER TABLE book ADD created TIMESTAMP; ALTER TABLE book ADD updated TIMESTAMP; UPDATE book SET created = DATETIME('NOW'), updated = DATETIME('NOW'); SELECT * FROM book; This will modify the C table to include the two new fields and populate those fields with the current time. =head2 Update DBIx::Class to Automatically Handle the Datetime Columns Next, we should re-run the DBIC helper to update the Result Classes with the new fields: $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \ create=static components=TimeStamp dbi:SQLite:myapp.db \ on_connect_do="PRAGMA foreign_keys = ON" exists "/home/catalyst/dev/MyApp/script/../lib/MyApp/Model" exists "/home/catalyst/dev/MyApp/script/../t" Dumping manual schema for MyApp::Schema to directory /home/catalyst/dev/MyApp/script/../lib ... Schema dump completed. exists "/home/catalyst/dev/MyApp/script/../lib/MyApp/Model/DB.pm" Notice that we modified our use of the helper slightly: we told it to include the L in the C line of the Result Classes. If you open F in your editor you should see that the C and C fields are now included in the call to C. However, also notice that the C relationships we manually added below the "C<# DO NOT MODIFY...>" line were automatically preserved. While we F open, let's update it with some additional information to have DBIC automatically handle the updating of these two fields for us. Insert the following code at the bottom of the file (it B be B the "C<# DO NOT MODIFY...>" line and B the C<1;> on the last line): # # 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 }, ); This will override the definition for these fields that Schema::Loader placed at the top of the file. The C and C options will cause DBIx::Class to automatically update the timestamps in these columns whenever a row is created or modified. B that adding the lines above will cause the development server to automatically restart if you are running it with the "-r" option. In other words, the development server is smart enough to restart not only for code under the F, F, and F directories, but also under other directions such as our "external DBIC model" in F. However, also note that it's smart enough to B restart when you edit your C<.tt2> files under F. Then enter the following URL into your web browser: http://localhost:3000/books/url_create/TCPIP_Illustrated_Vol-2/5/4 You should get the same "Book Created" screen we saw earlier. However, if you now use the sqlite3 command-line tool to dump the C table, you will see that the new book we added has an appropriate date and time entered for it (see the last line in the listing below): $ sqlite3 myapp.db "select * from book" 1|CCSP SNRS Exam Certification Guide|5|2010-02-16 04:15:45|2010-02-16 04:15:45 2|TCP/IP Illustrated, Volume 1|5|2010-02-16 04:15:45|2010-02-16 04:15:45 3|Internetworking with TCP/IP Vol.1|4|2010-02-16 04:15:45|2010-02-16 04:15:45 4|Perl Cookbook|5|2010-02-16 04:15:45|2010-02-16 04:15:45 5|Designing with Web Standards|5|2010-02-16 04:15:45|2010-02-16 04:15:45 9|TCP/IP Illustrated, Vol 3|5|2010-02-16 04:15:45|2010-02-16 04:15:45 10|TCPIP_Illustrated_Vol-2|5|2010-02-16 04:18:42|2010-02-16 04:18:42 Notice in the debug log that the SQL DBIC generated has changed to incorporate the datetime logic: INSERT INTO book ( created, rating, title, updated ) VALUES ( ?, ?, ?, ? ): '2010-02-16 04:18:42', '5', 'TCPIP_Illustrated_Vol-2', '2010-02-16 04:18:42' INSERT INTO book_author ( author_id, book_id ) VALUES ( ?, ? ): '4', '10' =head2 Create a ResultSet Class An often overlooked but extremely powerful features of DBIC is that it allows you to supply your own subclasses of L. This can be used to pull complex and unsightly "query code" out of your controllers and encapsulate it in a method of your ResultSet Class. These "canned queries" in your ResultSet Class can then be invoked via a single call, resulting in much cleaner and easier to read controller code (or View code, if that's where you want to call it). To illustrate the concept with a fairly simple example, let's create a method that returns books added in the last 10 minutes. Start by making a directory where DBIx::Class will look for our ResultSet Class: $ mkdir lib/MyApp/Schema/ResultSet Then open F and enter the following: 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 } }); } 1; Then add the following method to the F: =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'); } Now try different values for the "minutes" argument (the final number value) using the URL C in your browser. For example, this would list all books added in the last fifteen minutes: http://localhost:3000/books/list_recent/15 Depending on how recently you added books, you might want to try a higher or lower value for the minutes. =head2 Chaining ResultSets One of the most helpful and powerful features in L is that it allows you to "chain together" a series of queries (note that this has nothing to do with the "Chained Dispatch" for Catalyst that we were discussing earlier). Because each ResultSet method returns another ResultSet, you can take an initial query and immediately feed that into a second query (and so on for as many queries you need). Note that no matter how many ResultSets you chain together, the database itself will not be hit until you use a method that attempts to access the data. And, because this technique carries over to the ResultSet Class feature we implemented in the previous section for our "canned search", we can combine the two capabilities. For example, let's add an action to our C controller that lists books that are both recent I have "TCP" in the title. Open up F and add the following method: =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)) ->search({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'); } To try this out, enter the following URL into your browser: http://localhost:3000/books/list_recent_tcp/100 And you should get a list of books added in the last 100 minutes that contain the string "TCP" in the title. However, if you look at all books within the last 100 minutes, you should get a longer list (again, you might have to adjust the number of minutes depending on how recently you added books to your database): http://localhost:3000/books/list_recent/100 Take a look at the DBIC_TRACE output in the development server log for the first URL and you should see something similar to the following: SELECT me.id, me.title, me.rating, me.created, me.updated FROM book me WHERE ( ( title LIKE ? AND created > ? ) ): '%TCP%', '2010-02-16 02:49:32' However, let's not pollute our controller code with this raw "TCP" query -- it would be cleaner to encapsulate that code in a method on our ResultSet Class. To do this, open F and add the following method: =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%" } }); } We defined the search string as C<$title_str> to make the method more flexible. Now update the C method in F to match the following (we have replaced the C<< ->search >> line with the C<< ->title_like >> line shown here -- the rest of the method should be the same): =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'); } Try out the C and C URLs as we did above. They should work just the same, but our code is obviously cleaner and more modular, while also being more flexible at the same time. =head2 Adding Methods to Result Classes In the previous two sections we saw a good example of how we could use DBIx::Class ResultSet Classes to clean up our code for an entire query (for example, our "canned searches" that filtered the entire query). We can do a similar improvement when working with individual rows as well. Whereas the ResultSet construct is used in DBIC to correspond to an entire query, the Result Class construct is used to represent a row. Therefore, we can add row-specific "helper methods" to our Result Classes stored in F. For example, open F and add the following method (as always, it must be above the closing "C<1;>"): # # Row-level helper methods # sub full_name { my ($self) = @_; return $self->first_name . ' ' . $self->last_name; } This will allow us to conveniently retrieve both the first and last name for an author in one shot. Now open F and change the definition of C from this: ... [% tt_authors = [ ]; tt_authors.push(author.last_name) FOREACH author = book.authors %] ... to: ... [% tt_authors = [ ]; tt_authors.push(author.full_name) FOREACH author = book.authors %] ... (Only C was changed to C -- the rest of the file should remain the same.) Now go to the standard book list URL: http://localhost:3000/books/list The "Author(s)" column will now contain both the first and last name. And, because the concatenation logic was encapsulated inside our Result Class, it keeps the code inside our TT template nice and clean (remember, we want the templates to be as close to pure HTML markup as possible). Obviously, this capability becomes even more useful as you use it to remove even more complicated row-specific logic from your templates! =head2 Moving Complicated View Code to the Model The previous section illustrated how we could use a Result Class method to print the full names of the authors without adding any extra code to our view, but it still left us with a fairly ugly mess (see F): ... [% # NOTE: See Chapter 4 for a better way to do this! -%] [% # First initialize a TT variable to hold a list. Then use a TT FOREACH -%] [% # loop in 'side effect notation' to load just the last names of the -%] [% # authors into the list. Note that the 'push' TT vmethod does not print -%] [% # a value, so nothing will be printed here. But, if you have something -%] [% # in TT that does return a method and you don't want it printed, you -%] [% # can: 1) assign it to a bogus value, or 2) use the CALL keyword to -%] [% # call it and discard the return value. -%] [% tt_authors = [ ]; tt_authors.push(author.full_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 %] ... Let's combine some of the techniques used earlier in this section to clean this up. First, let's add a method to our Book Result Class to return the number of authors for a book. Open F and add the following method: =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; } Next, let's add a method to return a list of authors for a book to the same F file: =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); } This method loops through each author, using the C Result Class method we added to F in the prior section. Using these two methods, we can simplify our TT code. Open F and update the "Author(s)" table cell to match the following: ... [% # Print count and author list using Result Class methods -%] ([% book.author_count | html %]) [% book.author_list | html %] ... Although most of the code we removed comprised comments, the overall effect is dramatic... because our view code is so simple, we don't need huge comments to clue people in to the gist of our code. The view code is now self-documenting and readable enough that you could probably get by with no comments at all. All of the "complex" work is being done in our Result Class methods (and, because we have broken the code into nice, modular chunks, the Result Class code is hardly something you would call complex). As we saw in this section, always strive to keep your view AND controller code as simple as possible by pulling code out into your model objects. Because L can be easily extended in so many ways, it's an excellent to way accomplish this objective. It will make your code cleaner, easier to write, less error-prone, and easier to debug and maintain. Before you conclude this section, hit Refresh in your browser... the output should be the same even though the backend code has been trimmed down. You can jump to the next chapter of the tutorial here: L =head1 AUTHOR Kennedy Clark, C Feel free to contact the author for any errors or suggestions, but the best way to report issues is via the CPAN RT Bug system at L. Copyright 2006-2011, Kennedy Clark, under the Creative Commons Attribution Share-Alike License Version 3.0 (L). Catalyst-Manual-5.9010/lib/Catalyst/Manual/Tutorial/09_AdvancedCRUD.pod0000644000000000000000000000465013454703351025511 0ustar00rootwheel00000000000000=head1 NAME Catalyst::Manual::Tutorial::09_AdvancedCRUD - Catalyst Tutorial - Chapter 9: Advanced CRUD =head1 OVERVIEW This is B for the Catalyst tutorial. L =over 4 =item 1 L =item 2 L =item 3 L =item 4 L =item 5 L =item 6 L =item 7 L =item 8 L =item 9 B<09_Advanced CRUD> =item 10 L =back =head1 DESCRIPTION This chapter of the tutorial explores more advanced functionality for Create, Read, Update, and Delete (CRUD) than we saw in L. In particular, it looks at a number of techniques that can be useful for the Update portion of CRUD, such as automated form generation, validation of user-entered data, and automated transfer of data between forms and model objects. In keeping with the Catalyst (and Perl) spirit of flexibility, there are many different ways to approach advanced CRUD operations in a Catalyst environment. Therefore, this section of the tutorial allows you to pick from one of several modules that cover different form management tools. Select one or more options from the list below. =head1 ADVANCED CRUD OPTIONS =over 4 =item * L =item * L =item * L =back B Please contact the author if you would like to assist with writing a new module. =head1 AUTHOR Kennedy Clark, C Feel free to contact the author for any errors or suggestions, but the best way to report issues is via the CPAN RT Bug system at L. Copyright 2006-2011, Kennedy Clark, under the Creative Commons Attribution Share-Alike License Version 3.0 (L). Catalyst-Manual-5.9010/lib/Catalyst/Manual/Tutorial/05_Authentication.pod0000644000000000000000000011131613454703351026277 0ustar00rootwheel00000000000000=head1 NAME Catalyst::Manual::Tutorial::05_Authentication - Catalyst Tutorial - Chapter 5: Authentication =head1 OVERVIEW This is B for the Catalyst tutorial. L =over 4 =item 1 L =item 2 L =item 3 L =item 4 L =item 5 B<05_Authentication> =item 6 L =item 7 L =item 8 L =item 9 L =item 10 L =back =head1 DESCRIPTION Now that we finally have a simple yet functional application, we can focus on providing authentication (with authorization coming next in L). This chapter of the tutorial is divided into two main sections: 1) basic, cleartext authentication and 2) hash-based authentication. Source code for the tutorial in included in the F directory of the Tutorial Virtual machine (one subdirectory per chapter). There are also instructions for downloading the code in L. =head1 BASIC AUTHENTICATION This section explores how to add authentication logic to a Catalyst application. =head2 Add Users and Roles to the Database First, we add both user and role information to the database (we will add the role information here although it will not be used until the authorization section, Chapter 6). Create a new SQL script file by opening F in your editor and insert: -- -- 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@na.com', 'Joe', 'Blow', 1); INSERT INTO users VALUES (2, 'test02', 'mypass', 't02@na.com', 'Jane', 'Doe', 1); INSERT INTO users VALUES (3, 'test03', 'mypass', 't03@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); Then load this into the F database with the following command: $ sqlite3 myapp.db < myapp02.sql =head2 Add User and Role Information to DBIC Schema Although we could manually edit the DBIC schema information to include the new tables added in the previous step, let's use the C option on the DBIC model helper to do most of the work for us: $ script/myapp_create.pl model DB DBIC::Schema MyApp::Schema \ create=static components=TimeStamp dbi:SQLite:myapp.db \ on_connect_do="PRAGMA foreign_keys = ON" exists "/home/catalyst/dev/MyApp/script/../lib/MyApp/Model" exists "/home/catalyst/dev/MyApp/script/../t" Dumping manual schema for MyApp::Schema to directory /home/catalyst/dev/MyApp/script/../lib ... Schema dump completed. exists "/home/catalyst/dev/MyApp/script/../lib/MyApp/Model/DB.pm" $ $ ls lib/MyApp/Schema/Result Author.pm BookAuthor.pm Book.pm Role.pm User.pm UserRole.pm Notice how the helper has added three new table-specific Result Source files to the F directory. And, more importantly, even if there were changes to the existing result source files, those changes would have only been written above the C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!> comment and your hand-edited enhancements would have been preserved. Speaking of "hand-edited enhancements," we should now add the C relationship information to the User Result Source file. As with the Book, BookAuthor, and Author files in L, L has automatically created the C and C relationships for the new User, UserRole, and Role tables. However, as a convenience for mapping Users to their assigned roles (see L), we will also manually add a C relationship. Edit F add the following information between the C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!> comment and the closing C<1;>: # 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'); The code for this update is obviously very similar to the edits we made to the C and C classes created in L with one exception: we only defined the C relationship in one direction. Whereas we felt that we would want to map Authors to Books B Books to Authors, here we are only adding the convenience C in the Users to Roles direction. Note that we do not need to make any change to the F schema file. It simply tells DBIC to load all of the Result Class and ResultSet Class files it finds below the F directory, so it will automatically pick up our new table information. =head2 Sanity-Check of the Development Server Reload We aren't ready to try out the authentication just yet; we only want to do a quick check to be sure our model loads correctly. Assuming that you are following along and using the "-r" option on F, then the development server should automatically reload (if not, press C to break out of the server if it's running and then enter F