]> git.decadent.org.uk Git - maypole.git/commitdiff
+ Updated manual, thanks to Dave Howorth
authorSimon Cozens <simon@simon-cozens.org>
Thu, 23 Dec 2004 14:45:28 +0000 (14:45 +0000)
committerSimon Cozens <simon@simon-cozens.org>
Thu, 23 Dec 2004 14:45:28 +0000 (14:45 +0000)
+ update Author in Maypole::Manual

git-svn-id: http://svn.maypole.perl.org/Maypole/trunk@322 48953598-375a-da11-a14b-00016c27c3ee

lib/Maypole/Manual.pod
lib/Maypole/Manual/About.pod
lib/Maypole/Manual/Beer.pod
lib/Maypole/Manual/BuySpy.pod
lib/Maypole/Manual/Flox.pod
lib/Maypole/Manual/Model.pod
lib/Maypole/Manual/Request.pod
lib/Maypole/Manual/StandardTemplates.pod
lib/Maypole/Manual/View.pod
lib/Maypole/Manual/Workflow.pod
lib/Maypole/View/TT.pm

index 8de39b02b9b4e374be131bb2c07e125e0f32b34b..c8998547d8fb81d4a27218ecb8185e8683a97311 100644 (file)
@@ -10,17 +10,20 @@ be useful.
 
 =item L<Maypole::Manual::About> - Overview of the Project
 
-This document is a general introduction to Maypole: What it is
-(a framework for Web development), what it does (at the basic
-level, it converts a URL
-(e.g. C<http://www.mysite.com/product/display/12>) into a
-method call (i.e. "perform the C<display> method on item C<12>
-in the C<product> table") and then shows the result (here,
-presumably, a description of item C<12> in your product
-database)), and how it works (by MVC, a design paradigm in
+This document is a general introduction to Maypole: what it is, what it
+does and how it works.
+
+Maypole is a framework for Web development. At the basic level, it
+converts a URL like C<http://www.mysite.com/product/display/12> into a
+method call such as "perform the C<display> method on item C<12> in the
+C<product> table" and then shows the result: here, presumably, a
+description of item C<12> in your product database,
+
+It is based on Model-View-Controller (MVC), a design paradigm in
 which each major aspect of an application's operation is
-handled by a different and totally separate system). Basic
-installation instructions are given. A sample Web
+handled by a different and totally separate system).
+
+Basic installation instructions are given. A sample Web
 application--the Beer database--is introduced, set up, and
 discussed. Finally, the path a Maypole request takes as it
 moves through the system is described.
@@ -35,13 +38,13 @@ which basically creates a class for each table in your
 database and provides a variety of convenient methods for
 manipulating each table and its relations. It integrates very
 smoothly with Maypole's default L<view class|Maypole::Manual::View>,
-the L<Template> Toolkit.
+the L<Template|Template> Toolkit.
 
 =item L<Maypole::Manual::View> - View Classes *
 
-This document is an extensive discussion of Maypole's I<view
-class>, which takes the data produced by the model (see
-L<above|Maypole::Manual::Model>) and sends it through a templating
+This document is an extensive discussion of Maypole's I<view class>,
+which takes the data produced by the model (see
+above) and sends it through a templating
 system in order to produce output. It focusses chiefly on
 the L<Template> Toolkit, which is Maypole's default templating
 system, but discusses other possibilities.
@@ -54,8 +57,8 @@ operate on your database) include C<list>, which generates a
 paged list of a table suitable for browsing, and C<search>, which
 handles a search query and generates search results.
 
-The standard templates (which generate output for display on
-the Web) include C<list>, which displays the entries in a
+The standard templateswhich generate output for display on
+the Web, also include C<list>, which displays the entries in a
 table, and C<search>, which displays the result of a search.
 
 You'll note that most actions are associated with templates.
@@ -64,7 +67,7 @@ This document also introduces the theory behind Maypole's
 actions and templates, showing you how to write your own
 so that you can have a highly customized application.
 
-=item L<Maypole::Manual::Workflow> - Description of the Maypole Workflow 
+=item L<Maypole::Manual::Workflow> - Description of the Request Workflow 
 
 This is a technical document that describes the progress of a
 request through the entire Maypole system. It should be of
@@ -102,19 +105,22 @@ better as a Maypole application.
 
   * indicates incomplete chapters.
 
-=head1 SEE ALSO
-
-L<http://maypole.perl.org>
 
 =head1 AUTHOR
 
-Sebastian Riedel, C<sri@oook.de>
+The Maypole Manual was written by Simon Cozens. A generous grant from the Perl
+Foundation in the first quarter of 2004 funded some of the chapters of this
+manual.
 
-This overview written by Jesse Sheidlower, C<jester@panix.com>,
+This overview was rewritten by Jesse Sheidlower, C<jester#panix.com>,
 based on Simon Cozens' original I<Overview> document.
 
+In December 2004, Dave Howorth, C<dave.howorth#acm.org> kindly donated some
+of his spare time to improve the structure of the manual and bring it up to
+date.
+
 =head1 AUTHOR EMERITUS
 
-Simon Cozens, C<simon@cpan.org>
+Simon Cozens, C<simon#cpan.org>
 
 =cut
index 57c616f9e1447a14713f24f8df9612f73704cecf..f2ab8598b155f48b79b0b3868dd45d01a398be3f 100644 (file)
@@ -1,8 +1,8 @@
 
-=head1 Introduction to the Maypole Request Model
+=head1 Introduction to Maypole
 
 This chapter serves as a gentle introduction to Maypole and setting up
-Maypole applications. We look at Maypole is, how to get it up and
+Maypole applications. We look at what Maypole is, how to get it up and
 running, and how to start thinking about building Maypole applications.
 
 =head2 What is Maypole?
@@ -51,7 +51,7 @@ your data, a View class which is responsible for displaying that data to
 the user, and a Controller class which controls the other classes in
 response to events triggered by the user. This analogy doesn't
 correspond precisely to a web-based application, but we can take an
-important principle from it. As Andy Wardley explains:
+important principle from it. As Template Toolkit author Andy Wardley explains:
 
     What the MVC-for-the-web crowd are really trying to achieve is a clear
     separation of concerns.  Put your database code in one place, your 
@@ -109,6 +109,10 @@ Apache C<mod_perl>, with the C<Apache::Request> module installed, but as
 I said, it is a blank slate, and everything is customizable. There is a
 C<CGI::Maypole> frontend available to run as a standalone CGI script.
 
+As well as the documentation embedded in the Perl modules the distribution
+also includes the manual, of which this is a part. You can access it using the
+perldoc command, the man command, or by browsing CPAN.
+
 =head2 The Beer Database example
 
 Throughout this manual, we're going to be referring back to a particular
@@ -116,9 +120,9 @@ application so that we can give concrete examples for the concepts we're
 talking about. We could say "C<related_accessors> returns a list of
 accessors which can be called to return a list of objects in a has-a
 relationship to the original", or we could say "if we call
-C<related_accessors> on while viewing C<brewery>, it returns C<beers>,
+C<related_accessors> while viewing a C<brewery>, it returns C<beers>,
 because we can call C<beers> on a C<brewery> object to get a list of
-that berwery's beers." 
+that brewery's beers." 
 
 Because Maypole is all about beer. If you look carefully, you can
 probably see men playing cricket on the village green. The first
@@ -183,24 +187,30 @@ with a web interface.
 
 The first thing we need for a Maypole interface to a database is to
 have a database. If you don't have one, now would be a good time to
-create one, using the schema above.
+create one, using the schema above. If you're creating a database
+by hand, don't forget to grant permissions for your Apache server to
+access it as well as yourself (typically a user name like C<www-data>
+or C<wwwrun>).
 
 The next thing we need is a module which is going to do all the work.
 Thankfully, it doesn't need to do B<all> the work itself. It's going to be a 
 subclass of C<Maypole> or a Maypole front-end like C<Apache::MVC>. 
+It roughly corresponds to the controller in an MVC design, and is
+also referred to as the driver, handler or request.
 
 Here's the driver class for our beer database application. We're not
-going to go into much detail about it here; we'll do that in L<Beer.pod>.
+going to go into much detail about it here; we'll do that in the
+L<Beer Database|Maypole::Manual::Beer> chapter.
 For now, simply admire its brevity, as you realise this is all the code
 you need to write for a simple database front-end:
 
     package BeerDB;
     use Maypole::Application;
     BeerDB->setup("dbi:SQLite:t/beerdb.db");
-    BeerDB->config->{uri_base} = "http://localhost/beerdb";
-    BeerDB->config->{template_root} = "/path/to/templates";
-    BeerDB->config->{rows_per_page} = 10;
-    BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
+    BeerDB->config->uri_base("http://localhost/beerdb");
+    BeerDB->config->template_root("/path/to/templates");
+    BeerDB->config->rows_per_page(10);
+    BeerDB->config->display_tables([qw[beer brewery pub style]]);
     BeerDB::Brewery->untaint_columns( printable => [qw/name notes url/] );
     BeerDB::Style->untaint_columns( printable => [qw/name notes/] );
     BeerDB::Beer->untaint_columns(
@@ -216,27 +226,46 @@ you need to write for a simple database front-end:
         "a pub has beers on handpumps");
     1;
 
-This defines the C<BeerDB> application, which, as it inherits from 
-C<Apache::MVC>, will be a mod_perl handler. This means we need to
-tell the Apache configuration about it:
+There's a version of this program in the F<ex/> directory in the Maypole
+files that you downloaded in the F<~root/.cpan/> build area.
+This defines the C<BeerDB> application.
+To set it up as a mod_perl handler, just tell the Apache configuration
+about it:
 
     <Location /beerdb>
         SetHandler perl-script
         PerlHandler BeerDB
     </Location>
 
-And now we need some templates. As we'll see in the chapter on views,
-L<View.pod>, there are several types of template. We're going to copy
+To use it as a CGI script, put it in your F<cgi-bin> directory,
+together with a small file called F<beer.cgi>:
+
+    #!/usr/bin/perl
+    use strict;
+    use warnings;
+    use BeerDB;
+    BeerDB->run();
+
+and change one line in C<BeerDB.pm>:
+
+    BeerDB->config->uri_base("http://localhost/cgi-bin/beer.cgi");
+
+And now we need some templates. As we'll see in the chapter on
+L<views|Maypole::Manual::View>, there are several types of template.
+We're going to copy
 the whole lot from the F<templates/> directory of the Maypole source
 package into the F</beerdb> directory under our web root.
+Make the C<template_root> in C<BeerDB> agree with your path.
 
 And that's it. We should now be able to go to C<http://localhost/beerdb/>
+or C<http://localhost/cgi-bin/beer.cgi/>
 and see a menu of things to browse; C<http://localhost/beerdb/beer/list>
 will give a list of beers. There might not be any yet. There's a box
 that lets you add them.
 
-If you have any problems getting to this point, you might want to look
-at L<http://maypole.perl.org>.
+If you have any problems getting to this point, you might want to look at
+L<http://maypole.perl.org>. There's a FAQ and a link to a mailing
+list.
 
 Play about with the site. Add some beers. Maybe go out and buy some beers
 to review if you need some inspiration. Don't be ill on my carpet.
@@ -257,10 +286,14 @@ do all sorts of interesting things with our database, and most of the rest
 of this manual will be about how to do that.
 
 In order to do that, we need to look at what Maypole's actually doing.
+Here's a quick overview, there's more detail in the
+L<Workflow|Maypole::Manual::Workflow> chapter.
 
 As mentioned, Maypole is responsible for turning a URL into an object, a
-method call, and some templated output. Here's a handy diagram to
-explain how it does that:
+method call, and some templated output.
+
+=for html
+Here's a handy diagram to explain how it does that:
 
 =for html
 <IMG SRC="maypole_process2.png">
@@ -311,3 +344,8 @@ Then it calls C<BeerDB::Beer-E<gt>view>, passing in the request object
 as a parameter, and passes the whole lot to the view class for templating.
 In the next two chapters, we'll look at how Maypole's default model and
 view classes generally do what you want them to do.
+
+=head2 Links
+
+L<Contents|Maypole::Manual>,
+Next L<Maypole Model Classes|Maypole::Manual::Model>
index a1c85f20297ec793cafaf12eb43fdc75fb40ea75..39c8462d73c62df54e208576eee27ef42a6c182e 100644 (file)
@@ -1,7 +1,8 @@
 =head1 The Beer Database, Twice
 
-We briefly introduced the "beer database" example in the L<About.pod>
-material, where we presented its driver class as a fait accompli. Where
+We briefly introduced the "beer database" example in the
+L<Introduction to Maypole|Maypole::Manual::About> chapter, where we
+presented its driver class, C<BeerDB.pm>, as a fait accompli. Where
 did all that code come from, and what does it actually mean?
 
 =head2 The big beer problem
@@ -45,11 +46,11 @@ it; it looks like this.
 
     package BeerDB;
     use Maypole::Application;
-    BeerDB->set_database("dbi:SQLite:t/beerdb.db");
-    BeerDB->config->{uri_base} = "http://localhost/beerdb";
-    BeerDB->config->{template_root} = "/path/to/templates";
-    BeerDB->config->{rows_per_page} = 10;
-    BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
+    BeerDB->setup("dbi:SQLite:t/beerdb.db");
+    BeerDB->config->uri_base("http://localhost/beerdb");
+    BeerDB->config->template_root("/path/to/templates");
+    BeerDB->config->rows_per_page(10);
+    BeerDB->config->display_tables([qw[beer brewery pub style]]);
     BeerDB::Brewery->untaint_columns( printable => [qw/name notes url/] );
     BeerDB::Style->untaint_columns( printable => [qw/name notes/] );
     BeerDB::Beer->untaint_columns(
@@ -83,18 +84,19 @@ it's a relatively small class which defines how the whole application is
 going to run. 
 
 The second line says that our front-end is going to be
-C<Maypole::Application>, it automatically detects if you're using
-mod_perl or CGI and loads everything neccessary for you.
+L<Maypole::Application>, it automatically detects if you're using
+mod_perl or CGI and loads everything necessary for you.
 
 Thirdly we're going to need to set up our database with the given DBI
 connection string. Now the core of Maypole itself doesn't know about
-DBI; as we explained in L<Model.pod>, this argument is passed to our
+DBI; as we explained in the L<Model|Maypole::Manual::Model> chapter,
+this argument is passed to our
 model class wholesale. As we haven't said anything about a model
-class, we get the default one, C<Maypole::Model::CDBI>, which takes a
+class, we get the default one, L<Maypole::Model::CDBI>, which takes a
 DBI connect string. So this one line declares that we're using a C<CDBI>
 model class and it sets up the database for us. In the same way, we
 don't say that we want a particular view class, so we get the default
-C<Maypole::View::TT>.
+L<Maypole::View::TT>.
 
 At this point, everything is in place; we have our driver class, it uses
 a front-end, we have a model class and a view class, and we have a data
@@ -104,15 +106,19 @@ source.
 
 The next of our four sections is the configuration for the application itself.
 
-    BeerDB->config->{uri_base} = "http://localhost/beerdb/";
-    BeerDB->config->{rows_per_page} = 10;
-    BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
+    BeerDB->config->uri_base("http://localhost/beerdb");
+    BeerDB->config->template_root("/path/to/templates");
+    BeerDB->config->rows_per_page(10);
+    BeerDB->config->display_tables([qw[beer brewery pub style]]);
 
-Maypole provides a method called C<config> which returns a hash reference
-of the application's whole configuration. We can use this to set some
+Maypole provides a method called C<config> which returns an object that
+holds the application's whole configuration. We can use this to set some
 parameters; the C<uri_base> is used as the canonical URL of the base
 of this application, and Maypole uses it to construct links.
 
+We also tell Maypole where we keep our template files, using
+C<template_root>.
+
 By defining C<rows_per_page>, we say that any listings we do with the
 C<list> and C<search> templates should be arranged in sets of pages, with
 a maximum of 10 items on each page. If we didn't declare that, C<list>
@@ -137,8 +143,10 @@ The next section is the following set of lines:
         date => [ qw/date/],
     );
 
-As explained in L<StandardTemplates.pod>, this is an set of instructions to
-C<Class::DBI::FromCGI> regarding how the given columns should be edited.
+As explained in the
+L<Standard Templates|Maypole::Manual::StandardTemplates> chapter,
+this is an set of instructions to
+L<Class::DBI::FromCGI> regarding how the given columns should be edited.
 If we didn't have this section, we'd be able to view and delete records,
 but adding and editing them wouldn't work. It took me ages to work that
 one out.
@@ -150,9 +158,9 @@ each other. This is done so that, for instance, when displaying a beer,
 the brewery does not appear as an integer like "2" but as the name of
 the brewery from the C<brewery> table with an ID of 2.
 
-The usual C<Class::DBI> way to do this involves the C<has_a> and
+The usual L<Class::DBI> way to do this involves the C<has_a> and
 C<has_many> methods, but I can never remember how to use them, so I came
-up with the C<Class::DBI::Loader::Relationship> module; this was another
+up with the L<Class::DBI::Loader::Relationship> module; this was another
 yak that needed shaving on the way to the beer database:
 
     use Class::DBI::Loader::Relationship;
@@ -162,7 +170,7 @@ yak that needed shaving on the way to the beer database:
         "a pub has beers on handpumps");
     1;
 
-C<CDBIL::Relationship> acts on a C<Class::DBI::Loader> object and
+C<CDBIL::Relationship> acts on a L<Class::DBI::Loader> object and
 defines relationships between tables in a fairly free-form style.
 The equivalent in ordinary C<Class::DBI> would be:
 
@@ -195,7 +203,7 @@ The first thing we ought to look into is the names of the columns; most
 of them are fine, but that "Abv" column stands out. I'd rather that was
 "A.B.V.". Maypole uses the C<column_names> method to map between the
 names of the columns in the database to the names it displays in the
-default templates. This is provided by C<Maypole::Model::Base>, and
+default templates. This is provided by L<Maypole::Model::Base>, and
 normally, it does a pretty good job; it turns C<model_number> into
 "Model Number", for instance, but there was no way it could guess that
 C<abv> was an abbreviation. Since it returns a hash, the easiest way
@@ -207,7 +215,20 @@ right, and then override the bits it got wrong:
         (shift->SUPER::column_names(), abv => "A.B.V.")
     }
 
-Similarly, the order of columns is a bit wonky. We can fix this by
+There's something to be aware of here: where are you going to type that
+code? You can just put it in F<BeerDB.pm>. Perl will be happy with that,
+though you might want to put an extra pair of braces around it to limit
+the scope of that package declaration. Alternatively, you might think
+it's neater to put it in a file called F<BeerDB/Beer.pm>, which is the
+natural home for the package. This would certainly be a good idea if you
+have a lot of other code to add to the C<BeerDB::Beer> package. But if
+you do that, you will have to tell Perl to load the F<BeerDB/Beer.pm>
+file by adding a line to F<BeerDB.pm>:
+
+    BeerDB::Beer->require;
+
+For another example of customization, the order of columns is a bit
+wonky. We can fix this by
 overriding the C<display_columns> method; this is also a good way to
 hide away any columns we don't want to have displayed, in the same way
 as declaring the C<display_tables> configuration parameter let us hide
@@ -219,7 +240,7 @@ away tables we weren't using:
 
 Hey, have you noticed that we haven't done anything with the
 beers/handpumps/pubs thing yet? Good, I was hoping that you hadn't.
-Ayway, this is because Maypole can't tell easily that a C<BeerDB::Beer>
+Anyway, this is because Maypole can't tell easily that a C<BeerDB::Beer>
 object can call C<pubs> to get a list of pubs. Not yet, at least; we're
 working on it. In the interim, we can explicitly tell Maypole which
 accessors are related to the C<BeerDB::Beer> class like so:
@@ -227,3 +248,10 @@ accessors are related to the C<BeerDB::Beer> class like so:
     sub related { "pubs" }
 
 Now when we view a beer, we'll have a list of the pubs that it's on at.
+
+=head2 Links
+
+L<Contents|Maypole::Manual>,
+Next L<The Request Cookbook|Maypole::Manual::Request>,
+Previous L<Maypole|Maypole::Manual::Workflow>
+
index 89178d4f4b22b8683f6be030d473019b5deecf54..bf828a3279c4a9a07214870ae185698d3a0a7714 100644 (file)
@@ -1,10 +1,11 @@
 =head1 The Maypole iBuySpy Portal
 
-I think it's good fun to compare Maypole
+I think it's good fun to compare Maypole against other frameworks,
+so here's how to build the ASP.NET tutorial site in Maypole.
 
-We begin with a length process of planning and investigating the
+We begin with a lengthy process of planning and investigating the
 sources. Of prime interest is the database schema and the initial data,
-which we convert to a Mysql database. Converting MS SQL to Mysql is not fun.
+which we convert to a MySQL database. Converting MS SQL to MySQL is not fun.
 I shall spare you the gore. Especially the bit where the default insert IDs
 didn't match up between the tables.
 
@@ -36,7 +37,7 @@ This leads naturally to the following driver code:
     use Maypole::Application;
     Portal->setup("dbi:mysql:ibsportal");
     use Class::DBI::Loader::Relationship;
-    Portal->config->{loader}->relationship($_) for (
+    Portal->config->loader->relationship($_) for (
         "A module has a definition",  "A module has settings",
         "A tab has modules",          "A portal has tabs",
         "A role has a portal",        "A definition has a portal",
@@ -104,20 +105,20 @@ C</I<table>/I<action>/I<id>/I<arguments>>.
 
     sub parse_path {
         my $self = shift;
-        $self->{path} ||= "DesktopDefault.aspx";
-        return $self->SUPER::parse_path if not exists $pages{$self->{path}};
-        my $page = $pages{$self->{path}} ;
-        $self->{action} = $page->{action};
-        $self->{table} = $page->{table};
-        my %query = $self->{ar}->args;
-        $self->{args} = [ $query{tabid} || $query{ItemID} || 1];
+        $self->path("DesktopDefault.aspx") unless $self->path;
+        return $self->SUPER::parse_path if not exists $pages{$self->path};
+        my $page = $pages{$self->path} ;
+        $self->action($page->{action});
+        $self->table($page->{table});
+        my %query = $self->ar->args;
+        $self->args( [ $query{tabid} || $query{ItemID} || 1] );
     }
 
     1;
 
 Here we're overriding the C<parse_path> method which takes the C<path>
 slot from the request and populates the C<table>, C<action> and
-C<arguments> slots. If the user has asked for a page we don't know
+C<args> slots. If the user has asked for a page we don't know
 about, we ask the usual Maypole path handling method to give it a try;
 this will become important later on. We turn the default page,
 C<DesktopDefault.aspx>, into the equivalent of C</tab/view/1> unless
@@ -164,15 +165,16 @@ right?
 Dead right, but it was here that I got too clever. I guess it was the word
 "component" that set me off. I thought that since the page was made up of a
 large number of different modules, all requiring their own set of objects, I
-should use a seperate Maypole sub-request for each one, as shown in the
-"Component-based pages" recipe in L<Request.pod>.
+should use a separate Maypole sub-request for each one, as shown in the
+"Component-based pages" recipe in the
+L<Request Cookbook|Maypole::Manual::Request>.
 
 So this is what I did. I created a method in C<Portal::Module> that would
 set the template to the appropriate C<ascx> file:
 
     sub view_desktop :Exported {
         my ($self, $r) = @_;
-        $r->{template} = $r->objects->[0]->definition->DesktopSrc;
+        $r->template($r->objects->[0]->definition->DesktopSrc);
     }
 
 and changed the C<pane> macro to fire off a sub-request for each module:
@@ -318,15 +320,15 @@ tag with the appropriate values. This is only slightly difficult because
 we have to arrange the array of C<module.settings> into a hash of
 C<key_name> => C<setting> pairs. Frankly, I can't be bothered to do this
 in the template, so we'll add it into the C<template_args> again. This
-time C<addition_data> looks like:
+time C<additional_data> looks like:
 
     sub additional_data {
         my $r = shift;
-        shift->{template_args}{portal} = Portal::Portal->retrieve(2);
-        if ($r->{objects}->[0]->isa("Portal::Module")) {
-            $r->{template_args}{module_settings} =
+        shift->template_args->{portal} = Portal::Portal->retrieve(2);
+        if ($r->objects->[0]->isa("Portal::Module")) {
+            $r->template_args->{module_settings} =
                 { map { $_->key_name, $_->setting } 
-                  $r->{objects}->[0]->settings };
+                  $r->objects->[0]->settings };
         }
     }
 
@@ -345,3 +347,11 @@ of Perl code so far. But it's not finished yet.
 
 =head2 Adding users
 
+...
+
+=head2 Links
+
+L<Contents|Maypole::Manual>,
+Next B<That's all folks! Time to start coding ...>,
+Previous L<Flox|Maypole::Manual::Flox>
+
index 3b3fdf318f91d78ac7d1dd6d515216b682d2eb94..e2c47d2c8f0a38c721005fff42434645b3088d6b 100644 (file)
@@ -16,7 +16,8 @@ university student population.
 
 Flox is still in, uh, flux, but it does the essentials. We're going to
 see how it was put together, and how the techniques shown in the
-L<Request.pod> chapter can help to create a sophisticated web
+L<Request Cookbook|Maypole::Manual::Request> can help to
+create a sophisticated web
 application. Of course, I didn't have this manual available at the time,
 so it took a bit longer than it should have done...
 
@@ -24,7 +25,7 @@ so it took a bit longer than it should have done...
 
 Any Maypole application should start with two things: a database schema,
 and some idea of what the pages involved are going to look like.
-Usually, these pages will be tying to displaying or editing some element
+Usually, these pages will be displaying or editing some element
 of the database, so these two concepts should come hand in hand.
 
 When I started looking at social networking sites, I began by
@@ -69,7 +70,7 @@ and communities, and end up with a schema like so:
         expires date
     );
 
-Plus the definition of our two auxilliary tables:
+Plus the definition of our two auxiliary tables:
 
     CREATE TABLE affiliation (
         id int not null auto_increment primary key,
@@ -137,27 +138,26 @@ database:
     package Flox;
     use Maypole::Application;
     Flox->setup("dbi:mysql:flox");
-    Flox->config->{display_tables} = [qw[user invitation connection]];
+    Flox->config->display_tables([qw[user invitation connection]]);
     1;
 
 Very simple, as these things are meant to be. Now let's build on it.
 
-=head2 Authentication
+=head2 Users and Authentication
 
 The concept of a current user is absolutely critical in a site like
 Flox; it represents "me", the viewer of the page, as the site explores
 the connections in my world. We've described the authentication hacks
-briefly in the L<Request.pod> chapter, but now it's time to go into a
-little more detail about how user handling is done.
-
-XXX
+briefly in the L<Request Cookbook|Maypole::Manual::Request>,
+but now it's time to go into a little more detail about how user
+handling is done.
 
 We also want to be able to refer to the current user from the templates,
-so we use the overridable C<additional_data> method to give us a C<my>
-template variable:
+so we use the overridable C<additional_data> method in the driver class
+to give us a C<my> template variable:
 
     sub additional_data { 
-        my $r = shift; $r->{template_args}{my} = $r->{user}
+        my $r = shift; $r->template_args->{my} = $r->user
     }
 
 I've called it C<my> rather than C<me> because we it lets us check 
@@ -178,30 +178,30 @@ C<$r-E<gt>{user}> in there if not:
 
     sub view :Exported {
         my ($class, $r) = @_;
-        $r->{objects} = [ $r->{user} ] unless @{$r->{objects}||[]};
+        $r->objects([ $r->user ]) unless @{ $r->objects || [] };
     }
 
 Maypole, unfortunately, is very good at making programming boring. The
 downside of having to write very little code at all is that we now have
 to spend most of our time writing nice HTML for the templates.
 
-XXX
+=head2 Pictures of Users
 
 The next stage is viewing the user's photo. Assuming we've got the photo
 stored in the database already (which is a reasonable assumption for the
 moment since we don't have a way to upload a photo quite yet) then we
-can use the a variation of the "Displaying pictures" hack from the 
-Requests chapter:
+can use a variation of the "Displaying pictures" hack from the 
+L<Request Cookbook|Maypole::Manual::Request>:
 
     sub view_picture :Exported {
         my ($self, $r) = @_;
-        my $user = $r->{objects}->[0] || $r->{user};
-        if ($r->{content_type} = $user->photo_type) {
-           $r->{output} = $user->photo;
+        my $user = $r->objects->[0] || $r->user;
+        if ($r->content_type($user->photo_type)) {
+           $r->output($user->photo);
         } else {
            # Read no-photo photo
-           $r->{content_type} = "image/png";
-           $r->{output} = slurp_file("images/no-photo.png");
+           $r->content_type("image/png");
+           $r->output(slurp_file("images/no-photo.png"));
         }
     }
 
@@ -215,14 +215,14 @@ and allows us to write the content out directly.
 
 In our template, we can now say
 
-    <IMG SRC="/user/view_picture/[% user.id %]">
+    <IMG SRC="[%base%]/user/view_picture/[% user.id %]">
 
 and the appropriate user's mugshot will appear.
 
 However, if we're throwing big chunks of data around like C<photo>, it's
 now worth optimizing the C<User> class to ensure that only pertitent
 data is fetched by default, and C<photo> and friends are only fetched on
-demand. The "lazy population" section of C<Class::DBI>'s man page
+demand. The "lazy population" section of L<Class::DBI>'s man page
 explains how to group the columns by usage so that we can optimize
 fetches:
 
@@ -252,9 +252,7 @@ can do as much or as little of such optimization as you want or need.
 So now we can view users and their photos. It's time to allow the users
 to edit their profiles and upload a new photo.
 
-=head2 Editing users
-
-XXX Editing a profile
+=head2 Editing user profiles
 
 I introduced Flox to a bunch of friends and told them to be as ruthless
 as possible in finding bugs and trying to break it. And break it they
@@ -278,8 +276,8 @@ sensible size:
     use constant MAX_IMAGE_SIZE => 512 * 1024;
     sub do_upload :Exported {
         my ($class, $r) = @_;
-        my $user = $r->{user};
-        my $upload = $r->{ar}->upload("picture");
+        my $user = $r->user;
+        my $upload = $r->ar->upload("picture");
         if ($upload) {
             my $ct = $upload->info("Content-type");
             return $r->error("Unknown image file type $ct")
@@ -294,12 +292,12 @@ sensible size:
             my ($x, $y) = imgsize(\$image);
             return $r->error("Image too big! ($x, $y) Maximum size is 350x350")
                 if $y > 350 or $x > 350;
-            $r->{user}->photo_type($ct);
-            $r->{user}->photo($image);
+            $r->user->photo_type($ct);
+            $r->user->photo($image);
         }
 
         $r->objects([ $user ]);
-        $r->{template} = "view";
+        $r->template("view");
     }
 
 Now we've gone as far as we want to go about user editing at the moment.
@@ -308,8 +306,8 @@ other people involved, and registering connections between users.
 
 =head2 Invitations
 
-We need to do two things to invitations working: first provide a way to
-issue an invitation, and then provide a way to accept it. Since what
+We need to do two things to make invitations work: first provide a way
+to issue an invitation, and then provide a way to accept it. Since what
 we're doing in issuing an invitation is essentially creating a new
 one, we'll use our usual practice of having a page to display the form
 to offer an invitation, and then use a C<do_edit> method to actually do
@@ -325,7 +323,8 @@ and the template proceeds as normal:
     <FORM ACTION="[%base%]/invitation/do_edit/" METHOD="post">
     <TABLE>
 
-Now we use the "Catching errors in a form" recipe from L<Request.pod> and
+Now we use the "Catching errors in a form" recipe from the
+L<Request Cookbook|Maypole::Manual::Request> and
 write our form template:
 
     <TR><TD>
@@ -354,24 +353,26 @@ we handle the untainting manually, because there are a surprising number of
 things we need to check before we actually do the create. So here's the
 untainting of the parameters:
 
-    my ($self, $r) = @_;
-    my $h = CGI::Untaint->new(%{$r->{params}});
-    my (%errors, %ex);
-    for (qw( email forename surname )) {
-        $ex{$_} = $h->extract(
-                "-as_".($_ eq "email" ? "email" : "printable") => $_
-        ) or $errors{$_} = $h->error;
-    }
+    sub do_edit :Exported {
+        my ($self, $r) = @_;
+        my $h = CGI::Untaint->new(%{$r->params});
+        my (%errors, %ex);
+        for (qw( email forename surname )) {
+            $ex{$_} = $h->extract(
+                    "-as_".($_ eq "email" ? "email" : "printable") => $_
+            ) or $errors{$_} = $h->error;
+        }
 
 Next, we do the usual dance of throwing the user back at the form in case
 of errors:
 
-    if (keys %errors) {
-        $r->{template_args}{message} = "There was something wrong with that...";
-        $r->{template_args}{errors} = \%errors;
-        $r->{template} = "issue";
-        return;
-    }
+        if (keys %errors) {
+            $r->template_args->{message} =
+                "There was something wrong with that...";
+            $r->template_args->{errors} = \%errors;
+            $r->template("issue");
+            return;
+        }
 
 We've introduced a new template variable here, C<message>, which we'll use
 to display any important messages to the user.
@@ -381,23 +382,23 @@ with that email address. If we have, and they're a real user, then we
 abort the invite progress and instead redirect them to viewing that user's 
 profile.
 
-    my ($user) = Flox::User->search({ email => $ex{email} });
-    if ($user) {
-        if ($user->status eq "real") {
-            $r->{template_args}{message} =
-                "That user already seems to exist on Flox. ".
-                "Is this the one you meant?";
+        my ($user) = Flox::User->search({ email => $ex{email} });
+        if ($user) {
+            if ($user->status eq "real") {
+                $r->template_args->{message} =
+                    "That user already seems to exist on Flox. ".
+                    "Is this the one you meant?";
 
-            $self->redirect_to_user($r,$user);
-        } 
+                $self->redirect_to_user($r,$user);
+            
 
 Where C<redirect_to_user> looks like this:
 
     sub redirect_to_user {
         my ($self, $r, $user) = @_;
-        $r->{objects} = [ $user ];
-        $r->{template} = "view";
-        $r->{model_class} = "Flox::User"; # Naughty.
+        $r->objects([ $user ]);
+        $r->template("view");
+        $r->model_class("Flox::User"); # Naughty.
     }
 
 This is, as the comment quite rightly points out, naughty. We're currently
@@ -412,31 +413,32 @@ C<message> in there as well, so this will have to do. This isn't good practice;
 we put it into a subroutine so that we can fix it up if we find a better way
 to do it.
 
-Anyway, this is what we should do if a user already exists on the system
+Anyway back in the C<do_edit> action, 
+this is what we should do if a user already exists on the system
 and has accepted an invite already. What if we're trying to invite a user but
 someone else has invited them first and they haven't replied yet?
 
-         } else {
-            # Put it back to the form
-            $r->{template_args}{message} =
-            "That user has already been invited; ".
-            "please wait for them to accept";
-            $r->{template} = "issue";
+             } else {
+                # Put it back to the form
+                $r->template_args->{message} =
+                    "That user has already been invited; " .
+                    "please wait for them to accept";
+                $r->template("issue");
+             }
+             return;
         }
-        return;
-    }
 
 Race conditions suck.
 
 Okay. Now we know that the user doesn't exist, and so can create the new 
 one:
 
-    my $new_user = Flox::User->create({
-        email => $ex{email},
-        first_name => $ex{forename},
-        last_name  => $ex{surname},
-        status => "invitee"
-    });
+        my $new_user = Flox::User->create({
+            email      => $ex{email},
+            first_name => $ex{forename},
+            last_name  => $ex{surname},
+            status     => "invitee"
+        });
 
 We want to give the invitee a URL that they can go to in order to
 accept the invite. Now we don't just want the IDs of our invites to
@@ -444,28 +446,29 @@ be sequential, since someone could get one invite, and then guess the
 rest of the invite codes. We provide a relatively secure MD5 hash as
 the invite ID:
 
-    my $random = md5_hex(time.(0+{}).$$.rand);
+        my $random = md5_hex(time.(0+{}).$$.rand);
 
 For additional security, we're going to have the URL in the form
 C</invitation/accept/I<id>/I<from_id>/I<to_id>>, encoding the user ids
 of the two users. Now we can send email to the invitee to ask them to
 visit that URL:
 
-    my $newid = $new_user->id;
-    my $myid  = $r->{user}->id;
-    _send_mail(to => $ex{email}, url => "$random/$myid/$newid", 
-               user => $r->{user});
+        my $newid = $new_user->id;
+        my $myid  = $r->user->id;
+        _send_mail(to   => $ex{email},
+                   url  => "$random/$myid/$newid", 
+                   user => $r->user);
 
 I'm not going to show the C<_send_mail> routine, since it's boring.
 We haven't actually created the C<Invitation> object yet, so let's
 do that now.
 
-    Flox::Invitation->create({
-        id => $random,
-        issuer => $r->{user},
-        recipient => $new_user,
-        expires => Time::Piece->new(time + LIFETIME)->datetime
-    });
+        Flox::Invitation->create({
+            id        => $random,
+            issuer    => $r->user,
+            recipient => $new_user,
+            expires   => Time::Piece->new(time + LIFETIME)->datetime
+        });
 
 You can also imagine a daily cron job that cleans up the C<Invitation>
 table looking for invitations that ever got replied to within their
@@ -481,7 +484,7 @@ our hands.
 Anyway, now we've got the invitation created, we can go back to whence we
 came: viewing the original user:
 
-    $self->redirect_to_user($r, $r->{user});
+        $self->redirect_to_user($r, $r->user);
 
 Now our invitee has an email, and goes B<click> on the URL. What happens?
 
@@ -489,4 +492,14 @@ XXX
 
 =head2 Friendship Connections
 
-=head2 
+XXX
+
+=head2 Links
+
+The source for Flox is available at
+L<http://cvs.simon-cozens.org/viewcvs.cgi/flox>.
+
+L<Contents|Maypole::Manual>,
+Next L<The Maypole iBuySpy Portal|Maypole::Manual::BuySpy>,
+Previous L<Maypole Request Hacking Cookbook|Maypole::Manual::Request>
+
index bc54ffdb8105ef81a9e57e260c820816fbb445f1..9add8934c6f6f6e9b42ee7e3ac83f6908bc80673 100644 (file)
@@ -1,6 +1,9 @@
 =head1 Maypole Model Classes
 
-=head2 Class::DBI
+Maypole's model classes provide an interface to your data store.
+In principle Maypole can connect to pretty much any data source,
+but the default model is based on the popular L<Class::DBI> object
+interface that uses the near-universal L<DBI> Perl interface to databases.
 
 =head2 Maypole::Model::CDBI
 
@@ -14,15 +17,14 @@ your driver class, you'll say something like this:
     BeerDB->setup("dbi:mysql:beerdb");
 
 C<setup> is a Maypole method, and it hands its parameter to the model
-class. In our case, the model class is a DBI connect string, because
+class. In our case, the argument is a DBI connect string, because
 that's what C<Maypole::Model::CDBI>, the C<Class::DBI>-based model
 expects. C<Maypole::Model::CDBI> has a method called C<setup_database>
 that creates all the C<Class::DBI> table classes after connecting to the
 database with that connect string. It does this by using
 C<Class::DBI::Loader>, a utility module which asks a database
 about its schema and sets up classes such as C<BeerDB::Beer> to inherit
-from C<Class::DBI>. This is just doing automatically what we did
-manually in our examples above.
+from C<Class::DBI>.
 
 Now it gets interesting. The names of these classes are stashed away in
 the application's configuration, and then Maypole forcibly has these
@@ -42,11 +44,12 @@ make writing Maypole applications a lot easier:
     use Class::DBI::Plugin::RetrieveAll;
     use Class::DBI::Pager;
 
-We'll meet most of these goodies in L<StandardTemplates.pod>, where we
-explain how C<Maypole::Model::CDBI> works.
+We'll meet most of these goodies in the
+L<Standard Templates and Actions|Maypole::Manual::StandardTemplates>
+chapter, where we explain how C<Maypole::Model::CDBI> works.
 
 The second reason why we want our table classes to inherit from
-C<Maypole::Model::CDBI> is because C<CDBI> provides a useful set of 
+C<Maypole::Model::CDBI> is because it provides a useful set of 
 default actions. So what's an action, and why are they useful?
 
 =head2 Extending a model class with actions
@@ -64,13 +67,13 @@ C<BeerDB::Brewery-E<gt>delete(20)>. Now, it's slightly more complex than
 that. Firstly because it doesn't actually pass the parameter C<20>, but
 it passes an object representing row 20 in the database, but we can
 gloss over that for the second. No, the real issue is that Maypole does
-not allow you to call any method in the table class; that would be
+not allow you to call just any method in the table class; that would be
 somewhat insecure. 
 
 Instead, Maypole makes a distinction between the kind of methods that
 only the class itself and other Perl code can call, and the kind of
 methods that anyone can call from a URL. This latter set of methods are
-called B<exported> methods, and exporting is done with by means of Perl
+called B<exported> methods, and exporting is done by means of Perl
 attributes. You define a method to be exported like so:
 
     sub drink :Exported {
@@ -81,6 +84,7 @@ sometimes called an B<action>.
 
 Maypole model classes like C<Maypole::Model::CDBI> come with a
 relatively handy set of actions which are all you need to set up a CRUD
+(Create, Read, Update, Delete)
 database front-end: viewing a row in a database, editing it, adding a
 new one, deleting, and so on. The most important thing about Maypole,
 though, is that it doesn't stop there. You can add your own.
@@ -91,7 +95,7 @@ C<BeerDB::Beer> package, and put some additional actions in there.
     package BeerDB::Beer;
     sub top_five :Exported {
         my ($class, $r) = @_;
-        $r->{objects} = [ ($r->retrieve_all_sorted_by("score"))[-5..-1] ];
+        $r->objects([ ($r->retrieve_all_sorted_by("score"))[-5..-1] ]);
     }
 
 Our action is called as a class method with the Maypole request object.
@@ -102,8 +106,15 @@ view class will come along and process the C<top_five> template with
 these five beers.
 
 We'll look more at how to put together actions in the
-L<StandardTemplates.pod> chapter and our case studies.
+L<Standard Templates and Actions|Maypole::Manual::StandardTemplates>
+chapter and our case studies.
 
 =head2 What Maypole wants from a model
 
 =head2 Building your own model class
+
+=head2 Links
+
+L<Contents|Maypole::Manual>,
+Next L<Maypole View Classes|Maypole::Manual::View>,
+Previous L<Introduction to Maypole|Maypole::Manual::About>
index f9186563e440532604a84a0e91f62806fcf95585..d3d66f299c66d6116d27c2ba1dba747b23226471 100644 (file)
@@ -32,8 +32,8 @@ And in F<BeerDB.pm>, you put:
 
 It doesn't work.
 
-B<Solution>: It doesn't work because of the timing of the module
-loading. C<use Beer::Beer> will try to set up the C<has_a> relationships
+B<Solution>: It doesn't work because of the timing of the module loading.
+C<use BeerDB::Beer> will try to set up the C<has_a> relationships
 at compile time, when the database tables haven't even been set up,
 since they're set up by
 
@@ -54,10 +54,10 @@ or move the module loading to run-time (my preferred solution):
 You're seeing bizarre problems with Maypole output, and you want to test it in
 some place outside of the whole Apache/mod_perl/HTTP/Internet/browser circus.
 
-B<Solution>: Use the C<Maypole::CLI> module to go directly from a URL to
+B<Solution>: Use the L<Maypole::CLI> module to go directly from a URL to
 standard output, bypassing Apache and the network altogether.
 
-C<Maypole::CLI> is not a standalone front-end, but to allow you to debug your
+L<Maypole::CLI> is not a standalone front-end, but to allow you to debug your
 applications without having to change the front-end they use, it temporarily
 "borgs" an application. If you run it from the command line, you're expected
 to use it like so:
@@ -72,23 +72,30 @@ You can also use the C<Maypole::CLI> module programatically to create
 test suites for your application. See the Maypole tests themselves or
 the documentation to C<Maypole::CLI> for examples of this.
 
+Don't forget also to turn on debugging output in your application:
+
+    package BeerDB;
+    use strict;
+    use warnings;
+    use Maypole::Application qw(-Debug);
+
 =head3 Changing how URLs are parsed
 
 You don't like the way Maypole URLs look, and want something that either
 fits in with the rest of your site or hides the internal workings of the
 system.
 
-C<Solution>: So far we've been using the C</table/action/id/args> form
+B<Solution>: So far we've been using the C</table/action/id/args> form
 of a URL as though it was "the Maypole way"; well, there is no Maypole
 way. Maypole is just a framework and absolutely everything about it is 
 overridable. 
 
 If we want to provide our own URL handling, the method to override in
 the driver class is C<parse_path>. This is responsible for taking
-C<$r-E<gt>{path}> and filling the C<table>, C<action> and C<args> slots
+C<$r-E<gt>path> and filling the C<table>, C<action> and C<args> slots
 of the request object. Normally it does this just by splitting the path
-on C</>s, but you can do it any way you want, including getting the
-information from C<POST> form parameters or session variables. 
+on 'C</>' characters, but you can do it any way you want, including
+getting the information from C<POST> form parameters or session variables. 
 
 For instance, suppose we want our URLs to be of the form
 C<ProductDisplay.html?id=123>, we could provide a C<parse_path> method
@@ -96,13 +103,12 @@ like so:
 
     sub parse_path {
         my $r = shift;
-        $r->{path} ||= "ProductList.html";
-        ($r->{table}, $r->{action}) = 
-            ($r->{path} =~ /^(.*?)([A-Z]\w+)\.html/);
-        $r->{table}  = lc $r->{table};
-        $r->{action} = lc $r->{action};
-        my %query = $r->{ar}->args;
-        $self->{args} = [ $query{id} ];
+        $r->path("ProductList.html") unless $r->path;
+        ($r->path =~ /^(.*?)([A-Z]\w+)\.html/);
+        $r->table(lc $1);
+        $r->action(lc $2);
+        my %query = $r->ar->args;
+        $self->args([ $query{id} ]);
     }
 
 This takes the path, which already has the query parameters stripped off
@@ -110,7 +116,8 @@ and parsed, and finds the table and action portions of the filename,
 lower-cases them, and then grabs the C<id> from the query. Later methods
 will confirm whether or not these tables and actions exist.
 
-See L<BuySpy.pod> for another example of custom URL processing.
+See the L<iBuySpy Portal|Maypole::Manual::BuySpy> for another
+example of custom URL processing.
 
 =head3 Maypole for mobile devices
 
@@ -126,7 +133,7 @@ putting this method in our driver class:
 
     sub get_template_root {
         my $r = shift;
-        my $browser = $r->{ar}->headers_in->get('User-Agent');
+        my $browser = $r->headers_in->get('User-Agent');
         if ($browser =~ /mobile|palm|nokia/i) {
             "/home/myapp/templates/mobile";
         } else {
@@ -140,7 +147,7 @@ idea.)
 =head2 Content display hacks
 
 These hacks deal primarily with the presentation of data to the user,
-modifying the C<view> template or changing the way that the results of
+modifying the F<view> template or changing the way that the results of
 particular actions are displayed.
 
 =head3 Null Action
@@ -156,8 +163,9 @@ way.
 
 If, on the other hand, you want to display some data, and what you're
 essentially doing is a variant of the C<view> action, then you need to
-ensure that you have an exported action, as described in
-L<StandardTemplates.pod>:
+ensure that you have an exported action, as described in the
+L<templates and actions|Maypole::Manual::StandardTemplates/"C<view> and C<edit>">
+chapter:
 
     sub my_view :Exported { }
 
@@ -176,17 +184,17 @@ going to C</beer/view/I<id>> all along. We do this by setting the
 objects in the C<objects> slot and changing the C<template> to the
 one we wanted to go to.
 
-In this example from L<Flox.pod>, we've just performed an C<accept>
-method on a C<Flox::Invitation> object and we want to go back to viewing
-a user's page.
+In this example from L<Flox|Maypole::Manual::Flox>, we've just
+performed an C<accept> method on a C<Flox::Invitation> object and we
+want to go back to viewing a user's page.
 
     sub accept :Exported {
         my ($self, $r) = @_;
         my $invitation = $r->objects->[0];
         # [... do stuff to $invitation ...]
-        $r->{objects} = [$r->{user}];
-        $r->{model_class} = "Flox::User";
-        $r->{template} = "view";
+        $r->objects([$r->user]);
+        $r->model_class("Flox::User");
+        $r->template("view");
     }
 
 This hack is so common that it's expected that there'll be a neater
@@ -216,13 +224,13 @@ chords:
             <line> <sup>A</sup>Lay<sup>Dm</sup>la <sup>Bb</sup> </line> 
             <line> <sup>C</sup>Got me on my <sup>Dm</sup>knees </line> 
             ...
+
 I store the title, artist and key in the database, as well as an "xml"
 field which contains the whole song as XML.
 
 To load the songs into the database, I can C<use> the driver class for
 my application, since that's a handy way of setting up the database classes
-we're going to need to use. Then the handy C<XML::TreeBuilder> will handle
+we're going to need to use. Then the handy L<XML::TreeBuilder> will handle
 the XML parsing for us:
 
     use Songbook;
@@ -247,7 +255,7 @@ the XML parsing for us:
     }
 
 Now we need to set up the custom display for each song; thankfully, with
-the C<Template::Plugin::XSLT> module, this is as simple as putting the
+the L<Template::Plugin::XSLT> module, this is as simple as putting the
 following into F<templates/song/view>:
 
     [%
@@ -273,22 +281,23 @@ We also set the C<content_type> using one from the database.
 
     sub view_picture :Exported {
         my ($self, $r) = @_;
-        my $user = $r->{objects}->[0];
-        $r->{content_type} = $user->photo_type;
-        $r->{output} = $user->photo;
+        my $user = $r->objects->[0];
+        $r->content_type($user->photo_type);
+        $r->output($user->photo);
     }
 
 Of course, the file doesn't necessarily need to be in the database
 itself; if your file is stored in the filesystem, but you have a file
 name or some other pointer in the database, you can still arrange for
-the data to be fetched and inserted into C<$r-E<gt>{output}>.
+the data to be fetched and inserted into C<$r-E<gt>output>.
 
 =head3 REST
 
 You want to provide a programmatic interface to your Maypole site.
 
 B<Solution>: The best way to do this is with C<REST>, which uses a
-descriptive URL to encode the request. For instance, in L<Flox.pod> we
+descriptive URL to encode the request. For instance, in
+L<Flox|Maypole::Manual::Flox> we
 describe a social networking system. One neat thing you can do with
 social networks is to use them for reputation tracking, and we can use
 that information for spam detection. So if a message arrives from
@@ -313,15 +322,15 @@ row in the user table, it will appear in the C<args> array:
     use URI::Escape;
     sub relationship_by_email :Exported {
         my ($self, $r) = @_;
-        my $email = uri_unescape($r->{args}[0]);
-        $r->{content_type} = "text/plain";
+        my $email = uri_unescape($r->args->[0]);
+        $r->content_type("text/plain");
         my $user;
         unless (($user) = Flox::User->search(email => $email)) {
-            $r->{content} = "0\n"; return;
+            $r->content("0\n"); return;
         }
 
-        if ($r->{user}->is_friend($user)) { $r->{content} = "2\n"; return; };
-        $r->{content} = "1\n"; return;
+        if ($r->user->is_friend($user)) { $r->contenti("2\n"); return; };
+        $r->content("1\n"); return;
     }
 
 =head3 Component-based Pages
@@ -331,9 +340,13 @@ components, all displaying different bits of information about different
 objects. You want to include the output of one Maypole request call while
 building up another. 
 
-B<Solution>: Use C<Maypole::Component>. By inheriting from this, you can
-call the C<component> method on the Maypole request object to make a 
-"sub-request". For instance, if you have a template
+B<Solution>: Use L<Maypole::Plugin::Component>. By inheriting like this:
+
+    package BeerDB;
+    use Maypole::Application qw(Component);
+
+you can call the C<component> method on the Maypole request object to
+make a "sub-request". For instance, if you have a template
 
     <DIV class="latestnews">
     [% request.component("/news/latest_comp") %]
@@ -367,8 +380,8 @@ Put this in your driver class:
 
     sub error { 
         my ($r, $message) = @_;
-        $r->{template} = "error";
-        $r->{template_args}{error} = $message;
+        $r->template("error");
+        $r->template_args->{error} = $message;
         return OK;
     }
 
@@ -398,7 +411,7 @@ returns C<OK>, you can even use it in your C<authenticate> routine:
         my ($self, $r) = @_;
         $r->get_user;
         return $r->error("You do not exist. Go away.")
-            if $r->{user} and $r->{user}->status ne "real";
+            if $r->user and $r->user->status ne "real";
         ...
     }
 
@@ -409,7 +422,7 @@ Non-showstopper errors or other notifications are best handled by tacking a
 C<messages> template variable onto the request:
 
     if ((localtime)[6] == 1) {
-        push @{$r->{template_args}{messages}}, "Warning: Today is Monday";
+        push @{$r->template_args->{messages}}, "Warning: Today is Monday";
     }
 
 Now F<custom/messages> can contain:
@@ -429,7 +442,7 @@ appropriate point in your template; you may also want to use a template
 switcheroo to ensure that you're displaying a page that has the messages box in
 it.
 
-=head2 Authentication hacks
+=head2 Authentication and Authorization hacks
 
 The next series of hacks deals with providing the concept of a "user" for
 a site, and what you do with one when you've got one.
@@ -439,31 +452,33 @@ a site, and what you do with one when you've got one.
 You need the concept of a "current user".
 
 B<Solution>: Use something like
-C<Maypole::Authentication::UserSessionCookie> to authenticate a user
-against a user class and store a current user object in the request
-object.
+L<Maypole::Plugin::Authentication::UserSessionCookie> to authenticate
+a user against a user class and store a current user object in the
+request object.
 
 C<UserSessionCookie> provides the C<get_user> method which tries to get
 a user object, either based on the cookie for an already authenticated
-session, or by comparing C<username> and C<password> form parameters
+session, or by comparing C<user> and C<password> form parameters
 against a C<user> table in the database. Its behaviour is highly
-customizable, so see the documentation, or the authentication paper at
-C<http://maypole.simon-cozens.org/docs/authentication.html> for examples.
+customizable and described in its documentation.
 
 =head3 Pass-through login
 
 You want to intercept a request from a non-logged-in user and have
 them log in before sending them on their way to wherever they were
-originally going.
+originally going. Override C<Maypole::authenticate> in your driver
+class, something like this:
 
 B<Solution>:
 
+    use Maypole::Constants; # Otherwise it will silently fail!
+
     sub authenticate {
         my ($self, $r) = @_;
         $r->get_user;
-        return OK if $r->{user};
+        return OK if $r->user;
         # Force them to the login page.
-        $r->{template} = "login";
+        $r->template("login");
         return OK;
     }
 
@@ -478,13 +493,14 @@ like this:
     [% IF login_error %]
        <FONT COLOR="#FF0000"> [% login_error %] </FONT>
     [% END %]
-      <FORM ACTION="/[% request.path%]" METHOD="post">
+      <FORM ACTION="[% base ; '/' ; request.path %]" METHOD="post">
     Username: 
-        <INPUT TYPE="text" NAME="[% config.auth.user_field || "user" %]"> <BR>
+        <INPUT TYPE="text" NAME="[% config.auth.user_field || "user" %]"><BR>
     Password: <INPUT TYPE="password" NAME="password"> <BR>
     <INPUT TYPE="submit">
     </FORM>
     </DIV>
+    [% INCLUDE footer %]
 
 Notice that this request gets C<POST>ed back to wherever it came from, using
 C<request.path>. This is because if the user submits correct credentials,
@@ -497,28 +513,11 @@ Now your users are logged in, you want a way of having them log out
 again and taking the authentication cookie away from them, sending
 them back to the front page as an unprivileged user.
 
-B<Solution>: This action, on the user class, is probably overkill, but
-it does the job:
-
-    sub logout :Exported {
-        my ($class, $r) = @_;
-        # Remove the user from the request object
-        my $user = delete $r->{user};
-        # Destroy the session
-        tied(%{$r->{session}})->delete;
-        # Send a new cookie which expires the previous one
-        my $cookie = Apache::Cookie->new($r->{ar},
-            -name => $r->config->{auth}{cookie_name},
-            -value => undef,
-            -path => "/"
-            -expires => "-10m"
-        );
-        $cookie->bake();
-        # Template switcheroo
-        $r->template("frontpage");
-    }
+B<Solution>: Just call the C<logout> method of
+C<Maypole::Plugin::Authentication::UserSessionCookie>. You may also want
+to use the template switcheroo hack to send them back to the frontpage.
 
-=head3 Multi-level Authentication
+=head3 Multi-level Authorization
 
 You have both a global site access policy (for instance, requiring a
 user to be logged in except for certain pages) and a policy for
@@ -529,26 +528,33 @@ provided by the model class.)
 You don't know whether to override the global C<authenticate> method or
 provide one for each class.
 
-B<Solution>: Do both. Have a global C<authenticate> method which calls 
-a C<sub_authenticate> method based on the class:
+B<Solution>: Do both.
+Maypole checks whether there is an C<authenticate> method for the model
+class (e.g. BeerDB::Beer) and if so calls that. If there's no such
+method, it calls the default global C<authenticate> method in C<Maypole>,
+which always succeeds. You can override the global method as we saw
+above, and you can provide methods in the model classes.
 
-    sub authenticate {
-        ...
-        if ($r->{user}) {
-            return $r->model_class->sub_authenticate($r)
-                if $r->model_class && $r->model_class->can("sub_authenticate");
-            return OK;
-        }
-        ...
+To use per-table access control you can just add methods to your model
+subclasses that specify individual policies, perhaps like this:
+
+    sub authenticate { # Ensure we can only create, reject or accept
+        my ($self, $r) = @_;
+        return OK if $r->action =~ /^(issue|accept|reject|do_edit)$/;
+        return; # fail if any other action
     }
 
-And now your C<sub_authenticate> methods can specify the policy for
-each table:
+If you define a method like this, the global C<authenticate> method will
+not be called, so if you want it to be called you need to do so
+explicitly:
 
-    sub sub_authenticate { # Ensure we can only create, reject or accept
+    sub authenticate { # Ensure we can only create, reject or accept
         my ($self, $r) = @_;
-        return OK if $r->{action} =~ /^(issue|accept|reject|do_edit)$/;
-        return;
+        return unless $r->authenticate($r) == OK; # fail if not logged in
+        # now it's safe to use $r->user
+        return OK if $r->action =~ /^(accept|reject)$/
+            or ($r->user eq 'fred' and $r->action =~ /^(issue|do_edit)$/);
+        return; # fail if any other action
     }
 
 =head2 Creating and editing hacks
@@ -562,8 +568,8 @@ You want the user to be able to type in some text that you're later
 going to display on the site, but you don't want them to stick images in
 it, launch cross-site scripting attacks or otherwise insert messy HTML.
 
-B<Solution>: Use the C<CGI::Untaint::html> module to sanitize the HTML
-on input. C<CGI::Untaint::html> uses C<HTML::Sanitizer> to ensure that
+B<Solution>: Use the L<CGI::Untaint::html> module to sanitize the HTML
+on input. C<CGI::Untaint::html> uses L<HTML::Sanitizer> to ensure that
 tags are properly closed and can restrict the use of certain tags and
 attributes to a pre-defined list.
 
@@ -589,15 +595,15 @@ data from another source.
 
 B<Solution>: Munge the contents of C< $r-E<gt>params > before jumping
 to the original C<do_edit> routine. For instance, in this method,
-we use a C<Net::Amazon> object to fill in some fields of a database row based
-on an ISBN:
+we use a L<Net::Amazon> object to fill in some fields of a database row
+based on an ISBN:
 
     sub create_from_isbn :Exported {
        my ($self, $r) = @_;
-       my $response = $ua->search(asin => $r->{params}{isbn});
+       my $response = $ua->search(asin => $r->params->{isbn});
        my ($prop) = $response->properties;
        # Rewrite the CGI parameters with the ones from Amazon
-       @{$r->{params}{qw(title publisher author year)} =            
+       @{$r->params->{qw(title publisher author year)} =            
            ($prop->title,
            $prop->publisher,
            (join "/", $prop->authors()),
@@ -608,6 +614,8 @@ on an ISBN:
 
 The request will carry on as though it were a normal C<do_edit> POST, but
 with the additional fields we have provided.
+You might also want to add a template switcheroo so the user can verify
+the details you imported.
 
 =head3 Catching errors in a form
 
@@ -624,8 +632,8 @@ If there are any errors, these are placed in a hash, with each error
 keyed to the erroneous field. The hash is put into the template as
 C<errors>, and we process the same F<edit> template again:
 
-        $r->{template_args}{errors} = \%errors;
-        $r->{template} = "edit";
+        $r->template_args->{errors} = \%errors;
+        $r->template("edit");
 
 This throws us back to the form, and so the form's template should take
 note of the errors, like so:
@@ -696,7 +704,7 @@ the action:
 
     sub upload_picture : Exported {}
 
-And here's the template:
+And here's the F<custom/upload_picture> template:
 
     <FORM action="/user/do_upload" enctype="multipart/form-data" method="POST">
 
@@ -710,16 +718,16 @@ And here's the template:
 (Although you'll probably want a bit more HTML around it than that.)
 
 Now we need to write the C<do_upload> action. At this point we have to get a
-little friendly with the front-end system. If we're using C<Apache::Request>,
+little friendly with the front-end system. If we're using L<Apache::Request>,
 then the C<upload> method of the C<Apache::Request> object (which
-C<Apache::MVC> helpfully stores in C<$r-E<gt>{ar}>) will work for us:
+L<Apache::MVC> helpfully stores in C<$r-E<gt>{ar}>) will work for us:
 
     sub do_upload :Exported {
         my ($class, $r) = @_;
-        my $user = $r->{user};
-        my $upload = $r->{ar}->upload("picture");
+        my $user = $r->user;
+        my $upload = $r->ar->upload("picture");
 
-This returns a C<Apache::Upload> object, which we can query for its
+This returns a L<Apache::Upload> object, which we can query for its
 content type and a file handle from which we can read the data. It's
 also worth checking the image isn't going to be too massive before we
 try reading it and running out of memory, and that the content type is
@@ -735,26 +743,58 @@ something we're prepared to deal with.
         my $fh = $upload->fh;
         my $image = do { local $/; <$fh> };
 
+Don't forget C<binmode()> in there if you're on a platform that needs it.
 Now we can store the content type and data into our database, store it
 into a file, or whatever:
 
-        $r->{user}->photo_type($ct);
-        $r->{user}->photo($image);
+        $r->user->photo_type($ct);
+        $r->user->photo($image);
     }
 
 And finally, we use our familiar template switcheroo hack to get back to
 a useful page:
 
         $r->objects([ $user ]);
-        $r->{template} = "view";
+        $r->template("view");
     }
 
 Now, as we've mentioned, this only works because we're getting familiar with
-C<Apache::Request> and its C<Apache::Upload> objects. If we're planning to use
-C<CGI::Maypole> instead, or want to write our application in a generic way so
-that it'll work regardless of front-end, then we need to replace the C<upload>
-call with an equivalent which uses the C<CGI> module to get the upload data.
-This is convoluted and horrific and we're not going to show it here, but it's
-possible.
+C<Apache::Request> and its C<Apache::Upload> objects. If we're using
+L<CGI::Maypole> instead, we can write the action in a similar style:
+
+    sub do_upload :Exported {
+        my ($class, $r) = @_;
+        my $user = $r->user;
+        my $cgi = $r->cgi;
+        if ($cgi->upload == 1) { # if there was one file uploaded
+            my $filename = $cgi->param('picture');
+            my $ct = $cgi->upload_info($filename, 'mime');
+            return $r->error("Unknown image file type $ct")
+                if $ct !~ m{image/(jpeg|gif|png)};
+            return $r->error("File too big! Maximum size is ".MAX_IMAGE_SIZE)
+                if $cgi->upload_info($filename, 'size') > MAX_IMAGE_SIZE;
+            my $fh = $cgi->upload($filename);
+            my $image = do { local $/; <$fh> };
+            $r->user->photo_type($ct);
+            $r->user->photo($image);
+        }
+
+        $r->objects([ $user ]);
+        $r->template("view");
+    }
+
+It's easy to adapt this to upload multiple files if desired.
+You will also need to enable uploads in your driver initialization,
+with the slightly confusing statement:
+
+    $CGI::Simple::DISABLE_UPLOADS = 0; # enable uploads
 
 Combine with the "Displaying pictures" hack above for a happy time.
+
+=head2 Links
+
+L<Contents|Maypole::Manual>,
+Next L<Flox|Maypole::Manual::Flox>,
+Previous L<The Beer Database, Twice|Maypole::Manual::Beer>
+
+
index 3c8bc69ab9936b1573e48c591d397b6db3f42b87..35f9a8a86cca5befa0347d1808cc4a4643727af6 100644 (file)
@@ -1,7 +1,8 @@
 
 =head1 Maypole's Standard Templates and Actions
 
-As we saw in our CRUD example, Maypole does all it can to make your life
+As we saw in our Create-Read-Update-Delete (CRUD) example,
+Maypole does all it can to make your life
 easier; this inclues providing a set of default actions and
 factory-supplied templates. These are written in such a generic way,
 making extensive use of class metadata, that they are more or less
@@ -12,8 +13,18 @@ actions do their stuff, and how the default templates are put together.
 Once we have an understanding of what Maypole does for us automatically,
 we can begin to customize and create our own templates and actions.
 
+Although the standard templates can be applied in many situations,
+they're really provided just as examples,
+as a starting point to create your own templates to suit your needs.
+The goal of templating is to keep templates simple so the presentation
+can be changed easily when you desire.
+We're not trying to build a single set of reusable templates that cover
+every possible situation.
+
 =head2 The standard actions
 
+Remember that actions are just subroutines in the model classes with an
+I<Exported> attribute.
 A simple, uncustomized Maypole model class, such as one of the classes
 in the beer database application, provides the following default actions
 - that is, provides access to the following URLs:
@@ -32,9 +43,11 @@ web form to edit the object; it submits to C<do_edit>.
 
 =item C</[table]/do_edit/[id]>
 
+When called with an ID, the C<do_edit> action provides row editing.
+
 =item C</[table]/do_edit/>
 
-This provides both editing and row creation facilities. 
+When called without an ID, the C<do_edit> action provides row creation.
 
 =item C</[table]/delete/id>
 
@@ -61,7 +74,7 @@ into an object, and hand it to the template to be displayed. However, as
 taking the first argument and turning it into an object is such a common
 action, it is handled directly by the model class's C<process> method.
 Similarly, the default template name provided by the C<process> method
-is the name of the acction, and so will be C<view> or C<edit>
+is the name of the action, and so will be C<view> or C<edit>
 accordingly. 
 
 So the code required to make these two actions work turns out to be:
@@ -76,7 +89,7 @@ separated out the concerns of "acting" and displaying. Both of these
 "actions" are purely concerned with displaying a record, and don't need
 to do any "acting". Remember that the "edit" method doesn't actually do
 any editing - this is provided by C<do_edit>; it is just another view of
-the data, albeit once which allows the data to be modified later. These
+the data, albeit one which allows the data to be modified later. These
 two methods don't need to modify the row in any way, they don't need to
 do anything clever. They just are.
 
@@ -109,7 +122,7 @@ mode, acting upon that object. If not, we're in create mode.
 
     sub do_edit :Exported {
         my ($self, $r) = @_;
-        my $h = CGI::Untaint->new(%{$r->{params}});
+        my $h = CGI::Untaint->new(%{$r->params});
         my ($obj) = @{$r->objects || []};
         if ($obj) {
             # We have something to edit
@@ -118,15 +131,42 @@ mode, acting upon that object. If not, we're in create mode.
             $obj = $self->create_from_cgi($h);
         }
 
-The C<CDBI> model uses L<Class::DBI::FromCGI> to turn C<POST> parameters
-into database table data. This in turn uses C<CGI::Untaint> to ensure
+The C<CDBI> model uses the C<update_from_cgi> and C<create_from_cgi>
+methods of L<Class::DBI::FromCGI> to turn C<POST> parameters
+into database table data. This in turn uses L<CGI::Untaint> to ensure
 that the data coming in is suitable for the table. If you're using the
 default C<CDBI> model, then, you're going to need to set up your tables
 in a way that makes C<FromCGI> happy.
 
-=over 
+The data is untainted, and any errors are collected into a hash which is
+passed to the template. We also pass back in the parameters, so that the
+template can re-fill the form fields with the original values. The user
+is then sent back to the C<edit> template.
 
-=item Digression on C<Class::DBI::FromCGI>
+        if (my %errors = $obj->cgi_update_errors) {
+            # Set it up as it was:
+            $r->template_args->{cgi_params} = $r->params;
+            $r->template_args->{errors} = \%errors;
+            $r->template("edit");
+        }
+
+Otherwise, the user is taken back to viewing the new object:
+
+    } else {
+        $r->template("view");
+    }
+    $r->objects([ $obj ]);
+
+Notice that this does use hard-coded names for the templates to go to next.
+Feel free to override this in your subclasses:
+
+    sub do_edit :Exported {
+        my ($class, $r) = @_;
+        $class->SUPER::do_edit($r);
+        $r->template("my_edit");
+    }
+
+=head3 Digression on C<Class::DBI::FromCGI>
 
 C<CGI::Untaint> is a mechanism for testing that incoming form data
 conforms to various properties. For instance, given a C<CGI::Untaint>
@@ -167,36 +207,6 @@ This is usually integer, if you're using numeric IDs for your primary
 key. If not, you probably want C<printable>, but you probably know what
 you're doing anyway.
 
-=back
-
-The data is untainted, and any errors are collected into a hash which is
-passed to the template. We also pass back in the parameters, so that the
-template can re-fill the form fields with the original values. The user
-is then sent back to the C<edit> template.
-
-        if (my %errors = $obj->cgi_update_errors) {
-            # Set it up as it was:
-            $r->{template_args}{cgi_params} = $r->{params};
-            $r->{template_args}{errors} = \%errors;
-            $r->{template} = "edit";
-        }
-
-Otherwise, the user is taken back to viewing the new object:
-
-    } else {
-        $r->{template} = "view";
-    }
-    $r->objects([ $obj ]);
-
-Notice that this does use hard-coded names for the templates to go to next.
-Feel free to override this in your subclasses:
-
-    sub do_edit :Exported {
-        my ($class, $r) = @_;
-        $class->SUPER::do_edit($r);
-        $r->template("my_edit");
-    }
-
 =head3 delete
 
 The delete method takes a number of arguments and deletes those rows from the
@@ -218,8 +228,8 @@ URL, we want, by default, all the records in the table:
 However, things are slightly complicated by paging and ordering by
 column; the default implementation also provides a C<Class::DBI::Pager>
 object to the templates and uses that to retrieve the appropriate bit of
-the data, as specified by the C<page> URL query parameter. See the F<pager> 
-template below.
+the data, as specified by the C<page> URL query parameter. See the
+L<"pager"> template below.
 
 =head3 search
 
@@ -256,10 +266,12 @@ template view
 
 =head3 F<edit>
 
-The F<edit> template is pretty much the same as F<view>, but it uses the
+The F<edit> template is pretty much the same as F<view>, but it uses 
+L<Class::DBI::AsForm>'s
 C<to_field> method on each column of an object to return a C<HTML::Element>
 object representing a form element to edit that property. These elements
-are then rendered to HTML with C<as_HTML>. It expects to see a list of
+are then rendered to HTML with C<as_HTML> or to XHTML with C<as_XML>.
+It expects to see a list of
 editing errors, if any, in the C<errors> template variable:
 
      FOR col = classmetadata.columns;
@@ -285,4 +297,69 @@ The C<search> template argument is used to distinguish between the two cases:
     <h2> Listing of all [% classmetadata.plural %]</h2>
     [% END %]
 
-=head1 Customizing Generic CRUD Applications
+=head3 F<pager>
+
+The pager template controls the list of pages at the bottom (by default)
+of the list and search views. It expects a C<pager> template argument
+which responds to the L<Data::Page> interface.
+There's a description of how it works in
+L<the Template Toolkit section|Maypole::Manual::View/"The Template Toolkit">
+of the View chapter.
+
+=head3 F<macros>
+
+The F<macros> template is included at the start of most other templates
+and makes some generally-useful template macros available:
+
+=over
+
+=item C<link(table, command, additional, label)>
+
+This makes an HTML link pointing to C</base/table/command/additional>
+labelled by the text in I<label>. C<base> is the template variable that
+contains the base URL of this application.
+
+=item C<maybe_link_view(object)>
+
+C<maybe_link_view> takes something returned from the database - either
+some ordinary data, or an object in a related class expanded by a
+has-a relationship. If it is an object, it constructs a link to the view
+command for that object. Otherwise, it just displays the data.
+
+=item C<display_line(object)>
+
+C<display_line> is used in the list template to display a row from the
+database, by iterating over the columns and displaying the data for each
+column. It misses out the C<id> column by default, and magically
+URLifies columns called C<url>. This may be considered too much magic
+for some.
+
+=item C<button(object, action)>
+
+This is a simple button that is submitted to C</base/table/action/id>,
+where C<table> and C<id> are those belonging to the database row C<object>.
+The button is labelled with the name of the action.
+You can see buttons on many pages, including lists.
+
+=item C<view_related(object)>
+
+This takes an object, and looks up its C<related_accessors>; this gives
+a list of accessor methods that can be called to get a list of related
+objects. It then displays a title for that accessor, (e.g. "Beers" for a
+C<brewery.beers>) calls the accessor, and displays a list of the results.
+You can see it in use at the bottom of the standard view pages.
+
+=back
+
+=begin TODO
+
+=head2 Customizing Generic CRUD Applications
+
+=end TODO
+
+=head2 Links
+
+L<Contents|Maypole::Manual>,
+Next L<The Request Workflow|Maypole::Manual::Workflow>,
+Previous L<Maypole View Classes|Maypole::Manual::View>,
+
index 81ac57b8a15d33cd486c7a3ab13b136d3d9dcb29..01745ed8a5f6c6c15778f9fea03a4572c6917794 100644 (file)
@@ -18,7 +18,7 @@ lot to its preferred templating module, which will then do the hard work
 of filling in the template and coming up with the output.
 
 You can choose whatever Maypole view class you want, but the default
-view class is C<Maypole::View::TT>, and it feeds its data and templates
+view class is L<Maypole::View::TT>, and it feeds its data and templates
 to a module called the Template Toolkit.
 
 =head2 The Template Toolkit
@@ -27,18 +27,22 @@ The Template Toolkit, written by Andy Wardley, is a very powerful and
 generic templating system. It provides its own little formatting language
 which supports loops, conditionals, hash and array dereferences and
 method calls, macro processing and a plug-in system to connect it to
-external Perl modules. There are several good introductions to the
+external Perl modules.
+Its homepage is C<http://www.template-toolkit.org/>.
+There are several good introductions to the
 Template Toolkit available: you should have one installed as
 L<Template::Tutorial::Datafile>; there's one at
 L<http://www.perl.com/pub/a/2003/07/15/nocode.html>, and of course
 there's the "Badger Book" - I<The Perl Template Toolkit>, by Andy et al.
+C<http://www.oreilly.com/catalog/perltt/index.html>
 
 We'll present a brief introduction here by deconstructing some of the 
 templates used by Maypole applications. For more deconstruction, see
-L<StandardTemplates.pod>, which is an entire chapter dealing with the
+L<Standard Templates and Actions|Maypole::Manual::StandardTemplates>,
+which is an entire chapter dealing with the
 factory supplied templates.
 
-Here's the template which is called for the front page of the standard
+Here's a template that could be called for the front page of the example
 beer database application, C<custom/frontpage>.
 
     [% INCLUDE header %]
@@ -57,13 +61,13 @@ beer database application, C<custom/frontpage>.
 
 The first thing to note about this is that everything outside of the
 Template Toolkit tags (C<[%> and C<%]>) is output verbatim. That is,
-you're guaranteed to see 
+somewhere in the output you're guaranteed to see 
 
     <h2> The beer database </h2>
 
     <TABLE BORDER="0" ALIGN="center" WIDTH="70%">
 
-in the output somewhere. Inside the tags, magic happens. The first piece
+Inside the tags, magic happens. The first piece
 of magic is the C<[% INCLUDE header %]> directive. This goes away and
 finds a file called F<header> - don't worry about how it finds that yet,
 we'll come to that later on - and processes the file's contents as
@@ -78,26 +82,30 @@ The next piece of magic is this line:
 We're seeing a lot of things here at once. C<config> is where we should
 start looking. This is a template variable, which is what templates are
 all about - templating means getting data from somewhere outside and
-presenting it to the user in a useful way, and the C<config> hash is a
-prime example of data that we want to use. It's actually the hash of
-configuration parameters for this Maypole application, and one of the
-keys in that hash is C<display_tables>, the database tables that we're
-allowed to play with. In the application, we probably said something
-like
+presenting it to the user in a useful way, and C<config> is a
+prime example of data that we want to use. It's actually an object
+containing configuration parameters for this Maypole application, and
+one of the methods is C<display_tables>, which returns a list of the
+database tables that we're supposed to show. In the application, we
+probably said something like
 
-    BeerDB->config->{display_tables} = [qw[beer brewery pub style]];
+    BeerDB->config->display_tables([qw[beer brewery pub style]]);
 
 This stores the four values - C<beer>, C<brewery>, C<pub> and C<style> -
-in an array, which is placed in the config hash under the key
-C<display_tables>. Now we're getting them back again.
+in an array, which is placed in the config object using the
+accessor/mutator method C<display_tables>. Now we're getting them back
+again. Note that we're not going to show the handpump table.
 
 The Template Toolkit's dot operator is a sort of do-the-right-thing
 operator; we can say C<array.0> to get the first element of an array,
 C<hash.key> to look up the C<key> key in a hash, and C<object.method> to
 call C<method> on an object. So, for instance, if we said
-C<config.display_tables.2>, we'd look up the C<display_tables> key in
-the configuration hash and get our array back, then look up the 2nd
+C<config.display_tables.2>, we'd look up the C<display_tables> method in
+the configuration object and get our array back, then look up the 3rd
 element and get C<pub>.
+Thing is, you don't have to care whether C<display_tables> is an object
+or a hash. You can pretend it's a hash if you want. The syntax is the
+same, and Template Toolkit knows the right thing to do.
 
 The C<FOR> loop will repeat the code four times, setting our new
 variable C<table> to the appropriate array element. This code:
@@ -130,7 +138,7 @@ on a single page.
               ELSE;
                 SET args = "?page=" _ num;
                 SET label = "[" _ num _ "]";
-                link(classmetadata.moniker, "list", args, label);
+                link(classmetadata.table, "list", args, label);
               END;
          END;
     %]
@@ -144,7 +152,8 @@ We start by loading in a bunch of macros. Macros are Template Toolkit's
 functions - you can provide them some parameters and they'll run a little
 sub-template based on them. The C<macros> file contains some handy macros
 that I've found useful for constructing Maypole templates; again, these
-will be covered in full detail in L<StandardTemplates.pod>.
+will be covered in full detail in
+L<Standard Templates and Actions|Maypole::Manual::StandardTemplates>.
 
 We're going to be displaying something like this:
 
@@ -159,12 +168,7 @@ provides this: (C<FOREACH> and C<FOR> are identical, just like in Perl.)
 Here we're manually constructing an array of numbers, using the range
 operator (C<..>) to fill in all the numbers from the C<first_page> (1)
 to the C<last_page> (4). The same dot operator is used to ask the C<pager>
-what its C<first_page> and C<last_page> is. Remember when we said 
-C<config.display_tables>, we were looking up the C<display_tables> key
-in the C<config> hash? Well, this time we're not looking anything up in
-a hash. C<pager> is an object, and C<first_page> is a method. Thing is,
-you don't have to care. You can pretend it's a hash if you want. The
-syntax is the same, and Template Toolkit knows the right thing to do.
+object what its C<first_page> and C<last_page> are.
 
 Now we're going to be executing this loop four times, once each for C<num>
 being set to 1, 2, 3, and 4. At some point, we'll come across the page
@@ -208,18 +212,18 @@ Now we call a macro, C<link> with these two variables and the value of
 C<classmetadata.table>. This macro takes four arguments, C<table>, 
 C<action>, C<args> and C<label>, and constructs a link of the form
 
-    <A HREF="[% config.base_url %]/[% table %]/[% action %][% args %]">
+    <A HREF="[% base %]/[% table %]/[% action %][% args %]">
     [% label %]
     </A>
 
 In our case, it'll be filled in like so:
 
-    <A HREF="[% config.base_url %]/[% classmetadata.table %]/list?page=4">
+    <A HREF="[% base %]/[% classmetadata.table %]/list?page=4">
     [ 4 ]
     </A>
 
 Where C<classmetadata.table> will actually be the name of the current
-table, and C<config.base_url> will be replaced by the appropriate URL for
+table, and C<base> will be replaced by the appropriate URL for
 this application.
 
 =head2 Locating Templates
@@ -236,10 +240,10 @@ each of these in turn.
 When you configure a Maypole application, you can tell it the base
 directory of your templates like so:
 
-    BeerDB->config->{template_root} = "/var/www/beerdb/templates";
+    BeerDB->config->template_root("/var/www/beerdb/templates");
 
 If you don't do this, most Maypole front-ends will use the current
-directory, which is generally what you want anyway. Off this directory,
+directory, which may be what you want anyway. Off this directory,
 Maypole will look for a set of subdirectories.
 
 For instance, I said we were in the middle of processing the front page
@@ -286,12 +290,17 @@ C<Maypole::View::TT> provides quite a variety of template variables to
 the template. As these are the building blocks of your pages, it's worth
 looking at precisely what variables are available.
 
+=head3 objects
+
 The most important variable is called C<objects>, and is a list of all
 the objects that this page is going to deal with. For instance,
+if the URL is C<http://localhost/beerdb/beer/view/23>, then
 in the template F</beer/view>, C<objects> will contain the C<BeerDB::Beer>
-object for the 23rd item in the database, while F</brewery/list> will
-fill C<objects> will all the breweries; or at least, all the breweries
-on the current page.
+object for the 23rd item in the database, while for the F</brewery/list>
+template, the view will fill C<objects> with all the breweries; or at
+least, all the breweries on the current page.
+
+=head3 breweries!
 
 This variable is so important that to help design templates with it,
 C<Maypole::View::TT> provides a helpful alias to it depending on
@@ -301,18 +310,28 @@ variable called C<breweries>. If you're working on F</brewery/view>,
 though, it's available in C<brewery>, since there's only one brewery to
 be displayed.
 
+=head3 base
+
 Additionally, you can get the base URL for the application from the
 C<base> template variable; this allows you to construct links, as we
 saw earlier:
 
     <A HREF="[% base %]/brewery/edit/[% brewery.id %]">Edit this brewery</A>
 
+=head3 config
+
 You can also get at the rest of the configuration for the site with the
-C<config> variable as we saw above, and the entire request object in 
+C<config> variable as we saw above.
+
+=head3 request
+
+The entire request object is made available in 
 C<request>, should you really need to poke at it. (I've only found this
 useful when working with authentication modules which stash a current user
 object in C<request.user>.)
 
+=head3 classmetadata
+
 To allow the construction of the "generic" templates which live in
 F<factory>, Maypole also passes in a hash called C<classmetadata>,
 which contains all sorts of useful information about the class under
@@ -324,7 +343,7 @@ examination:
 
 This is the name of the table that is represented by the class.
 
-=item C<class>
+=item C<name>
 
 This is the Perl's idea of the class; you don't need this unless you're
 doing really tricky things.
@@ -332,7 +351,7 @@ doing really tricky things.
 =item C<moniker>
 
 This is a more human-readable version of the table name, that can be
-used for display.
+used for display. "brewery" for example.
 
 =item C<plural>
 
@@ -340,13 +359,19 @@ The same, but a correctly-formed plural. For instance, "breweries".
 
 =item C<columns>
 
-The list of columns for display; see the section "Customizing Generic
-CRUD Applications" in L<StandardTemplates.pod>.
+The list of columns for display; see the
+L<hard way|Maypole::Manual::Beer/"The hard way"> section in the Beer
+Database chapter.
+
+=item C<list_columns>
+
+As for C<columns>, but these are the columns to be displayed on a
+F<list> page.
 
 =item C<colnames>
 
 This is a hash mapping the database's name for a column to a more
-human-readable name. Again, see "Customizing Generic CRUD Applications>.
+human-readable name. Again, see "Customizing Generic CRUD Applications".
 
 =item C<cgi>
 
@@ -354,11 +379,11 @@ This is a slightly trickier one. It is a hash mapping column names to
 a C<HTML::Element> suitable for entering data into a new instance of
 that class. That is, for the C<beer> table, C<classmetadata.cgi.style>
 should be a C<HTML::Element> object containing a drop-down list of
-beer styles. This is explained in L<StandardTemplates.pod>.
-
-=item C<description>
+beer styles.
 
-This is the human-readable description provided by a class.
+TODO =item C<description>
+TODO
+TODO This is the human-readable description provided by a class.
 
 =item C<related_accessors>
 
@@ -371,6 +396,18 @@ ordinary one-to-one relationships, such as C<style>.
 
 =back
 
+=head3 Additional variables and overrides
+
+You can pass additional data to templates by creating new variables.
+You'd typically do this in your view class.
+Just add the name of your template variable as a key to the
+C<template_args> hash in the request object, and supply its value:
+
+  $r->template_args->{your_variable_name} = 'some_value';
+
+You can also override the value of any of the standard variables by
+giving their name as the key.
+
 =head2 Other view classes
 
 Please note that these template variables, C<config>, C<classmetadata>,
@@ -398,9 +435,9 @@ your application:
    use base Maypole::Application;
    ...
    BeerDB->setup("dbi:SQLite:t/beerdb.db");
-   BeerDB->config->{uri_base} = "http://localhost/beerdb/";
-   BeerDB->config->{rows_per_page} = 10;
-   BeerDB->config->{view} = "Maypole::View::Mason"
+   BeerDB->config->uri_base(http://localhost/beerdb/");
+   BeerDB->config->rows_per_page(10);
+   BeerDB->config->view("Maypole::View::Mason")
 
 Where do these alternate view classes come from? Gentle reader, they
 come from B<you>.
@@ -414,7 +451,7 @@ than the Template Toolkit. I know I'm stretching your imagination a bit
 here, but try. You'd like to use it with Maypole, which means writing your
 own view class. How is it done?
 
-We'll demonstrate by implementing a view class for C<HTML::Mason>,
+We'll demonstrate by implementing a view class for L<HTML::Mason>,
 although no value judgement is implied. C<HTML::Mason> is a templating
 system which embeds pure Perl code inside its magic tags. The good side
 of this is that it can get into hash references and objects, and so
@@ -424,7 +461,7 @@ running more or less standalone, and having all the template variables
 it wants already at its disposal through CGI parameters and the like, so
 we have to fiddle a bit to get these variables into our template.
 
-The key to building view classes is C<Maypole::View::Base>. This is the
+The key to building view classes is L<Maypole::View::Base>. This is the
 base class that you're going to inherit from and, to be honest, it does
 pretty much everything you need. It provides a method called C<vars>
 which returns a hash of all the template variables described above, so
@@ -437,8 +474,8 @@ can see L<Maypole::View::Base> for more about them.
 
 The module will do the right thing for us if we agree to provide a
 method called C<template>. This is responsible for taking the Maypole
-request object (of which more later) and putting the appropriate output
-either into C<$r-E<gt>{output}> or C<$r-E<gt>{error}>, depending, of
+request object C<$r> (of which more later) and putting the appropriate output
+either into C<$r-E<gt>output> or C<$r-E<gt>error>, depending, of
 course, whether things are OK or whether we got an error.
 
 Thankfully, C<HTML::Mason> makes things really easy for us. We B<can>
@@ -501,10 +538,16 @@ and return a true value to indicate that we processed everything OK. (If there
 was an error, then Mason will have produced some suitable output, so we can
 pretend that everything's OK anyway.)
 
-    $r->{output} = $output;
+    $r->output($output);
     return 1;
 
 And that's all we need to do. Barely twenty lines of code for the finished
 product. Wasn't that easy? Don't you feel inspired to write Maypole view
 classes for your favourite templating language? Well, don't let me stop you!
 Patches are always welcome!
+
+=head2 Links
+
+L<Contents|Maypole::Manual>,
+Next L<Standard Templates and Actions|Maypole::Manual::StandardTemplates>,
+Previous L<Maypole Model Classes|Maypole::Manual::Model>,
index 622d75564a6ce7fd2c766aa87af28a2a44d035d9..d860523b1dc9ebe60a8ef5c345feb517534d6a34 100644 (file)
@@ -1,10 +1,13 @@
 =pod
 
-=head1 NAME
+=head1 Maypole's Request Workflow
 
-Maypole::Manual::Workflow - Describes the progress of a request through Maypole
+This chapter describes the progress of a request through Maypole.
 
-=head1 SYNOPSIS
+An application based on C<Maypole> provides an Apache or CGI handler,
+and eventually delivers a page. This document explains how that happens,
+and how to influence it. We'll use the C<BeerDB> project as our example.
+Here's a diagram that gives an overview:
 
                               config $h
                                   |
@@ -26,18 +29,11 @@ Maypole::Manual::Workflow - Describes the progress of a request through Maypole
                                   |
                      $r->view_object->process($r)
 
-
-=head1 DESCRIPTION
-
-An application based on C<Maypole> will provide an Apache handler,
-and eventually deliver a page. This document explains how that happens,
-and how to influence it. We'll use the C<BeerDB> project as our example.
-
 =head2 Initialize class
 
-When the first request comes in, the class will call its own
-C<init> method. This creates a new view object, sets up inheritance
-relationships between the model classes and their parent, and so on.
+When the first request comes in, the application class will call its own
+C<init> method, inherited from L<Maypole>.
+This creates a new view object.
 
 =head2 Construction
 
@@ -89,15 +85,24 @@ or whatever other codes for permissions problems.
 We then look for an appropriate C<authenticate> method to call; first
 it will try calling the C<authenticate> method of the model class, or,
 if that does not exist, the C<authenticate> method on itself. By
-default, this allows access to everyone for everything. Similarly, this
-should return an Apache status code.
+default, this allows access to everyone for everything.
+Your C<authenticate> methods must return an Apache status code: C<OK> or
+C<DECLINED>. These codes are defined by the L<Maypole::Constants>
+module, which is automatically used by your application.
 
 =head2 Add any additional data to the request
 
-The open-ended C<additional_data> method allows any additional fiddling
+You can write an C<additional_data> method to do any additional fiddling
 with the request object before it is despatched. Specifically, it allows
 you to add to the C<template_args> slot, which is a hash of arguments to
-be added to the template.
+be added to the template, like this:
+
+    sub additional_data {
+        my $self = shift;
+        $self->{template_args}{answer} = 42;
+    }
+
+which adds a new template variable C<answer> with the value 42.
 
 =head2 Ask model for widget set
 
@@ -105,16 +110,6 @@ Asking the model class to C<process> the current request allows it to do
 any work it needs for the given command, and populate the C<objects> and
 C<template> slots of the request. 
 
-=head2 Ask view to process template
-
-Now the view class has its C<process> method called, finds the
-appropriate templates, passes the C<objects> and any additional data to
-the template, and pushes the output to the web server.
-
-We will go into more detail about these last two phases.
-
-=head1 Model class processing
-
 The model's C<process> method is usually a thin wrapper around the
 action that we have selected. It sets the template name to the name of
 the action, fills C<objects> with an object of that class whose ID comes
@@ -128,9 +123,13 @@ the request object. This method will usually do any actions which are
 required, including modifying the list of objects to be passed to the
 template, or the name of the template to be called.
 
-=head1 Template class processing
+=head2 Ask view to process template
+
+Now the view class has its C<process> method called. It finds the
+appropriate templates and calls the L<Template Toolkit|Template>
+processor.
 
-Finally, the template processor is handed the objects, the template
+The template processor is handed the objects, the template
 name, and various other bits and pieces, and tries to find the right
 template. It does this by looking first for C</beer/foo>: that is, a
 specific template appropriate to the class. Next, it looks at
@@ -138,63 +137,21 @@ C</custom/foo>, a local modification, before looking for
 C</factory/foo>, one of the default templates that came with
 C<Maypole>.
 
-=head2 Default template arguments
-
-The following things are passed to the Template Toolkit template by
-default:
-
-=over 3
-
-=item request
+The view puts the template's output in the C<$r-E<gt>{output}> slot. The
+application's C<handler> method calls the C<send_output> method to push
+it to the web server.
 
-The whole C<Maypole> request object, for people getting really dirty
-with the templates.
-
-=item objects
-
-The objects handed to us by the model.
-
-=item base
-
-The base URL of the application.
-
-=item config
-
-The whole configuration hash for the application.
-
-=item classmetadata
-
-A hash consisting of:
-
-C<name> - The name of the model class for the request: e.g. C<BeerDB::Beer>.
-
-C<columns> - The names of the columns in this class.
-
-C<colnames> - A hash mapping between the database's idea of a column
-name and a human-readable equivalent. (C<abv> should be mapped to
-C<A.B.V.>, perhaps.)
-
-C<related_accessors> - A list of accessors which are not exactly fields
-in the table but are related by a has-many relationship. For instance,
-breweries have many beers, so C<beers> would appear in the list.
-
-C<moniker> - The human-readable name for the class: C<beer>.
-
-C<plural> - The same, only plural: C<beers>.
-
-C<cgi> - A hash mapping columns and C<HTML::Element> objects
-representing a form field for editing that column.
-
-C<description> - (Perhaps) a user-supplied description of the class.
+=head2 Default template arguments
 
-=back
+If you're looking for the list of variables that are passed to the
+Template Toolkit template by default, you'll find it in the
+L<View|Maypole::Manual::View> chapter.
 
-Additionally, depending on the number of objects, there will be an alias
-for the C<objects> slot with the name of the moniker or plural moniker.
+=head2 Links
 
-That sounds a bit tricky, but what it means is that if you look at
-C</beer/view/4> then C<beer> will be populated with a C<BeerDB::Beer>
-object with ID 4. On the other hand, if you look at C</beer/list> you
-can get all the beers in C<beers> as well as in C<objects>.
+L<Contents|Maypole::Manual>,
+Next L<The Beer Database Revisited|Maypole::Manual::Beer>,
+Previous
+L<Standard Templates and Actions|Maypole::Manual::StandardTemplates>
 
 
index f012cbfb71b738ad213d39ed907fb8282b5cf16f..c79c027633f23ae55623b10eda75cd0c1dbb32ca 100644 (file)
@@ -50,14 +50,15 @@ Maypole::View::TT - A Template Toolkit view class for Maypole
 
 =head1 DESCRIPTION
 
-This is the default view class for Maypole; it uses the Template Toolkit
-to fill in templates with the objects produced by Maypole's model classes.
-Please see the Maypole manual, and in particular, the C<View> chapter,
-for the template variables available and for a refresher on how template
-components are resolved.
+This is the default view class for Maypole; it uses the Template Toolkit to
+fill in templates with the objects produced by Maypole's model classes.  Please
+see the Maypole manual, and in particular, the L<view|Maypole::Manual::View>
+chapter for the template variables available and for a refresher on how
+template components are resolved.
 
 The underlying Template toolkit object is configured through
-C<$r-E<gt>config-E<gt>view_options>. See L<Template> for available options.
+C<$r-E<gt>config-E<gt>view_options>. See L<Template|Template> for available
+options.
 
 =over 4