test suites for your application. See the Maypole tests themselves or
the documentation to C<Maypole::CLI> for examples of this.
-=head3 Changing how params are parsed
+=head3 Changing how URLs are parsed
+
+You don't like the way Maypole URLs look, and want something that either
+fits in with the rest of your site or hides the internal workings of the
+system.
+
+C<Solution>: So far we've been using the C</table/action/id/args> form
+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</>s, 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";
+ ($r->{table}, $r->{action}) =
+ ($r->{path} =~ /^(.*?)([A-Z]\w+)\.html/);
+ $r->{table} = lc $r->{table};
+ $r->{action} = lc $r->{action};
+ my %query = $r->{ar}->args;
+ $self->{args} = [ $query{id} ];
+ }
-=head3 REST
+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 L<BuySpy.pod> 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->{ar}->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
This hack is so common that it's expected that there'll be a neater
way of doing this in the future.
-=head3 Maypole for mobile devices
-
-XXX
-
=head3 XSLT
Here's a hack I've used a number of times. You want to store structured
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.pod> 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->{content} = "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