]> 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.
 
-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
-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.)
 
+
+=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
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.
 
+=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
@@ -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>.
 
-=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
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.
 
-=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
 
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
 
-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;
@@ -22,11 +23,12 @@ support to the BeerDB example application:
 
 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
-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;
@@ -34,7 +36,9 @@ parts of the application. For instance, to add sessions to your application, you
 
    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  => {
@@ -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
 
-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 / );
 
-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 / );
 
@@ -69,20 +78,24 @@ Now the accessors are only added to applications that use this plugin.
 
 =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 );
 
-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;
@@ -102,20 +115,23 @@ the call up through the hierarchy. A common idiom for this is:
        # 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;
@@ -127,35 +143,39 @@ You can use the C<setup> method to load modules into the request class namespace
          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
-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.
 
-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
 
-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 / );
 
-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
 
@@ -163,28 +183,33 @@ be available on the config object.
 
 =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.
 
-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";
 
-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;
 
-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
 
@@ -193,8 +218,6 @@ Same plugin, next line has
 Same yuk, same mod_perl caveat.
 
 
-
-
 =back
 
 
@@ -208,11 +231,13 @@ Again, just specify a different view in the application configuration.
 
 =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
    {
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