]> git.decadent.org.uk Git - maypole.git/commitdiff
Added Maypole::Manual::Inheritance and Maypole::Manual::Terminology. Renamed Maypole...
authorDavid Baird <cpan.zerofive@googlemail.com>
Sun, 30 Oct 2005 16:02:35 +0000 (16:02 +0000)
committerDavid Baird <cpan.zerofive@googlemail.com>
Sun, 30 Oct 2005 16:02:35 +0000 (16:02 +0000)
git-svn-id: http://svn.maypole.perl.org/Maypole/trunk@402 48953598-375a-da11-a14b-00016c27c3ee

lib/Maypole.pm
lib/Maypole/Manual.pod
lib/Maypole/Manual/About.pod
lib/Maypole/Manual/Cookbook.pod [new file with mode: 0644]
lib/Maypole/Manual/Inheritance.pod [new file with mode: 0644]
lib/Maypole/Manual/Install.pod [new file with mode: 0644]
lib/Maypole/Manual/Plugins.pod
lib/Maypole/Manual/Request.pod [deleted file]
lib/Maypole/Manual/Terminology.pod [new file with mode: 0644]

index cae420fea4bcf68d88ce41abdeafbfb15b167dbb..53732c480dfdfa0aa5e8aa819f475f739cb07ba7 100644 (file)
@@ -488,16 +488,39 @@ Maypole is a Perl web application framework similar to Java's struts. It is
 essentially completely abstracted, and so doesn't know anything about
 how to talk to the outside world.
 
 essentially completely abstracted, and so doesn't know anything about
 how to talk to the outside world.
 
-To use it, you need to create a package which represents your entire
+To use it, you need to create a driver  package which represents your entire
 application. In our example above, this is the C<BeerDB> package.
 
 This needs to first use L<Maypole::Application> which will make your package
 inherit from the appropriate platform driver such as C<Apache::MVC> or
 application. In our example above, this is the C<BeerDB> package.
 
 This needs to first use L<Maypole::Application> which will make your package
 inherit from the appropriate platform driver such as C<Apache::MVC> or
-C<CGI::Maypole>, and then call setup.  This sets up the model classes and
+C<CGI::Maypole>. Then, the driver calls C<setup>.  This sets up the model classes and
 configures your application. The default model class for Maypole uses
 L<Class::DBI> to map a database to classes, but this can be changed by altering
 configuration. (B<Before> calling setup.)
 
 configures your application. The default model class for Maypole uses
 L<Class::DBI> to map a database to classes, but this can be changed by altering
 configuration. (B<Before> calling setup.)
 
+
+=head1 DOCUMENTATION ROADMAP
+
+The primary documentation is the Maypole manual. This lives in the 
+C<Maypole::Manual> pod documents included with the distribution. 
+
+
+=head1 DEMOS
+
+A couple of demos are available, usually with source code and configs. 
+
+=over4 
+
+=item beerdb.riverside-cms.co.uk
+
+Looks to be down at the moment. 
+
+=item beerfb.riverside-cms.co.uk
+
+A demo of L<Maypole::FormBuilder>. This site is running on the set of Mason 
+templates included in the L<Maypole::FormBuilder> distribution. See the 
+synopsis of L<Maypole::Plugin::FormBuilder> for an example driver
+
 =head2 CLASS METHODS
 
 =head3 config
 =head2 CLASS METHODS
 
 =head3 config
index dc60222467a234b8cae8cd4ab54ac94e323bfde6..d556ae93d4274245aa04a482e200e80acafa2995 100644 (file)
@@ -71,6 +71,15 @@ 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.
 
 actions and templates, showing you how to write your own
 so that you can have a highly customized application.
 
+=item L<Maypole::Manual::Plugins> - writing Maypole plugins
+
+Useful information for plugin authors. 
+
+=item L<Maypole::Manual::Terminology> - pinning down usage
+
+As well as defining common terms used in Maypole discussions, this document 
+briefly discusses the MVC-ness of Maypole. 
+
 =item L<Maypole::Manual::Workflow> - Description of the Request Workflow 
 
 This is a technical document that describes the progress of a
 =item L<Maypole::Manual::Workflow> - Description of the Request Workflow 
 
 This is a technical document that describes the progress of a
@@ -83,7 +92,7 @@ and not to most of those who are using it.
 This document gives a close look at the Beer database that
 was introduced in L<Maypole::Manual::About>.
 
 This document gives a close look at the Beer database that
 was introduced in L<Maypole::Manual::About>.
 
-=item L<Maypole::Manual::Request> - The Request Cookbook 
+=item L<Maypole::Manual::Cookbook> - The Maypole Cookbook 
 
 This extensive document is Maypole's main "How do I do X?" FAQ.
 It provides a wide variety of cookbook-like techniques that
 
 This extensive document is Maypole's main "How do I do X?" FAQ.
 It provides a wide variety of cookbook-like techniques that
index d78be34aea8df871866f7706dea05b054598062d..6f48663e45787434fd658b00ebfc863b1fd344f8 100644 (file)
@@ -74,48 +74,6 @@ logic of your application. This is one of the reasons why Maypole lets
 you develop so rapidly: because most of the time, you don't need to do
 any development at all.
 
 you develop so rapidly: because most of the time, you don't need to do
 any development at all.
 
-=head2 Installing Maypole
-
-The first thing you're going to need to do to get Maypole running is to
-install it. Maypole needs an absolute shedload of Perl modules from CPAN
-to do its job. I am unrepentant about this. Maypole does a lot of work,
-so that you don't have to. This is called code re-use, and if we're
-serious about code re-use, then Maypole should be re-using as much code
-as possible in terms of Perl modules. In another sense, this gives the
-impression that Maypole doesn't actually do all that much itself,
-because all it's doing is gluing together already-existing code. Well,
-welcome to code re-use.
-
-The downside of code re-use is, of course, that you then have to install
-a shedload of Perl modules from CPAN. If you're using OpenBSD or
-FreeBSD, the wonderful ports system will be your friend. There's a
-Maypole port in C<p5-Maypole>. Just type C<make install>.
-
-Debian users, hang in there. There's a package coming.
-
-For other Unices, the L<CPANPLUS> or C<CPAN> modules will help with
-this. If you don't have C<CPANPLUS> installed, my recommendation is to
-use C<perl -MCPAN -e install CPANPLUS> to install it and then throw
-C<CPAN.pm> away. In any case, one of these two should get all that
-Maypole needs:
-
-    % perl -MCPANPLUS -e 'install Maypole'
-    % perl -MCPAN -e 'install Maypole'
-
-I don't know if Maypole works on Windows. I'm not sure I care.
-
-You're also going to need a database server and a web server. For
-databases, I recommend SQLite (if you install the C<DBD::SQLite> module,
-you get the SQLite library for free) for prototyping and mysql for
-production; heavier duty users should use Postgresql or Oracle - Maypole
-should be happy with them all. Maypole is happiest when running under
-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
 
 
 =head2 The Beer Database example
 
diff --git a/lib/Maypole/Manual/Cookbook.pod b/lib/Maypole/Manual/Cookbook.pod
new file mode 100644 (file)
index 0000000..1d2395c
--- /dev/null
@@ -0,0 +1,839 @@
+=head1 NAME\r
+\r
+Maypole::Manual::Cookbook - Maypole Cookbook\r
+\r
+=head1 DESCRIPTION\r
+\r
+Hacks; design patterns; recipes: call it what you like, this chapter is a\r
+developing collection of techniques which can be slotted in to Maypole\r
+applications to solve common problems or make the development process easier.\r
+\r
+As Maypole developers, we don't necessarily know the "best practice" for\r
+developing Maypole applications ourselves, in the same way that Larry Wall\r
+didn't know all about the best Perl programming style as soon as he wrote\r
+Perl. These techniques are what we're using at the moment, but they may\r
+be refined, modularized, or rendered irrelevant over time. But they've\r
+certainly saved us a bunch of hours work.\r
+\r
+=head2 Frontend hacks\r
+\r
+These hacks deal with changing the way Maypole relates to the outside world;\r
+alternate front-ends to the Apache and CGI interfaces, or subclassing chunks\r
+of the front-end modules to alter Maypole's behaviour in particular ways.\r
+\r
+=head3 Separate model class modules\r
+\r
+You want to put all the C<BeerDB::Beer> routines in a separate module,\r
+so you say:\r
+\r
+    package BeerDB::Beer;\r
+    BeerDB::Beer->has_a(brewery => "BeerDB::Brewery");\r
+    sub foo :Exported {}\r
+\r
+And in F<BeerDB.pm>, you put:\r
+\r
+    use BeerDB::Beer;\r
+\r
+It doesn't work.\r
+\r
+B<Solution>: It doesn't work because of the timing of the module loading.\r
+C<use BeerDB::Beer> will try to set up the C<has_a> relationships\r
+at compile time, when the database tables haven't even been set up,\r
+since they're set up by\r
+\r
+    BeerDB->setup("...")\r
+\r
+which does its stuff at runtime. There are two ways around this; you can\r
+either move the C<setup> call to compile time, like so:\r
+\r
+    BEGIN { BeerDB->setup("...") }\r
+\r
+or move the module loading to run-time (my preferred solution):\r
+\r
+    BeerDB->setup("...");\r
+    BeerDB::Beer->require;\r
+\r
+=head3 Redirecting to SSL for sensitive information\r
+\r
+You have a website with forms that people will be entering sensitive information into,\r
+such as credit cards or login details. You want to make sure that they aren't sent\r
+in plain text but over SSL instead.\r
+\r
+B<Solution>\r
+\r
+The solution is a bit tricky for 2 reasons :\r
+\r
+Firstly -- Many browsers and web clients will change a redirected \r
+POST request into a GET request (which displays all that sensitive information in the\r
+browser, or access logs and possibly elsewhere) and/or drops the values on the floor.\r
+\r
+Secondly -- If somebody has sent that sensitive information in plain text already, then\r
+sending it again over SSL won't solve the problem.\r
+\r
+Redirecting a request is actually rather simple :\r
+\r
+$r->redirect_request('https://www.example.com/path'); # perldoc Maypole for API\r
+\r
+.. as is checking the protocol :\r
+\r
+$r->get_protocol(); # returns 'http' or 'https'\r
\r
+You should check that the action that generates the form that people will enter\r
+the sensitive information into is https and redirect if not.\r
+\r
+You should also check that no information is lost when redirecting, possibly by \r
+storing it in a session and retrieving it later - see Maypole::Plugin::Session\r
+\r
+=head3 Debugging with the command line\r
+\r
+You're seeing bizarre problems with Maypole output, and you want to test it in\r
+some place outside of the whole Apache/mod_perl/HTTP/Internet/browser circus.\r
+\r
+B<Solution>: Use the L<Maypole::CLI> module to go directly from a URL to\r
+standard output, bypassing Apache and the network altogether.\r
+\r
+L<Maypole::CLI> is not a standalone front-end, but to allow you to debug your\r
+applications without having to change the front-end they use, it temporarily\r
+"borgs" an application. If you run it from the command line, you're expected\r
+to use it like so:\r
+\r
+    perl -MMaypole::CLI=Application -e1 'http://your.server/path/table/action'\r
+\r
+For example:\r
+\r
+    perl -MMaypole::CLI=BeerDB -e1 'http://localhost/beerdb/beer/view/1?o2=desc'\r
+\r
+You can also use the C<Maypole::CLI> module programatically to create\r
+test suites for your application. See the Maypole tests themselves or\r
+the documentation to C<Maypole::CLI> for examples of this.\r
+\r
+Don't forget also to turn on debugging output in your application:\r
+\r
+    package BeerDB;\r
+    use strict;\r
+    use warnings;\r
+    use Maypole::Application qw(-Debug);\r
+\r
+=head3 Changing how URLs are parsed\r
+\r
+You don't like the way Maypole URLs look, and want something that either\r
+fits in with the rest of your site or hides the internal workings of the\r
+system.\r
+\r
+B<Solution>: So far we've been using the C</table/action/id/args> form\r
+of a URL as though it was "the Maypole way"; well, there is no Maypole\r
+way. Maypole is just a framework and absolutely everything about it is \r
+overridable. \r
+\r
+If we want to provide our own URL handling, the method to override in\r
+the driver class is C<parse_path>. This is responsible for taking\r
+C<$r-E<gt>path> and filling the C<table>, C<action> and C<args> slots\r
+of the request object. Normally it does this just by splitting the path\r
+on 'C</>' characters, but you can do it any way you want, including\r
+getting the information from C<POST> form parameters or session variables. \r
+\r
+For instance, suppose we want our URLs to be of the form\r
+C<ProductDisplay.html?id=123>, we could provide a C<parse_path> method\r
+like so:\r
+\r
+    sub parse_path {\r
+        my $r = shift;\r
+        $r->path("ProductList.html") unless $r->path;\r
+        ($r->path =~ /^(.*?)([A-Z]\w+)\.html/);\r
+        $r->table(lc $1);\r
+        $r->action(lc $2);\r
+        my %query = $r->ar->args;\r
+        $self->args([ $query{id} ]);\r
+    }\r
+\r
+This takes the path, which already has the query parameters stripped off\r
+and parsed, and finds the table and action portions of the filename,\r
+lower-cases them, and then grabs the C<id> from the query. Later methods\r
+will confirm whether or not these tables and actions exist.\r
+\r
+See the L<iBuySpy Portal|Maypole::Manual::BuySpy> for another\r
+example of custom URL processing.\r
+\r
+=head3 Maypole for mobile devices\r
+\r
+You want Maypole to use different templates to display on particular\r
+browsers.\r
+\r
+B<Solution>: There are several ways to do this, but here's the neatest\r
+we've found. Maypole chooses where to get its templates either by\r
+looking at the C<template_root> config parameter or, if this is not\r
+given, calling the C<get_template_root> method to ask the front-end to\r
+try to work it out. We can give the front-end a little bit of help, by\r
+putting this method in our driver class:\r
+\r
+    sub get_template_root {\r
+        my $r = shift;\r
+        my $browser = $r->headers_in->get('User-Agent');\r
+        if ($browser =~ /mobile|palm|nokia/i) {\r
+            "/home/myapp/templates/mobile";\r
+        } else {\r
+            "/home/myapp/templates/desktop";\r
+        }\r
+    }\r
+\r
+(Maybe there's a better way to detect a mobile browser, but you get the\r
+idea.)\r
+\r
+=head2 Content display hacks\r
+\r
+These hacks deal primarily with the presentation of data to the user,\r
+modifying the F<view> template or changing the way that the results of\r
+particular actions are displayed.\r
+\r
+=head3 Null Action\r
+\r
+You need an "action" which doesn't really do anything, but just formats\r
+up a template.\r
+\r
+B<Solution>: There are two ways to do this, depending on what precisely\r
+you need. If you just need to display a template, C<Apache::Template>\r
+style, with no Maypole objects in it, then you don't need to write any\r
+code; just create your template, and it will be available in the usual\r
+way.\r
+\r
+If, on the other hand, you want to display some data, and what you're\r
+essentially doing is a variant of the C<view> action, then you need to\r
+ensure that you have an exported action, as described in the\r
+L<templates and actions|Maypole::Manual::StandardTemplates/"C<view> and C<edit>">\r
+chapter:\r
+\r
+    sub my_view :Exported { }\r
+\r
+=head3 Template Switcheroo\r
+\r
+An action doesn't have any data of its own to display, but needs to display\r
+B<something>.\r
+\r
+B<Solution>: This is an B<extremely> common hack. You've just issued an\r
+action like C<beer/do_edit>, which updates the database. You don't want\r
+to display a page that says "Record updated" or similar. Lesser\r
+application servers would issue a redirect to have the browser request\r
+C</beer/view/I<id>> instead, but we can actually modify the Maypole\r
+request on the fly and, after doing the update, pretend that we were\r
+going to C</beer/view/I<id>> all along. We do this by setting the\r
+objects in the C<objects> slot and changing the C<template> to the\r
+one we wanted to go to.\r
+\r
+In this example from L<Flox|Maypole::Manual::Flox>, we've just\r
+performed an C<accept> method on a C<Flox::Invitation> object and we\r
+want to go back to viewing a user's page.\r
+\r
+    sub accept :Exported {\r
+        my ($self, $r) = @_;\r
+        my $invitation = $r->objects->[0];\r
+        # [... do stuff to $invitation ...]\r
+        $r->objects([$r->user]);\r
+        $r->model_class("Flox::User");\r
+        $r->template("view");\r
+    }\r
+\r
+This hack is so common that it's expected that there'll be a neater\r
+way of doing this in the future.\r
+\r
+=head3 XSLT\r
+\r
+Here's a hack I've used a number of times. You want to store structured\r
+data in a database and to abstract out its display.\r
+\r
+B<Solution>: You have your data as XML, because handling big chunks of\r
+XML is a solved problem. Build your database schema as usual around the\r
+important elements that you want to be able to search and browse on. For\r
+instance, I have an XML format for songs which has a header section of\r
+the key, title and so on, plus another section for the lyrics and\r
+chords:\r
+\r
+    <song>\r
+        <header>\r
+            <title>Layla</title>\r
+            <artist>Derek and the Dominos</artist>\r
+            <key>Dm</key>\r
+        </header>\r
+        <lyrics>\r
+          <verse>...</verse>\r
+          <chorus>\r
+            <line> <sup>A</sup>Lay<sup>Dm</sup>la <sup>Bb</sup> </line> \r
+            <line> <sup>C</sup>Got me on my <sup>Dm</sup>knees </line> \r
+            ...\r
+\r
+I store the title, artist and key in the database, as well as an "xml"\r
+field which contains the whole song as XML.\r
+\r
+To load the songs into the database, I can C<use> the driver class for\r
+my application, since that's a handy way of setting up the database classes\r
+we're going to need to use. Then the handy L<XML::TreeBuilder> will handle\r
+the XML parsing for us:\r
+\r
+    use Songbook;\r
+    use XML::TreeBuilder;\r
+    my $t = XML::TreeBuilder->new;\r
+    $t->parse_file("songs.xml");\r
+\r
+    for my $song ($t->find("song")) {\r
+        my ($key) = $song->find("key"); $key &&= $key->as_text;\r
+        my ($title) = $song->find("title"); $title = $title->as_text;\r
+        my ($artist) = $song->find("artist"); $artist = $artist->as_text;\r
+        my ($first_line) = $song->find("line");\r
+        $first_line = join "", grep { !ref } $first_line->content_list;\r
+        $first_line =~ s/[,\.\?!]\s*$//;\r
+        Songbook::Song->find_or_create({\r
+            title => $title,\r
+            first_line => $first_line,\r
+            song_key => Songbook::SongKey->find_or_create({name => $key}),\r
+            artist => Songbook::Artist->find_or_create({name => $artist}),\r
+            xml => $song->as_XML\r
+        });\r
+    }\r
+\r
+Now we need to set up the custom display for each song; thankfully, with\r
+the L<Template::Plugin::XSLT> module, this is as simple as putting the\r
+following into F<templates/song/view>:\r
+\r
+    [%\r
+        USE transform = XSLT("song.xsl");\r
+        song.xml | $transform\r
+    %]\r
+\r
+We essentially pipe the XML for the selected song through to an XSL\r
+transformation, and this will fill out all the HTML we need. Job done.\r
+\r
+=head3 Displaying pictures\r
+\r
+You want to serve a picture, a Word document, or something else which\r
+doesn't have a content type of C<text/html>, out of your database.\r
+\r
+B<Solution>: Fill the content and content-type yourself.\r
+\r
+Here's a subroutine which displays the C<photo> for either a specified\r
+user or the currently logged in user. We set the C<output> slot of the\r
+Maypole request object: if this is done then the view class is not called\r
+upon to process a template, since we already have some output to display.\r
+We also set the C<content_type> using one from the database.\r
+\r
+    sub view_picture :Exported {\r
+        my ($self, $r) = @_;\r
+        my $user = $r->objects->[0];\r
+        $r->content_type($user->photo_type);\r
+        $r->output($user->photo);\r
+    }\r
+\r
+Of course, the file doesn't necessarily need to be in the database\r
+itself; if your file is stored in the filesystem, but you have a file\r
+name or some other pointer in the database, you can still arrange for\r
+the data to be fetched and inserted into C<$r-E<gt>output>.\r
+\r
+=head3 REST\r
+\r
+You want to provide a programmatic interface to your Maypole site.\r
+\r
+B<Solution>: The best way to do this is with C<REST>, which uses a\r
+descriptive URL to encode the request. For instance, in\r
+L<Flox|Maypole::Manual::Flox> we\r
+describe a social networking system. One neat thing you can do with\r
+social networks is to use them for reputation tracking, and we can use\r
+that information for spam detection. So if a message arrives from\r
+C<person@someco.com>, we want to know if they're in our network of\r
+friends or not and mark the message appropriately. We'll do this by\r
+having a web agent (say, L<WWW::Mechanize> or L<LWP::UserAgent>) request\r
+a URL of the form\r
+C<http://flox.simon-cozens.org/user/relationship_by_email/person%40someco.com>.\r
+Naturally, they'll need to present the appropriate cookie just like a\r
+normal browser, but that's a solved problem. We're just interested in\r
+the REST request.\r
+\r
+The request will return a single integer status code: 0 if they're not\r
+in the system at all, 1 if they're in the system, and 2 if they're our\r
+friend.\r
+\r
+All we need to do to implement this is provide the C<relationship_by_email>\r
+action, and use it to fill in the output in the same way as we did when\r
+displaying a picture. Since C<person%40someco.com> is not the ID of a\r
+row in the user table, it will appear in the C<args> array:\r
+\r
+    use URI::Escape;\r
+    sub relationship_by_email :Exported {\r
+        my ($self, $r) = @_;\r
+        my $email = uri_unescape($r->args->[0]);\r
+        $r->content_type("text/plain");\r
+        my $user;\r
+        unless (($user) = Flox::User->search(email => $email)) {\r
+            $r->content("0\n"); return;\r
+        }\r
+\r
+        if ($r->user->is_friend($user)) { $r->contenti("2\n"); return; };\r
+        $r->content("1\n"); return;\r
+    }\r
+\r
+=head3 Component-based Pages\r
+\r
+You're designing something like a portal site which has a number of\r
+components, all displaying different bits of information about different\r
+objects. You want to include the output of one Maypole request call while\r
+building up another. \r
+\r
+B<Solution>: Use L<Maypole::Plugin::Component>. By inheriting like this:\r
+\r
+    package BeerDB;\r
+    use Maypole::Application qw(Component);\r
+\r
+you can call the C<component> method on the Maypole request object to\r
+make a "sub-request". For instance, if you have a template\r
+\r
+    <DIV class="latestnews">\r
+    [% request.component("/news/latest_comp") %]\r
+    </DIV>\r
+\r
+    <DIV class="links">\r
+    [% request.component("/links/list_comp") %]\r
+    </DIV>\r
+\r
+then the results of calling the C</news/latest_comp> action and template\r
+will be inserted in the C<latestnews> DIV, and the results of calling\r
+C</links/list_comp> will be placed in the C<links> DIV. Naturally, you're\r
+responsible for exporting actions and creating templates which return \r
+fragments of HTML suitable for inserting into the appropriate locations.\r
+\r
+Alternatively, if you've already got all the objects you need, you can\r
+probably just C<[% PROCESS %]> the templates directly.\r
+\r
+=head3 Bailing out with an error\r
+\r
+Maypole's error handling sucks. Something really bad has happened to the\r
+current request, and you want to stop processing now and tell the user about\r
+it.\r
+\r
+B<Solution>: Maypole's error handling sucks because you haven't written it\r
+yet. Maypole doesn't know what you want to do with an error, so it doesn't\r
+guess. One common thing to do is to display a template with an error message\r
+in it somewhere.\r
+\r
+Put this in your driver class:\r
+\r
+    sub error { \r
+        my ($r, $message) = @_;\r
+        $r->template("error");\r
+        $r->template_args->{error} = $message;\r
+        return OK;\r
+    }\r
+\r
+And then have a F<custom/error> template like so:\r
+\r
+    [% PROCESS header %]\r
+    <H2> There was some kind of error... </H2>\r
+    <P>\r
+    I'm sorry, something went so badly wrong, we couldn't recover. This\r
+    may help:\r
+    </P>\r
+    <DIV CLASS="messages"> [% error %] </DIV>\r
+\r
+Now in your actions you can say things like this:\r
+\r
+    if (1 == 0) { return $r->error("Sky fell!") }\r
+\r
+This essentially uses the template switcheroo hack to always display the\r
+error template, while populating the template with an C<error> parameter.\r
+Since you C<return $r-E<gt>error>, this will terminate the processing\r
+of the current action.\r
+\r
+The really, really neat thing about this hack is that since C<error>\r
+returns C<OK>, you can even use it in your C<authenticate> routine:\r
+\r
+    sub authenticate {\r
+        my ($self, $r) = @_;\r
+        $r->get_user;\r
+        return $r->error("You do not exist. Go away.")\r
+            if $r->user and $r->user->status ne "real";\r
+        ...\r
+    }\r
+\r
+This will bail out processing the authentication, the model class, and\r
+everything, and just skip to displaying the error message. \r
+\r
+Non-showstopper errors or other notifications are best handled by tacking a\r
+C<messages> template variable onto the request:\r
+\r
+    if ((localtime)[6] == 1) {\r
+        push @{$r->template_args->{messages}}, "Warning: Today is Monday";\r
+    }\r
+\r
+Now F<custom/messages> can contain:\r
+\r
+    [% IF messages %]\r
+    <DIV class="messages">\r
+    <UL>\r
+        [% FOR message = messages %]\r
+           <LI> [% message %] </LI>\r
+        [% END %]\r
+    </UL>\r
+    </DIV>\r
+    [% END %]\r
+\r
+And you can display messages to your user by adding C<PROCESS messages> at an\r
+appropriate point in your template; you may also want to use a template\r
+switcheroo to ensure that you're displaying a page that has the messages box in\r
+it.\r
+\r
+=head2 Authentication and Authorization hacks\r
+\r
+The next series of hacks deals with providing the concept of a "user" for\r
+a site, and what you do with one when you've got one.\r
+\r
+=head3 Logging In\r
+\r
+You need the concept of a "current user".\r
+\r
+B<Solution>: Use something like\r
+L<Maypole::Plugin::Authentication::UserSessionCookie> to authenticate\r
+a user against a user class and store a current user object in the\r
+request object.\r
+\r
+C<UserSessionCookie> provides the C<get_user> method which tries to get\r
+a user object, either based on the cookie for an already authenticated\r
+session, or by comparing C<user> and C<password> form parameters\r
+against a C<user> table in the database. Its behaviour is highly\r
+customizable and described in its documentation.\r
+\r
+=head3 Pass-through login\r
+\r
+You want to intercept a request from a non-logged-in user and have\r
+them log in before sending them on their way to wherever they were\r
+originally going. Override C<Maypole::authenticate> in your driver\r
+class, something like this:\r
+\r
+B<Solution>:\r
+\r
+    use Maypole::Constants; # Otherwise it will silently fail!\r
+\r
+    sub authenticate {\r
+        my ($self, $r) = @_;\r
+        $r->get_user;\r
+        return OK if $r->user;\r
+        # Force them to the login page.\r
+        $r->template("login");\r
+        return OK;\r
+    }\r
+\r
+This will display the C<login> template, which should look something\r
+like this:\r
+\r
+    [% INCLUDE header %]\r
+\r
+      <h2> You need to log in </h2>\r
+\r
+    <DIV class="login">\r
+    [% IF login_error %]\r
+       <FONT COLOR="#FF0000"> [% login_error %] </FONT>\r
+    [% END %]\r
+      <FORM ACTION="[% base ; '/' ; request.path %]" METHOD="post">\r
+    Username: \r
+        <INPUT TYPE="text" NAME="[% config.auth.user_field || "user" %]"><BR>\r
+    Password: <INPUT TYPE="password" NAME="password"> <BR>\r
+    <INPUT TYPE="submit">\r
+    </FORM>\r
+    </DIV>\r
+    [% INCLUDE footer %]\r
+\r
+Notice that this request gets C<POST>ed back to wherever it came from, using\r
+C<request.path>. This is because if the user submits correct credentials,\r
+C<get_user> will now return a valid user object, and the request will pass\r
+through unhindered to the original URL.\r
+\r
+=head3 Logging Out\r
+\r
+Now your users are logged in, you want a way of having them log out\r
+again and taking the authentication cookie away from them, sending\r
+them back to the front page as an unprivileged user.\r
+\r
+B<Solution>: Just call the C<logout> method of\r
+C<Maypole::Plugin::Authentication::UserSessionCookie>. You may also want\r
+to use the template switcheroo hack to send them back to the frontpage.\r
+\r
+=head3 Multi-level Authorization\r
+\r
+You have both a global site access policy (for instance, requiring a\r
+user to be logged in except for certain pages) and a policy for\r
+particular tables. (Only allowing an admin to delete records in some\r
+tables, say, or not wanting people to get at the default set of methods\r
+provided by the model class.) \r
+\r
+You don't know whether to override the global C<authenticate> method or\r
+provide one for each class.\r
+\r
+B<Solution>: Do both.\r
+Maypole checks whether there is an C<authenticate> method for the model\r
+class (e.g. BeerDB::Beer) and if so calls that. If there's no such\r
+method, it calls the default global C<authenticate> method in C<Maypole>,\r
+which always succeeds. You can override the global method as we saw\r
+above, and you can provide methods in the model classes.\r
+\r
+To use per-table access control you can just add methods to your model\r
+subclasses that specify individual policies, perhaps like this:\r
+\r
+    sub authenticate { # Ensure we can only create, reject or accept\r
+        my ($self, $r) = @_;\r
+        return OK if $r->action =~ /^(issue|accept|reject|do_edit)$/;\r
+        return; # fail if any other action\r
+    }\r
+\r
+If you define a method like this, the global C<authenticate> method will\r
+not be called, so if you want it to be called you need to do so\r
+explicitly:\r
+\r
+    sub authenticate { # Ensure we can only create, reject or accept\r
+        my ($self, $r) = @_;\r
+        return unless $r->authenticate($r) == OK; # fail if not logged in\r
+        # now it's safe to use $r->user\r
+        return OK if $r->action =~ /^(accept|reject)$/\r
+            or ($r->user eq 'fred' and $r->action =~ /^(issue|do_edit)$/);\r
+        return; # fail if any other action\r
+    }\r
+\r
+=head2 Creating and editing hacks\r
+\r
+These hacks particularly deal with issues related to the C<do_edit>\r
+built-in action.\r
+\r
+=head3 Limiting data for display\r
+\r
+You want the user to be able to type in some text that you're later\r
+going to display on the site, but you don't want them to stick images in\r
+it, launch cross-site scripting attacks or otherwise insert messy HTML.\r
+\r
+B<Solution>: Use the L<CGI::Untaint::html> module to sanitize the HTML\r
+on input. C<CGI::Untaint::html> uses L<HTML::Sanitizer> to ensure that\r
+tags are properly closed and can restrict the use of certain tags and\r
+attributes to a pre-defined list.\r
+\r
+Simply replace:\r
+\r
+    App::Table->untaint_columns(\r
+        text      => [qw/name description/]\r
+    );\r
+\r
+with:\r
+\r
+    App::Table->untaint_columns(\r
+        html      => [qw/name description/]\r
+    );\r
+\r
+And incoming HTML will be checked and cleaned before it is written to\r
+the database.\r
+\r
+=head3 Getting data from external sources\r
+\r
+You want to supplement the data received from a form with additional\r
+data from another source.\r
+\r
+B<Solution>: Munge the contents of C< $r-E<gt>params > before jumping\r
+to the original C<do_edit> routine. For instance, in this method,\r
+we use a L<Net::Amazon> object to fill in some fields of a database row\r
+based on an ISBN:\r
+\r
+    use Net::Amazon;\r
+    my $amazon = Net::Amazon->new(token => 'YOUR_AMZN_TOKEN');\r
+\r
+    ...\r
+\r
+    sub create_from_isbn :Exported {\r
+       my ($self, $r) = @_;\r
+       my $book_info = $amazon->search(asin => $r->params->{isbn})->properties;\r
+\r
+       # Rewrite the CGI parameters with the ones from Amazon\r
+       $r->params->{title} = $book_info->title;\r
+       $r->params->{publisher} = $book_info->publisher;\r
+       $r->params->{year} = $book_info->year;\r
+       $r->params->{author} = join('and', $book_info->authors());\r
\r
+       # And jump to the usual edit/create routine\r
+       $self->do_edit($r);\r
+    }\r
+\r
+The request will carry on as though it were a normal C<do_edit> POST, but\r
+with the additional fields we have provided.\r
+You might also want to add a template switcheroo so the user can verify\r
+the details you imported.\r
+\r
+=head3 Catching errors in a form\r
+\r
+A user has submitted erroneous input to an edit/create form. You want to\r
+send him back to the form with errors displayed against the erroneous\r
+fields, but have the other fields maintain the values that the user\r
+submitted.\r
+\r
+B<Solution>: This is basically what the default C<edit> template and\r
+C<do_edit> method conspire to do, but it's worth highlighting again how\r
+they work. \r
+\r
+If there are any errors, these are placed in a hash, with each error\r
+keyed to the erroneous field. The hash is put into the template as\r
+C<errors>, and we process the same F<edit> template again:\r
+\r
+        $r->template_args->{errors} = \%errors;\r
+        $r->template("edit");\r
+\r
+This throws us back to the form, and so the form's template should take\r
+note of the errors, like so:\r
+\r
+     FOR col = classmetadata.columns;\r
+        NEXT IF col == "id";\r
+        "<P>";\r
+        "<B>"; classmetadata.colnames.$col; "</B>";\r
+        ": ";\r
+            item.to_field(col).as_HTML;\r
+        "</P>";\r
+        IF errors.$col;\r
+            "<FONT COLOR=\"#ff0000\">"; errors.$col; "</FONT>";\r
+        END;\r
+    END;\r
+\r
+If we're designing our own templates, instead of using generic ones, we\r
+can make this process a lot simpler. For instance:\r
+\r
+    <TR><TD>\r
+    First name: <INPUT TYPE="text" NAME="forename">\r
+    </TD>\r
+    <TD>\r
+    Last name: <INPUT TYPE="text" NAME="surname">\r
+    </TD></TR>\r
+\r
+    [% IF errors.forename OR errors.surname %]\r
+        <TR>\r
+        <TD><SPAN class="error">[% errors.forename %]</SPAN> </TD>\r
+        <TD><SPAN class="error">[% errors.surname %]</SPAN> </TD>\r
+        </TR>\r
+    [% END %]\r
+\r
+The next thing we want to do is to put the originally-submitted values\r
+back into the form. We can do this relatively easily because Maypole\r
+passes the Maypole request object to the form, and the POST parameters\r
+are going to be stored in a hash as C<request.params>. Hence:\r
+\r
+    <TR><TD>\r
+    First name: <INPUT TYPE="text" NAME="forename"\r
+    VALUE="[%request.params.forename%]">\r
+    </TD>\r
+    <TD>\r
+    Last name: <INPUT TYPE="text" NAME="surname"\r
+    VALUE="[%request.params.surname%]"> \r
+    </TD></TR>\r
+\r
+Finally, we might want to only re-fill a field if it is not erroneous, so\r
+that we don't get the same bad input resubmitted. This is easy enough:\r
+\r
+    <TR><TD>\r
+    First name: <INPUT TYPE="text" NAME="forename"\r
+    VALUE="[%request.params.forename UNLESS errors.forename%]">\r
+    </TD>\r
+    <TD>\r
+    Last name: <INPUT TYPE="text" NAME="surname"\r
+    VALUE="[%request.params.surname UNLESS errors.surname%]"> \r
+    </TD></TR>\r
+\r
+=head3 Uploading files and other data\r
+\r
+You want the user to be able to upload files to store in the database.\r
+\r
+B<Solution>: It's messy.\r
+\r
+First, we set up an upload form, in an ordinary dummy action. Here's\r
+the action:\r
+\r
+    sub upload_picture : Exported {}\r
+\r
+And here's the F<custom/upload_picture> template:\r
+\r
+    <FORM action="/user/do_upload" enctype="multipart/form-data" method="POST">\r
+\r
+    <P> Please provide a picture in JPEG, PNG or GIF format:\r
+    </P>\r
+    <INPUT TYPE="file" NAME="picture">\r
+    <BR>\r
+    <INPUT TYPE="submit">\r
+    </FORM>\r
+\r
+(Although you'll probably want a bit more HTML around it than that.)\r
+\r
+Now we need to write the C<do_upload> action. At this point we have to get a\r
+little friendly with the front-end system. If we're using L<Apache::Request>,\r
+then the C<upload> method of the C<Apache::Request> object (which\r
+L<Apache::MVC> helpfully stores in C<$r-E<gt>{ar}>) will work for us:\r
+\r
+    sub do_upload :Exported {\r
+        my ($class, $r) = @_;\r
+        my $user = $r->user;\r
+        my $upload = $r->ar->upload("picture");\r
+\r
+This returns a L<Apache::Upload> object, which we can query for its\r
+content type and a file handle from which we can read the data. It's\r
+also worth checking the image isn't going to be too massive before we\r
+try reading it and running out of memory, and that the content type is\r
+something we're prepared to deal with. \r
+\r
+    if ($upload) {\r
+        my $ct = $upload->info("Content-type");\r
+        return $r->error("Unknown image file type $ct")\r
+            if $ct !~ m{image/(jpeg|gif|png)};\r
+        return $r->error("File too big! Maximum size is ".MAX_IMAGE_SIZE)\r
+            if $upload->size > MAX_IMAGE_SIZE;\r
+\r
+        my $fh = $upload->fh;\r
+        my $image = do { local $/; <$fh> };\r
+\r
+Don't forget C<binmode()> in there if you're on a platform that needs it.\r
+Now we can store the content type and data into our database, store it\r
+into a file, or whatever:\r
+\r
+        $r->user->photo_type($ct);\r
+        $r->user->photo($image);\r
+    }\r
+\r
+And finally, we use our familiar template switcheroo hack to get back to\r
+a useful page:\r
+\r
+        $r->objects([ $user ]);\r
+        $r->template("view");\r
+    }\r
+\r
+Now, as we've mentioned, this only works because we're getting familiar with\r
+C<Apache::Request> and its C<Apache::Upload> objects. If we're using\r
+L<CGI::Maypole> instead, we can write the action in a similar style:\r
+\r
+    sub do_upload :Exported {\r
+        my ($class, $r) = @_;\r
+        my $user = $r->user;\r
+        my $cgi = $r->cgi;\r
+        if ($cgi->upload == 1) { # if there was one file uploaded\r
+            my $filename = $cgi->param('picture');\r
+            my $ct = $cgi->upload_info($filename, 'mime');\r
+            return $r->error("Unknown image file type $ct")\r
+                if $ct !~ m{image/(jpeg|gif|png)};\r
+            return $r->error("File too big! Maximum size is ".MAX_IMAGE_SIZE)\r
+                if $cgi->upload_info($filename, 'size') > MAX_IMAGE_SIZE;\r
+            my $fh = $cgi->upload($filename);\r
+            my $image = do { local $/; <$fh> };\r
+            $r->user->photo_type($ct);\r
+            $r->user->photo($image);\r
+        }\r
+\r
+        $r->objects([ $user ]);\r
+        $r->template("view");\r
+    }\r
+\r
+It's easy to adapt this to upload multiple files if desired.\r
+You will also need to enable uploads in your driver initialization,\r
+with the slightly confusing statement:\r
+\r
+    $CGI::Simple::DISABLE_UPLOADS = 0; # enable uploads\r
+\r
+Combine with the "Displaying pictures" hack above for a happy time.\r
+\r
+=head2 Links\r
+\r
+L<Contents|Maypole::Manual>,\r
+Next L<Flox|Maypole::Manual::Flox>,\r
+Previous L<The Beer Database, Twice|Maypole::Manual::Beer>\r
+\r
diff --git a/lib/Maypole/Manual/Inheritance.pod b/lib/Maypole/Manual/Inheritance.pod
new file mode 100644 (file)
index 0000000..2dd0eaf
--- /dev/null
@@ -0,0 +1,264 @@
+\r
+=head1 NAME\r
+\r
+Maypole::Manual::Inheritance - structure of a Maypole application\r
+\r
+=head1 DESCRIPTION\r
+\r
+Discusses the inheritance structure of a basic and a more advanced Maypole\r
+application.\r
+\r
+=head1 CONVENTIONS\r
+          \r
+=over 4\r
+\r
+=item inheritance\r
+\r
+        +\r
+        |\r
+    +--  --+\r
+        |\r
+       |\r
+       +\r
+       \r
+=item notes\r
+\r
+    target *-------- note about the target\r
+\r
+=item association\r
+\r
+    source ------> target\r
+\r
+=back\r
+\r
+=head1 A standard Maypole application\r
+\r
+A minimal Maypole application (such as the Beer database) consists of a \r
+custom driver class (BeerDB.pm), a set of auto-generated model classes, and a \r
+view class: \r
+\r
+                  THE DRIVER\r
+                                          +------- init() is a factory method,\r
+                   1      Maypole         |           it sets up the view\r
+   Maypole::Config <----- config();       |              classes\r
+   model();               init(); *-------+                           THE VIEW\r
+    |                     view_object(); -------+              \r
+    |    +--------------* setup();              |      Maypole::View::Base\r
+    |    |                   +                  |              +\r
+    |    |                   |                  |     1        |\r
+    |    |               Apache::MVC *-----+    +-----> Maypole::View::TT\r
+    |    |                   +             |             (or another view class)\r
+    |    |                   |             |\r
+    |    |                PLUGINS          |\r
+    |    |                   +             |\r
+    |    |                   |             +----- or CGI::Maypole\r
+    |    |                BeerDB                   or MasonX:::Maypole\r
+    |    |\r
+    |   setup() is a factory method,\r
+    |     it sets up the model\r
+    |         classes\r
+    |\r
+    |                                 THE MODEL\r
+    |\r
+    |  Maypole::Model::Base   Class::DBI\r
+    |             +             +\r
+    |             |             |                             \r
+    +-------> Maypole::Model::CDBI                            \r
+                      +                                       \r
+                      |                                       \r
+           +------------+--------+-------+---------+\r
+           |            |        |       |         |\r
+       BeerDB::Pub      |   BeerDB::Beer | BeerDB::Brewery\r
+       beers();         |   pubs();      | beers();\r
+                        |   brewery();   |\r
+                        |   style();     |\r
+          BeerDB::Handpump               |\r
+          pub();                      BeerDB::Style\r
+          beer();                     beers();\r
+\r
+\r
+=head2 What about Maypole::Application - loading plugins\r
+\r
+The main job of L<Maypole::Application> is to insert the plugins into the \r
+hierarchy. It ensures that L<BeerDB> inherits from the first plugin, which \r
+inherits from the next, etc., until the last plugin inherits from the frontend. \r
+\r
+It is also the responsibility of L<Maypole::Application> to decide which \r
+frontend to use. \r
+\r
+From Maypole 2.11, L<Maypole::Application> makes no appearance in the\r
+inheritance structure of a Maypole application. (In prior versions,\r
+L<Maypole::Application> would make itself inherit the plugins, and then insert\r
+itself in the hierarchy, but this was unnecessary).\r
+\r
+The order of inheritance is the same as the order in which plugins are supplied \r
+in the C<use Maypole::Application> statement. \r
+\r
+=head2 Who builds the model?\r
+\r
+First, remember we are talking about the standard, unmodified Maypole here. \r
+It is possible, and common, to override some or all of this stage and build a \r
+customised model.\r
+\r
+The standard model is built in 3 stages. \r
+\r
+First, C<Maypole::setup> calls C<setup_database> on the Maypole model class, in\r
+this case L<Maypole::Model::CDBI>. C<setup_database> then uses\r
+L<Class::DBI::Loader> to autogenerate individual L<Class::DBI> classes for each\r
+of the tables in the database (C<BeerDB::Beer>, C<BeerDB::Pub> etc).\r
+\r
+Next, C<Maypole::setup> B<unshifts> L<Maypole::Model::CDBI> onto the C<@ISA> \r
+array of each of these classes. \r
+\r
+Finally, the relationships among these tables are set up. Either do this\r
+manually, perhaps with the help of L<Class::DBI::Relationship>, or use\r
+L<Maypole::Plugin::Relationship>. In the latter case, you need to set up the\r
+relationships configuration before calling C<setup()>.\r
+\r
+\r
+=head1 An advanced Maypole application\r
+\r
+We'll call it C<BeerDB2>.\r
+\r
+Maypole is a framework, and you can replace different bits as you wish. So what \r
+follows is one example of good practice, other people may do things differently. \r
+\r
+We assume this application is being built from the ground up, but it will often\r
+be straightforward to adapt an existing L<Class::DBI> application to this\r
+general model.\r
+\r
+The main idea is that the autogenerated Maypole model is used as a layer on top\r
+of a separate L<Class::DBI> model. I am going to refer to this model as the\r
+'Offline' model, and the Maypole classes as the 'Maypole' model. The idea is\r
+that the Offline model can (potentially or in actuality) be used as part of\r
+another application, perhaps a command line program or a cron script, whatever.\r
+The Offline model does not know about the Maypole model, whereas the Maypole\r
+model does know about the Offline model.\r
+\r
+Let's call the offline model C<OfflineBeer>. As a traditional L<Class::DBI>\r
+application, individual table classes in this model will inherit from a common\r
+base (C<OfflineBeer>), which inherits from L<Class::DBI>).\r
+\r
+One advantage of this approach is that you can still use Maypole's autogenerated\r
+model. Another is that you do not mix online and offline code in the same\r
+packages.\r
+\r
+=head2 Building it\r
+\r
+Build a driver in a similar way as for the basic app, calling C<setup()> after\r
+setting up all the configuration. \r
+\r
+It is a good habit to use a custom Maypole model class for each application, as\r
+it's a likely first target for customisation. Start it like this:\r
+\r
+    package BeerDB2::Maypole::Model;\r
+    use strict;\r
+    use warnings;\r
+    use base 'Maypole::Model::CDBI';\r
+    1;\r
+    \r
+You can add methods which should be shared by all table classes to this package \r
+as and when required.\r
+    \r
+Configure it like this, before the C<setup()> call in the driver class:\r
+\r
+    # in package BeerDB2\r
+    __PACKAGE__->config->model('BeerDB2::Maypole::Model');\r
+    __PACKAGE__->setup;\r
+\r
+The C<setup()> call will ensure your custom model is loaded via C<require>.\r
+\r
+B<Note>: by default, this will create Maypole/CDBI classes for all the tables in\r
+the database. You can control this by passing options for L<Class::DBI::Loader>\r
+in the call to C<setup()>.\r
+\r
+For each class in the model, you need to create a separate file. So for\r
+C<BeerDB2::Beer>, you would write:\r
+\r
+    package BeerDB2::Beer;\r
+    use strict;\r
+    use warnings;\r
+    use base 'OfflineBeer::Beer';\r
+    1;\r
+    \r
+This package will be loaded automatically during C<setup()>, and\r
+C<BeerDB2::Maypole::Model> is B<unshifted> onto it's C<@ISA>. \r
+\r
+Configure relationships either in the individual C<OfflineBeer::*> classes, or\r
+else all together in C<OfflineBeer> itself i.e. not in the Maypole model. This \r
+way, you only define the relationships in one place.\r
+\r
+The resulting model looks like this:\r
+\r
+                                       Class::DBI\r
+    MAYPOLE 'MODEL'                       |\r
+                                          |\r
+   Maypole::Model::Base                   |\r
+           +                              |\r
+           |       +----------------------+-----------------+\r
+           |       |                                        |\r
+           |       |                                        |\r
+     Maypole::Model::CDBI                                   |     OFFLINE\r
+             +                                              |        MODEL\r
+             |                                              |\r
+     BeerDB2::Maypole::Model                           OfflineBeer\r
+       +                                                    +\r
+       |                                                    |\r
+       |                                                    |\r
+       +--- BeerDB2::Pub --------+ OfflineBeer::Pub --------+\r
+       |                           beers();                 |\r
+       |                                                    |\r
+       |                           OfflineBeer::Handpump ---+\r
+       |                           beer();                  |\r
+       |                           pub();                   |\r
+       |                                                    |\r
+       +--- BeerDB2::Beer -------+ OfflineBeer::Beer -------+\r
+       |                           pubs();                  |\r
+       |                           brewery();               |\r
+       |                           style();                 |\r
+       |                                                    |\r
+       +--- BeerDB2::Style ------+ OfflineBeer::Style ------+\r
+       |                           beers();                 |\r
+       |                                                    |\r
+       +--- BeerDB2::Brewery ----+ OfflineBeer::Brewery ----+\r
+                                   beers();\r
+\r
+\r
+\r
+=head3 Features\r
+\r
+Non-Maypole applications using the Offline model are completely isolated from\r
+the Maypole application, and need not know it exists at all.\r
+\r
+Methods defined in the Maypole table classes, override methods defined in the\r
+Offline table classes, because C<BeerDB2::Maypole::Model> was unshifted onto the\r
+beginning of each Maypole table class's C<@ISA>. Perl's depth first,\r
+left-to-right method lookup from e.g. C<BeerDB2::Beer> starts in\r
+C<BeerDB2::Beer>, then C<BeerDB2::Maypole::Model>, C<Maypole::Model::CDBI>,\r
+C<Maypole::Model::Base>, and C<Class::DBI>, before moving on to\r
+C<OfflineBeer::Beer> and finally C<OfflineBeer>.\r
+\r
+Methods defined in the Maypole model base class (C<BeerDB2::Maypole::Model>),\r
+override methods in the individual Offline table classes, and in the Offline\r
+model base class (C<Offline>). \r
+\r
+Relationships defined in the Offline classes are inherited by the Maypole model.\r
+\r
+The Maypole model has full access to the underlying Offline model. \r
+\r
+=head3 Theory \r
+\r
+This layout illustrates more clearly why the Maypole model may be thought of as\r
+part of the controller, rather than part of the model of MVC. Its function is to \r
+mediate web requests, translating them into method calls on the Offline model, \r
+munging the results, and returning them via the Maypole request object. \r
+\r
+Another way of thinking about it is that Maypole implements a two-layer\r
+controller. The first layer translates a raw request into a single method call\r
+on the Maypole model layer, which then translates that call into one or more\r
+calls on the underlying model.\r
+\r
+Whatever label you prefer to use, this approach provides for clear separation of\r
+concerns between the underlying model and the web/user interface, and that's\r
+what it's all about.\r
+          \r
diff --git a/lib/Maypole/Manual/Install.pod b/lib/Maypole/Manual/Install.pod
new file mode 100644 (file)
index 0000000..f2e85a7
--- /dev/null
@@ -0,0 +1,47 @@
+\r
+=head1 NAME\r
+\r
+Maypole::Manual::Install - installing Maypole\r
+\r
+=head1 Installing Maypole\r
+\r
+The first thing you're going to need to do to get Maypole running is to\r
+install it. Maypole needs an absolute shedload of Perl modules from CPAN\r
+to do its job. I am unrepentant about this. Maypole does a lot of work,\r
+so that you don't have to. This is called code re-use, and if we're\r
+serious about code re-use, then Maypole should be re-using as much code\r
+as possible in terms of Perl modules. In another sense, this gives the\r
+impression that Maypole doesn't actually do all that much itself,\r
+because all it's doing is gluing together already-existing code. Well,\r
+welcome to code re-use.\r
+\r
+The downside of code re-use is, of course, that you then have to install\r
+a shedload of Perl modules from CPAN. If you're using OpenBSD or\r
+FreeBSD, the wonderful ports system will be your friend. There's a\r
+Maypole port in C<p5-Maypole>. Just type C<make install>.\r
+\r
+Debian users, hang in there. There's a package coming.\r
+\r
+For other Unices, the L<CPANPLUS> or C<CPAN> modules will help with\r
+this. If you don't have C<CPANPLUS> installed, my recommendation is to\r
+use C<perl -MCPAN -e install CPANPLUS> to install it and then throw\r
+C<CPAN.pm> away. In any case, one of these two should get all that\r
+Maypole needs:\r
+\r
+    % perl -MCPANPLUS -e 'install Maypole'\r
+    % perl -MCPAN -e 'install Maypole'\r
+\r
+I don't know if Maypole works on Windows. I'm not sure I care.\r
+\r
+You're also going to need a database server and a web server. For\r
+databases, I recommend SQLite (if you install the C<DBD::SQLite> module,\r
+you get the SQLite library for free) for prototyping and mysql for\r
+production; heavier duty users should use Postgresql or Oracle - Maypole\r
+should be happy with them all. Maypole is happiest when running under\r
+Apache C<mod_perl>, with the C<Apache::Request> module installed, but as\r
+I said, it is a blank slate, and everything is customizable. There is a\r
+C<CGI::Maypole> frontend available to run as a standalone CGI script.\r
+\r
+As well as the documentation embedded in the Perl modules the distribution\r
+also includes the manual, of which this is a part. You can access it using the\r
+perldoc command, the man command, or by browsing CPAN.\r
index f0582c63225344c0b16b7d1c42f948be75cba65f..7389b60875798bdb0807142c905258e0c4d49791 100644 (file)
@@ -8,11 +8,12 @@ This version written for Maypole 2.10
 
 =head1 LOADING PLUGINS
 
 
 =head1 LOADING PLUGINS
 
-Plugins occupy the C<Maypole::Plugin::*> namespace on CPAN. At time of writing, there are 16 plugin modules
-available - see http://search.cpan.org/search?query=Maypole%3A%3APlugin&mode=module
+Plugins occupy the C<Maypole::Plugin::*> namespace on CPAN. At time of writing, 
+there are 16 plugin modules available - see 
+http://search.cpan.org/search?query=Maypole%3A%3APlugin&mode=module
 
 
-Plugins are loaded into a Maypole application by L<Maypole::Application>. For instance, to add L<HTML::QuickTable>
-support to the BeerDB example application:
+Plugins are loaded into a Maypole application by L<Maypole::Application>. For 
+instance, to add L<HTML::QuickTable> support to the BeerDB example application:
 
    package BeerDB;
    use strict;
 
    package BeerDB;
    use strict;
@@ -22,11 +23,12 @@ support to the BeerDB example application:
 
 Note that the leading C<Maypole::Plugin::*> is omitted.
 
 
 Note that the leading C<Maypole::Plugin::*> is omitted.
 
-For some plugins, that's it. You probably have a bunch of new methods available on your Maypole request objects -
-see the documentation for the plugin.
+For some plugins, that's it. You probably have a bunch of new methods available 
+on your Maypole request objects - see the documentation for the plugin.
 
 For others, you will need to set configuration variables or customise other
 
 For others, you will need to set configuration variables or customise other
-parts of the application. For instance, to add sessions to your application, you can use L<Maypole::Plugin::Session>:
+parts of the application. For instance, to add sessions to your application, you 
+can use L<Maypole::Plugin::Session>:
 
    package BeerDB;
    use strict;
 
    package BeerDB;
    use strict;
@@ -34,7 +36,9 @@ parts of the application. For instance, to add sessions to your application, you
 
    use Maypole::Application( 'Session' );
 
 
    use Maypole::Application( 'Session' );
 
-That's all, if you're willing to stick with the defaults (L<Apache::Session::File> backend, session and lock files in C</tmp/sessions> and C</tmp/sessionlock>). Otherwise, you need to supply some configuration:
+That's all, if you're willing to stick with the defaults 
+(L<Apache::Session::File> backend, session and lock files in C</tmp/sessions> 
+and C</tmp/sessionlock>). Otherwise, you need to supply some configuration:
 
    __PACKAGE__->config->session( { class => "Apache::Session::Flex",
                                    args  => {
 
    __PACKAGE__->config->session( { class => "Apache::Session::Flex",
                                    args  => {
@@ -45,23 +49,28 @@ That's all, if you're willing to stick with the defaults (L<Apache::Session::Fil
                                        }
                                    } );
 
                                        }
                                    } );
 
-The plugin module is responsible for adding slots to L<Maypole::Config>, in this case, the C<session> accessor.
+The plugin module is responsible for adding slots to L<Maypole::Config>, in this 
+case, the C<session> accessor.
 
 =head1 WRITING PLUGINS
 
 =head2 Modifying the Maypole request object
 
 
 =head1 WRITING PLUGINS
 
 =head2 Modifying the Maypole request object
 
-Plugins are inserted into the C<@ISA> of the Maypole request object. So method calls on the request object will
-first search the plugin classes, before looking in L<Maypole>. Methods defined in the plugin are
-therefore directly available on the request. That also goes for methods inherited by the plugin. I'm not aware
-of any plugins that currently inherit from another package, but there's no reason not to.
+Plugins are inserted into the C<@ISA> of the Maypole request object. So method 
+calls on the request object will first search the plugin classes, before looking 
+in L<Maypole>. Methods defined in the plugin are therefore directly available on 
+the request. That also goes for methods inherited by the plugin. I'm not aware
+of any plugins that currently inherit from another package, but there's no 
+reason not to.
 
 
-Note that if you need simple accessor methods on the request, you can add them by saying
+Note that if you need simple accessor methods on the request, you can add them 
+by saying
 
    Maypole->mk_accessors( qw/ fee fi fo / );
 
 
    Maypole->mk_accessors( qw/ fee fi fo / );
 
-at the start of your plugin. Under mod_perl, you've just added these accessors to B<all> Maypole applications
-on the server, even ones that do not use this plugin. You could instead make the call inside the C<setup> method:
+at the start of your plugin. Under mod_perl, you've just added these accessors 
+to B<all> Maypole applications on the server, even ones that do not use this 
+plugin. You could instead make the call inside the C<setup> method:
 
    $r->mk_accessors( qw/ fee fi fo / );
 
 
    $r->mk_accessors( qw/ fee fi fo / );
 
@@ -69,20 +78,24 @@ Now the accessors are only added to applications that use this plugin.
 
 =head2 Initialisation with C<setup>
 
 
 =head2 Initialisation with C<setup>
 
-After loading plugins via L<Maypole::Application>, setting configuration variables in calls to
-C<< __PACKAGE__->config->foo( 'bar' ) >>, and optionally defining custom request methods, your
-application should call its C<setup> method, generally including arguments for the database connection:
+After loading plugins via L<Maypole::Application>, setting configuration 
+variables in calls to C<< __PACKAGE__->config->foo( 'bar' ) >>, and optionally 
+defining custom request methods, your application should call its C<setup> 
+method, generally including arguments for the database connection:
 
    __PACKAGE__->setup( $dsn, $user, $pass, @more_args );
 
 
    __PACKAGE__->setup( $dsn, $user, $pass, @more_args );
 
-All of these arguments will be passed to the C<setup_database> method of the model class.
+All of these arguments will be passed to the C<setup_database> method of the 
+model class.
 
 
-C<Maypole::setup()> is responsible for loading the model class, calling the C<setup_database> method
-on the model class, and making each table class in the application inherit from the model. It is therefore
-recommended that you call C<setup> B<after> setting up all your configuration options.
+C<Maypole::setup()> is responsible for loading the model class, calling the 
+C<setup_database> method on the model class, and making each table class in the 
+application inherit from the model. It is therefore recommended that you call 
+C<setup> B<after> setting up all your configuration options.
 
 
-Plugins can intercept the call to C<setup> to carry out their own initialisation, as long as they propagate
-the call up through the hierarchy. A common idiom for this is:
+Plugins can intercept the call to C<setup> to carry out their own 
+initialisation, as long as they propagate the call up through the hierarchy. A 
+common idiom for this is:
 
    Maypole::Plugin::Foo;
    use strict;
 
    Maypole::Plugin::Foo;
    use strict;
@@ -102,20 +115,23 @@ the call up through the hierarchy. A common idiom for this is:
        # do something with $option
    }
 
        # do something with $option
    }
 
-L<NEXT> is a replacement for the built-in C<SUPER> syntax. C<SUPER> dispatches a call to the superclass
-of the current package - B<but> it determines the superclass at compile time. At that time, the superclass
-is something like C<main::>. L<NEXT> does the superclass lookup at runtime, after L<Maypole::Application> has
-inserted the plugin into the request class's inheritance chain.
+L<NEXT> is a replacement for the built-in C<SUPER> syntax. C<SUPER> dispatches a 
+call to the superclass of the current package - B<but> it determines the 
+superclass at compile time. At that time, the superclass is something like 
+C<main::>. L<NEXT> does the superclass lookup at runtime, after 
+L<Maypole::Application> has inserted the plugin into the request class's 
+inheritance chain.
 
 
-The C<DISTINCT> modifier ensures each plugin's C<setup> method is only called once, and protects against
-diamond inheritance. This may or may not be an issue in your app - and if you always use the C<DISTINCT>
-syntax, it won't be.
+The C<DISTINCT> modifier ensures each plugin's C<setup> method is only called 
+once, and protects against diamond inheritance. This may or may not be an issue 
+in your app - and if you always use the C<DISTINCT> syntax, it won't be.
 
 
-Notice that the C<setup> call is re-dispatched before running the plugin's own initialisation code. This
-allows C<Maypole::setup()> to set up the database, model, and table classes, before your plugin starts tweaking
-things.
+Notice that the C<setup> call is re-dispatched before running the plugin's own 
+initialisation code. This allows C<Maypole::setup()> to set up the database, 
+model, and table classes, before your plugin starts tweaking things.
 
 
-You can use the C<setup> method to load modules into the request class namespace. L<Maypole::Plugin::I18N> has:
+You can use the C<setup> method to load modules into the request class 
+namespace. L<Maypole::Plugin::I18N> has:
 
    sub setup {
        my $r = shift;
 
    sub setup {
        my $r = shift;
@@ -127,35 +143,39 @@ You can use the C<setup> method to load modules into the request class namespace
          Path   => $r->config->lexicon;
 }
 
          Path   => $r->config->lexicon;
 }
 
-Now the application namespace has a C<_loc> function (exported by L<Locale::Maketext::Simple>), (plus C<lang> and
-C<maketext> methods inherited from L<Maypole::Plugin::I18N>).
+Now the application namespace has a C<_loc> function (exported by 
+L<Locale::Maketext::Simple>), (plus C<lang> and C<maketext> methods inherited 
+from L<Maypole::Plugin::I18N>).
 
 =head3 More initialisation with C<init>
 
 L<Maypole> also defines an C<init> method. It
 
 =head3 More initialisation with C<init>
 
 L<Maypole> also defines an C<init> method. It
-pulls the name of the view class from the config, loads it, instantiates an object in the view class, and
-sets this in the C<view_object> config slot.
+pulls the name of the view class from the config, loads it, instantiates an 
+object in the view class, and sets this in the C<view_object> config slot.
 
 In CGI applications, C<init> is called at the start of every request.
 
 
 In CGI applications, C<init> is called at the start of every request.
 
-Under mod_perl, this method will only ever be called once per server child, at the start of the first request after
-server startup. If instead, you call this method in your application module (after the call to C<setup>),
-then the code loaded by this call will be shared by all child servers.
+Under mod_perl, this method will only ever be called once per server child, at 
+the start of the first request after server startup. If instead, you call this 
+method in your application module (after the call to C<setup>), then the code 
+loaded by this call will be shared by all child servers.
 
 See B<Hacking the view> for a plugin that uses C<init>.
 
 =head2 Adding configuration
 
 
 See B<Hacking the view> for a plugin that uses C<init>.
 
 =head2 Adding configuration
 
-The configuration object can be retrieved from the Maypole request object (C<< $r->config >>) or as a class method
-on the application (e.g. C<< BeerDB->config >>).
+The configuration object can be retrieved from the Maypole request object 
+(C<< $r->config >>) or as a class method on the application (e.g. 
+C<< BeerDB->config >>).
 
 
-If your plugin needs some custom configuration settings, you can add methods to the config object by
-saying
+If your plugin needs some custom configuration settings, you can add methods to 
+the config object by saying
 
    Maypole::Config->mk_accessors( qw/ foo bar baz / );
 
 
    Maypole::Config->mk_accessors( qw/ foo bar baz / );
 
-at the start of your plugin. In the application, after the C<Maypole::Application> call, these methods will
-be available on the config object.
+at the start of your plugin. In the application, after the 
+C<Maypole::Application> call, these methods will be available on the config 
+object.
 
 =head2 Modifying the Maypole model
 
 
 =head2 Modifying the Maypole model
 
@@ -163,28 +183,33 @@ be available on the config object.
 
 =item Replacing the model
 
 
 =item Replacing the model
 
-To load a different model, set C<< __PACKAGE__->config->model( 'Custom::Model' ) >> in the application
-before calling C<setup>. You could instead set C<< $r->config->model >> before re-dispatching the C<setup> call,
-but this is going to confuse and annoy your users.
+To load a different model, set 
+C<< __PACKAGE__->config->model( 'Custom::Model' ) >> in the application
+before calling C<setup>. You could instead set C<< $r->config->model >> before 
+re-dispatching the C<setup> call, but this is going to confuse and annoy your 
+users.
 
 =item Hacking the model
 
 B<CAVEAT>: the way I do this just seems dirty, so there must be a Better Way.
 
 
 =item Hacking the model
 
 B<CAVEAT>: the way I do this just seems dirty, so there must be a Better Way.
 
-L<Maypole::Plugin::FormBuilder> (part of the L<Maypole::FormBuilder> distribution), in its C<setup> method,
-loads a custom pager class into the model by saying
+L<Maypole::Plugin::FormBuilder> (part of the L<Maypole::FormBuilder> 
+distribution), in its C<setup> method, loads a custom pager class into the model 
+by saying
 
    eval "package $model; use $pager";
 
 
    eval "package $model; use $pager";
 
-Yuk. Note that under mod_perl, you have just forced B<every> application using C<$model> to also use C<$pager>.
+Yuk. Note that under mod_perl, you have just forced B<every> application using 
+C<$model> to also use C<$pager>.
 
 
-C<Maypole::Plugin::AutoUntaint::setup()> loads an extra method into the model by saying
+C<Maypole::Plugin::AutoUntaint::setup()> loads an extra method into the model by 
+saying
 
    no strict 'refs';
    *{"$model\::auto_untaint"} = \&Class::DBI::Plugin::AutoUntaint::auto_untaint;
 
 
    no strict 'refs';
    *{"$model\::auto_untaint"} = \&Class::DBI::Plugin::AutoUntaint::auto_untaint;
 
-Yuk again. And again, under mod_perl, now every application using C<$model> has an C<auto_untaint> method
-added to its model.
+Yuk again. And again, under mod_perl, now every application using C<$model> has 
+an C<auto_untaint> method added to its model.
 
 Same plugin, next line has
 
 
 Same plugin, next line has
 
@@ -193,8 +218,6 @@ Same plugin, next line has
 Same yuk, same mod_perl caveat.
 
 
 Same yuk, same mod_perl caveat.
 
 
-
-
 =back
 
 
 =back
 
 
@@ -208,11 +231,13 @@ Again, just specify a different view in the application configuration.
 
 =item Hacking the view
 
 
 =item Hacking the view
 
-L<Maypole::Plugin::FormBuilder> intercepts the C<init> call to override the C<vars> method in the view class.
-First it re-dispatches the C<init> call, which will set up either a default view class and object, or those
-configured in the application. Then it builds a new view class on-the-fly, and makes this new class inherit from
-L<Maypole::FormBuilder::View> and from the original view class. Finally it replaces the C<view> and C<view_object>
-in the application's config object.
+L<Maypole::Plugin::FormBuilder> intercepts the C<init> call to override the 
+C<vars> method in the view class. First it re-dispatches the C<init> call, which 
+will set up either a default view class and object, or those configured in the 
+application. Then it builds a new view class on-the-fly, and makes this new 
+class inherit from L<Maypole::FormBuilder::View> and from the original view 
+class. Finally it replaces the C<view> and C<view_object> in the application's 
+config object.
 
    sub init
    {
 
    sub init
    {
diff --git a/lib/Maypole/Manual/Request.pod b/lib/Maypole/Manual/Request.pod
deleted file mode 100644 (file)
index 64ee38b..0000000
+++ /dev/null
@@ -1,840 +0,0 @@
-=head1 NAME
-
-Maypole::Manual::Request - Maypole Request Hacking Cookbook
-
-=head1 DESCRIPTION
-
-Hacks; design patterns; recipes: call it what you like, this chapter is a
-developing collection of techniques which can be slotted in to Maypole
-applications to solve common problems or make the development process easier.
-
-As Maypole developers, we don't necessarily know the "best practice" for
-developing Maypole applications ourselves, in the same way that Larry Wall
-didn't know all about the best Perl programming style as soon as he wrote
-Perl. These techniques are what we're using at the moment, but they may
-be refined, modularized, or rendered irrelevant over time. But they've
-certainly saved us a bunch of hours work.
-
-=head2 Frontend hacks
-
-These hacks deal with changing the way Maypole relates to the outside world;
-alternate front-ends to the Apache and CGI interfaces, or subclassing chunks
-of the front-end modules to alter Maypole's behaviour in particular ways.
-
-=head3 Separate model class modules
-
-You want to put all the C<BeerDB::Beer> routines in a separate module,
-so you say:
-
-    package BeerDB::Beer;
-    BeerDB::Beer->has_a(brewery => "BeerDB::Brewery");
-    sub foo :Exported {}
-
-And in F<BeerDB.pm>, you put:
-
-    use BeerDB::Beer;
-
-It doesn't work.
-
-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
-
-    BeerDB->setup("...")
-
-which does its stuff at runtime. There are two ways around this; you can
-either move the C<setup> call to compile time, like so:
-
-    BEGIN { BeerDB->setup("...") }
-
-or move the module loading to run-time (my preferred solution):
-
-    BeerDB->setup("...");
-    BeerDB::Beer->require;
-
-=head3 Redirecting to SSL for sensitive information
-
-You have a website with forms that people will be entering sensitive information into,
-such as credit cards or login details. You want to make sure that they aren't sent
-in plain text but over SSL instead.
-
-B<Solution>
-
-The solution is a bit tricky for 2 reasons :
-
-Firstly -- Many browsers and web clients will change a redirected 
-POST request into a GET request (which displays all that sensitive information in the
-browser, or access logs and possibly elsewhere) and/or drops the values on the floor.
-
-Secondly -- If somebody has sent that sensitive information in plain text already, then
-sending it again over SSL won't solve the problem.
-
-Redirecting a request is actually rather simple :
-
-$r->redirect_request('https://www.example.com/path'); # perldoc Maypole for API
-
-.. as is checking the protocol :
-
-$r->get_protocol(); # returns 'http' or 'https'
-You should check that the action that generates the form that people will enter
-the sensitive information into is https and redirect if not.
-
-You should also check that no information is lost when redirecting, possibly by 
-storing it in a session and retrieving it later - see Maypole::Plugin::Session
-
-=head3 Debugging with the command line
-
-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 L<Maypole::CLI> module to go directly from a URL to
-standard output, bypassing Apache and the network altogether.
-
-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:
-
-    perl -MMaypole::CLI=Application -e1 'http://your.server/path/table/action'
-
-For example:
-
-    perl -MMaypole::CLI=BeerDB -e1 'http://localhost/beerdb/beer/view/1?o2=desc'
-
-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.
-
-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
-of the request object. Normally it does this just by splitting the path
-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
-like so:
-
-    sub parse_path {
-        my $r = shift;
-        $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
-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 the L<iBuySpy Portal|Maypole::Manual::BuySpy> for another
-example of custom URL processing.
-
-=head3 Maypole for mobile devices
-
-You want Maypole to use different templates to display on particular
-browsers.
-
-B<Solution>: There are several ways to do this, but here's the neatest
-we've found. Maypole chooses where to get its templates either by
-looking at the C<template_root> config parameter or, if this is not
-given, calling the C<get_template_root> method to ask the front-end to
-try to work it out. We can give the front-end a little bit of help, by
-putting this method in our driver class:
-
-    sub get_template_root {
-        my $r = shift;
-        my $browser = $r->headers_in->get('User-Agent');
-        if ($browser =~ /mobile|palm|nokia/i) {
-            "/home/myapp/templates/mobile";
-        } else {
-            "/home/myapp/templates/desktop";
-        }
-    }
-
-(Maybe there's a better way to detect a mobile browser, but you get the
-idea.)
-
-=head2 Content display hacks
-
-These hacks deal primarily with the presentation of data to the user,
-modifying the F<view> template or changing the way that the results of
-particular actions are displayed.
-
-=head3 Null Action
-
-You need an "action" which doesn't really do anything, but just formats
-up a template.
-
-B<Solution>: There are two ways to do this, depending on what precisely
-you need. If you just need to display a template, C<Apache::Template>
-style, with no Maypole objects in it, then you don't need to write any
-code; just create your template, and it will be available in the usual
-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 the
-L<templates and actions|Maypole::Manual::StandardTemplates/"C<view> and C<edit>">
-chapter:
-
-    sub my_view :Exported { }
-
-=head3 Template Switcheroo
-
-An action doesn't have any data of its own to display, but needs to display
-B<something>.
-
-B<Solution>: This is an B<extremely> common hack. You've just issued an
-action like C<beer/do_edit>, which updates the database. You don't want
-to display a page that says "Record updated" or similar. Lesser
-application servers would issue a redirect to have the browser request
-C</beer/view/I<id>> instead, but we can actually modify the Maypole
-request on the fly and, after doing the update, pretend that we were
-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|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");
-    }
-
-This hack is so common that it's expected that there'll be a neater
-way of doing this in the future.
-
-=head3 XSLT
-
-Here's a hack I've used a number of times. You want to store structured
-data in a database and to abstract out its display.
-
-B<Solution>: You have your data as XML, because handling big chunks of
-XML is a solved problem. Build your database schema as usual around the
-important elements that you want to be able to search and browse on. For
-instance, I have an XML format for songs which has a header section of
-the key, title and so on, plus another section for the lyrics and
-chords:
-
-    <song>
-        <header>
-            <title>Layla</title>
-            <artist>Derek and the Dominos</artist>
-            <key>Dm</key>
-        </header>
-        <lyrics>
-          <verse>...</verse>
-          <chorus>
-            <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 L<XML::TreeBuilder> will handle
-the XML parsing for us:
-
-    use Songbook;
-    use XML::TreeBuilder;
-    my $t = XML::TreeBuilder->new;
-    $t->parse_file("songs.xml");
-
-    for my $song ($t->find("song")) {
-        my ($key) = $song->find("key"); $key &&= $key->as_text;
-        my ($title) = $song->find("title"); $title = $title->as_text;
-        my ($artist) = $song->find("artist"); $artist = $artist->as_text;
-        my ($first_line) = $song->find("line");
-        $first_line = join "", grep { !ref } $first_line->content_list;
-        $first_line =~ s/[,\.\?!]\s*$//;
-        Songbook::Song->find_or_create({
-            title => $title,
-            first_line => $first_line,
-            song_key => Songbook::SongKey->find_or_create({name => $key}),
-            artist => Songbook::Artist->find_or_create({name => $artist}),
-            xml => $song->as_XML
-        });
-    }
-
-Now we need to set up the custom display for each song; thankfully, with
-the L<Template::Plugin::XSLT> module, this is as simple as putting the
-following into F<templates/song/view>:
-
-    [%
-        USE transform = XSLT("song.xsl");
-        song.xml | $transform
-    %]
-
-We essentially pipe the XML for the selected song through to an XSL
-transformation, and this will fill out all the HTML we need. Job done.
-
-=head3 Displaying pictures
-
-You want to serve a picture, a Word document, or something else which
-doesn't have a content type of C<text/html>, out of your database.
-
-B<Solution>: Fill the content and content-type yourself.
-
-Here's a subroutine which displays the C<photo> for either a specified
-user or the currently logged in user. We set the C<output> slot of the
-Maypole request object: if this is done then the view class is not called
-upon to process a template, since we already have some output to display.
-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);
-    }
-
-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>.
-
-=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|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
-C<person@someco.com>, we want to know if they're in our network of
-friends or not and mark the message appropriately. We'll do this by
-having a web agent (say, L<WWW::Mechanize> or L<LWP::UserAgent>) request
-a URL of the form
-C<http://flox.simon-cozens.org/user/relationship_by_email/person%40someco.com>.
-Naturally, they'll need to present the appropriate cookie just like a
-normal browser, but that's a solved problem. We're just interested in
-the REST request.
-
-The request will return a single integer status code: 0 if they're not
-in the system at all, 1 if they're in the system, and 2 if they're our
-friend.
-
-All we need to do to implement this is provide the C<relationship_by_email>
-action, and use it to fill in the output in the same way as we did when
-displaying a picture. Since C<person%40someco.com> is not the ID of a
-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 $user;
-        unless (($user) = Flox::User->search(email => $email)) {
-            $r->content("0\n"); return;
-        }
-
-        if ($r->user->is_friend($user)) { $r->contenti("2\n"); return; };
-        $r->content("1\n"); return;
-    }
-
-=head3 Component-based Pages
-
-You're designing something like a portal site which has a number of
-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 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") %]
-    </DIV>
-
-    <DIV class="links">
-    [% request.component("/links/list_comp") %]
-    </DIV>
-
-then the results of calling the C</news/latest_comp> action and template
-will be inserted in the C<latestnews> DIV, and the results of calling
-C</links/list_comp> will be placed in the C<links> DIV. Naturally, you're
-responsible for exporting actions and creating templates which return 
-fragments of HTML suitable for inserting into the appropriate locations.
-
-Alternatively, if you've already got all the objects you need, you can
-probably just C<[% PROCESS %]> the templates directly.
-
-=head3 Bailing out with an error
-
-Maypole's error handling sucks. Something really bad has happened to the
-current request, and you want to stop processing now and tell the user about
-it.
-
-B<Solution>: Maypole's error handling sucks because you haven't written it
-yet. Maypole doesn't know what you want to do with an error, so it doesn't
-guess. One common thing to do is to display a template with an error message
-in it somewhere.
-
-Put this in your driver class:
-
-    sub error { 
-        my ($r, $message) = @_;
-        $r->template("error");
-        $r->template_args->{error} = $message;
-        return OK;
-    }
-
-And then have a F<custom/error> template like so:
-
-    [% PROCESS header %]
-    <H2> There was some kind of error... </H2>
-    <P>
-    I'm sorry, something went so badly wrong, we couldn't recover. This
-    may help:
-    </P>
-    <DIV CLASS="messages"> [% error %] </DIV>
-
-Now in your actions you can say things like this:
-
-    if (1 == 0) { return $r->error("Sky fell!") }
-
-This essentially uses the template switcheroo hack to always display the
-error template, while populating the template with an C<error> parameter.
-Since you C<return $r-E<gt>error>, this will terminate the processing
-of the current action.
-
-The really, really neat thing about this hack is that since C<error>
-returns C<OK>, you can even use it in your C<authenticate> routine:
-
-    sub authenticate {
-        my ($self, $r) = @_;
-        $r->get_user;
-        return $r->error("You do not exist. Go away.")
-            if $r->user and $r->user->status ne "real";
-        ...
-    }
-
-This will bail out processing the authentication, the model class, and
-everything, and just skip to displaying the error message. 
-
-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";
-    }
-
-Now F<custom/messages> can contain:
-
-    [% IF messages %]
-    <DIV class="messages">
-    <UL>
-        [% FOR message = messages %]
-           <LI> [% message %] </LI>
-        [% END %]
-    </UL>
-    </DIV>
-    [% END %]
-
-And you can display messages to your user by adding C<PROCESS messages> at an
-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 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.
-
-=head3 Logging In
-
-You need the concept of a "current user".
-
-B<Solution>: Use something like
-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<user> and C<password> form parameters
-against a C<user> table in the database. Its behaviour is highly
-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. 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;
-        # Force them to the login page.
-        $r->template("login");
-        return OK;
-    }
-
-This will display the C<login> template, which should look something
-like this:
-
-    [% INCLUDE header %]
-
-      <h2> You need to log in </h2>
-
-    <DIV class="login">
-    [% IF login_error %]
-       <FONT COLOR="#FF0000"> [% login_error %] </FONT>
-    [% END %]
-      <FORM ACTION="[% base ; '/' ; request.path %]" METHOD="post">
-    Username: 
-        <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,
-C<get_user> will now return a valid user object, and the request will pass
-through unhindered to the original URL.
-
-=head3 Logging Out
-
-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>: 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 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
-particular tables. (Only allowing an admin to delete records in some
-tables, say, or not wanting people to get at the default set of methods
-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.
-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.
-
-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
-    }
-
-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 authenticate { # Ensure we can only create, reject or accept
-        my ($self, $r) = @_;
-        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
-
-These hacks particularly deal with issues related to the C<do_edit>
-built-in action.
-
-=head3 Limiting data for display
-
-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 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.
-
-Simply replace:
-
-    App::Table->untaint_columns(
-        text      => [qw/name description/]
-    );
-
-with:
-
-    App::Table->untaint_columns(
-        html      => [qw/name description/]
-    );
-
-And incoming HTML will be checked and cleaned before it is written to
-the database.
-
-=head3 Getting data from external sources
-
-You want to supplement the data received from a form with additional
-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 L<Net::Amazon> object to fill in some fields of a database row
-based on an ISBN:
-
-    use Net::Amazon;
-    my $amazon = Net::Amazon->new(token => 'YOUR_AMZN_TOKEN');
-
-    ...
-
-    sub create_from_isbn :Exported {
-       my ($self, $r) = @_;
-       my $book_info = $amazon->search(asin => $r->params->{isbn})->properties;
-
-       # Rewrite the CGI parameters with the ones from Amazon
-       $r->params->{title} = $book_info->title;
-       $r->params->{publisher} = $book_info->publisher;
-       $r->params->{year} = $book_info->year;
-       $r->params->{author} = join('and', $book_info->authors());
-       # And jump to the usual edit/create routine
-       $self->do_edit($r);
-    }
-
-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
-
-A user has submitted erroneous input to an edit/create form. You want to
-send him back to the form with errors displayed against the erroneous
-fields, but have the other fields maintain the values that the user
-submitted.
-
-B<Solution>: This is basically what the default C<edit> template and
-C<do_edit> method conspire to do, but it's worth highlighting again how
-they work. 
-
-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");
-
-This throws us back to the form, and so the form's template should take
-note of the errors, like so:
-
-     FOR col = classmetadata.columns;
-        NEXT IF col == "id";
-        "<P>";
-        "<B>"; classmetadata.colnames.$col; "</B>";
-        ": ";
-            item.to_field(col).as_HTML;
-        "</P>";
-        IF errors.$col;
-            "<FONT COLOR=\"#ff0000\">"; errors.$col; "</FONT>";
-        END;
-    END;
-
-If we're designing our own templates, instead of using generic ones, we
-can make this process a lot simpler. For instance:
-
-    <TR><TD>
-    First name: <INPUT TYPE="text" NAME="forename">
-    </TD>
-    <TD>
-    Last name: <INPUT TYPE="text" NAME="surname">
-    </TD></TR>
-
-    [% IF errors.forename OR errors.surname %]
-        <TR>
-        <TD><SPAN class="error">[% errors.forename %]</SPAN> </TD>
-        <TD><SPAN class="error">[% errors.surname %]</SPAN> </TD>
-        </TR>
-    [% END %]
-
-The next thing we want to do is to put the originally-submitted values
-back into the form. We can do this relatively easily because Maypole
-passes the Maypole request object to the form, and the POST parameters
-are going to be stored in a hash as C<request.params>. Hence:
-
-    <TR><TD>
-    First name: <INPUT TYPE="text" NAME="forename"
-    VALUE="[%request.params.forename%]">
-    </TD>
-    <TD>
-    Last name: <INPUT TYPE="text" NAME="surname"
-    VALUE="[%request.params.surname%]"> 
-    </TD></TR>
-
-Finally, we might want to only re-fill a field if it is not erroneous, so
-that we don't get the same bad input resubmitted. This is easy enough:
-
-    <TR><TD>
-    First name: <INPUT TYPE="text" NAME="forename"
-    VALUE="[%request.params.forename UNLESS errors.forename%]">
-    </TD>
-    <TD>
-    Last name: <INPUT TYPE="text" NAME="surname"
-    VALUE="[%request.params.surname UNLESS errors.surname%]"> 
-    </TD></TR>
-
-=head3 Uploading files and other data
-
-You want the user to be able to upload files to store in the database.
-
-B<Solution>: It's messy.
-
-First, we set up an upload form, in an ordinary dummy action. Here's
-the action:
-
-    sub upload_picture : Exported {}
-
-And here's the F<custom/upload_picture> template:
-
-    <FORM action="/user/do_upload" enctype="multipart/form-data" method="POST">
-
-    <P> Please provide a picture in JPEG, PNG or GIF format:
-    </P>
-    <INPUT TYPE="file" NAME="picture">
-    <BR>
-    <INPUT TYPE="submit">
-    </FORM>
-
-(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 L<Apache::Request>,
-then the C<upload> method of the C<Apache::Request> object (which
-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");
-
-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
-something we're prepared to deal with. 
-
-    if ($upload) {
-        my $ct = $upload->info("Content-type");
-        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 $upload->size > MAX_IMAGE_SIZE;
-
-        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);
-    }
-
-And finally, we use our familiar template switcheroo hack to get back to
-a useful page:
-
-        $r->objects([ $user ]);
-        $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 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>
-
-
diff --git a/lib/Maypole/Manual/Terminology.pod b/lib/Maypole/Manual/Terminology.pod
new file mode 100644 (file)
index 0000000..05b321e
--- /dev/null
@@ -0,0 +1,173 @@
+=head1 NAME\r
+\r
+Maypole::Manual::Terminology - common terms\r
+\r
+=head1 VERSION\r
+\r
+This version written for Maypole 2.11\r
+\r
+=head1 TERMINOLOGY\r
+\r
+For the avoidance of confusion, the following terms are defined. We'll try and \r
+ensure the Maypole docs stick to these usages.\r
+\r
+=over 4\r
+\r
+=item driver\r
+\r
+The custom package written to set up a Maypole application. This is the package \r
+that has the C<use Maypole::Application> statement. If you're not using \r
+L<Maypole::Application> to set up your app (not recommended for newbies, but \r
+common enough), the driver class will directly inherit from one of Maypole's \r
+frontend classes. \r
+\r
+=item application\r
+\r
+Sometimes this is used to refer to the driver, but this term is best reserved \r
+to refer to the complete application, i.e. driver, plugins, templates, model, \r
+the whole shebang.\r
+\r
+=item frontend\r
+\r
+An adapter class that allows Maypole to work in a number of different server \r
+environments. The currently available frontends are:\r
+\r
+       Frontend        Distribution       Environment\r
+       ==============================================\r
+       CGI::Maypole    Maypole            CGI\r
+       Apache::MVC     Maypole            Apache/mod_perl and Apache2/mod_perl2\r
+       MasonX::Maypole MasonX::Maypole    Apache/mod_perl with Mason\r
+       \r
+The driver class inherits from the appropriate frontend, which inherits from \r
+L<Maypole>.\r
+       \r
+=item backend\r
+\r
+Confusingly, sometimes you'll see the frontend referred to as the backend. It \r
+depends on your point of view.\r
+\r
+Also confusingly, the Maypole model (e.g. L<Maypole::Model::CDBI>) is sometimes\r
+referred to as the backend.\r
+\r
+You'll just need to pay attention to context. In general, it's probably best to \r
+avoid using this term altogether. \r
+\r
+=item request\r
+\r
+The Maypole request object. \r
+\r
+=item workflow\r
+\r
+The sequence of events when a browser sends a request to a Maypole \r
+application. \r
+\r
+You will also often see this referred to as the C<request> (distinct from the \r
+request object).\r
+\r
+=item Exported method\r
+\r
+A method in a Maypole model class that is labelled with the C<Exported> \r
+attribute. These methods are mapped to part of the request URI. So requesting \r
+a path will result in a particular method being called on a particular model \r
+class.\r
+\r
+=item action\r
+\r
+An Exported method.\r
+\r
+Note: not the action attribute of a form, although the form's action URI will \r
+generally include a Maypole action component.\r
+\r
+=item command\r
+\r
+In some templates, an C<action> is called a C<command>.\r
+\r
+=item template\r
+\r
+A file used to generate HTML for part or all of a web page. Maypole currently \r
+supports Template Toolkit and Mason as templating languages, but others could \r
+be added easily.\r
+\r
+=head2 MVC and Maypole\r
+\r
+=item MVC - Model-View-Controller\r
+\r
+A pattern describing separation of concerns in a complex application. The \r
+C<model> represents the domain or business logic. The C<view> represents the \r
+user interface. The C<controller> mediates the interaction between the two. \r
+\r
+Opinions vary between how closely Maypole adheres to this pattern. \r
+\r
+Here's one opinion:\r
+\r
+=over 4\r
+\r
+=item controller\r
+\r
+An abstract concept in Maypole, i.e. there is no specific controller class. \r
+\r
+The main sequence of events that occur during the processing of a request is \r
+controlled by methods in L<Maypole>. Thus, major parts of the controller are in \r
+the same class as the request object. This may seem a bit strange, but in \r
+practice it works well.\r
+\r
+More detailed events within the processing of a request are actually handled by \r
+methods in the Maypole 'model'. For instance, switching from one template to \r
+another. \r
+\r
+=item model\r
+\r
+In Maypole, the model is the set of classes representing individual tables in \r
+the database. Tables are related to each other in a more or less complex \r
+way. Each table class inherits from a Maypole model class, such as \r
+L<Maypole::Model::CDBI> or L<Maypole::Model::CDBI::Plain>. \r
+\r
+In fact, much of the functionality provided by the Maypole model class, may be \r
+better thought of as being controller code. And much of the custom code written \r
+by developers to support specific applications, is in fact controller code. \r
+\r
+The distinction is probably unimportant when using Maypole in 'default' mode - \r
+i.e. using L<Maypole::Model::CDBI>, and allowing Maypole to autogenerate the \r
+'model' classes straight out of the database. \r
+\r
+However, in many applications, a more complex domain model is required, or may \r
+already exist. In this case, the Maypole model is more clearly seen as a layer \r
+that sits on top of the custom domain model, and controls access to it from \r
+the web UI. In this kind of situation, it seems more helpful to think of the \r
+Maypole model as part of the controller. This conceptualisation helps developers \r
+maintain a separation between the Maypole model classes, and their own domain \r
+model. Without this distinction, developers may add domain-specific code \r
+to the Maypole model classes, ending up with two separate code bases \r
+intermingled within one set of files. \r
+\r
+To a certain extent, this is fine. But if you find yourself adding lots on \r
+non-Exported methods to your Maypole model classes, \r
+and these methods are not there to support Exported methods, consider whether \r
+you could separate out the domain model into a separate hierarchy of classes \r
+that you import into the Maypole model. \r
+\r
+The distinction between the Maypole model, the domain model, and the model in \r
+the MVC pattern, is fuzzy and variable. In straightforward Maypole applications, \r
+they're all pretty much the same thing. In more advanced applications, they \r
+begin to diverge. Take care out there. \r
+\r
+=item view\r
+\r
+This is represented in Maypole by the view class (L<Maypole::View::TT>, \r
+L<Maypole::View::Mason>, or L<MasonX::Maypole::View>), and by the templates. \r
+\r
+\r
+=back\r
+\r
+=head1 AUTHOR\r
+\r
+David Baird, C<< <cpan@riverside-cms.co.uk> >>\r
+\r
+=head1 COPYRIGHT & LICENSE\r
+\r
+Copyright 2005 David Baird, All Rights Reserved.\r
+\r
+This text is free documentation; you can redistribute it and/or modify it\r
+under the same terms as the Perl documentation itself.\r
+\r
+=cut\r