X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=doc%2FRequest.pod;h=1782cd5df5b23c5b1c5600aade6e0e0e8d8fabfb;hb=b90c6e146c423746001d304953b6b136acc666db;hp=6af82784ba5263dc2e78a07a681251bf63e1bbd2;hpb=d6437cf11ffb95f80143ea9af87e3bc972b0807e;p=maypole.git diff --git a/doc/Request.pod b/doc/Request.pod index 6af8278..1782cd5 100644 --- a/doc/Request.pod +++ b/doc/Request.pod @@ -1,44 +1,197 @@ =head1 Maypole Request Hacking Cookbook +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 -=head3 Changing how params are parsed +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 REST +=head3 Separate model class modules -=head2 Authentication hacks +You want to put all the C routines in a separate module, +so you say: -=head2 Creating and editing hacks + package BeerDB::Beer; + BeerDB::Beer->has_a(brewery => "BeerDB::Brewery"); + sub foo :Exported {} -=head3 Getting data from external sources +And in F, you put: -You want to supplement the data received from a form with additional -data from another source. + use BeerDB::Beer; -B: Munge the contents of C< $r-Eparams > before jumping -to the original C routine. For instance, in this method, -we use a C object to fill in some fields of a database row based -on an ISBN: +It doesn't work. - sub create_from_isbn :Exported { - my ($self, $r) = @_; - my $response = $ua->search(asin => $r->{params}{isbn}); - my ($prop) = $response->properties; - # Rewrite the CGI parameters with the ones from Amazon - @{$r->{params}{qw(title publisher author year)} = - ($prop->title, - $prop->publisher, - (join "/", $prop->authors()), - $prop->year()); - # And jump to the usual edit/create routine - $self->do_edit($r); +B: It doesn't work because of the timing of the module +loading. C will try to set up the C 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 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 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: Use the C module to go directly from a URL to +standard output, bypassing Apache and the network altogether. + +C 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 module programatically to create +test suites for your application. See the Maypole tests themselves or +the documentation to C for examples of this. + +=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: So far we've been using the C 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. This is responsible for taking +C<$r-E{path}> and filling the C, C and C slots +of the request object. Normally it does this just by splitting the path +on Cs, but you can do it any way you want, including getting the +information from C form parameters or session variables. + +For instance, suppose we want our URLs to be of the form +C, we could provide a C 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} ]; } -The request will carry on as though it were a normal C POST, but -with the additional fields we have provided. +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 from the query. Later methods +will confirm whether or not these tables and actions exist. + +See L 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: 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 config parameter or, if this is not +given, calling the C 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 +These hacks deal primarily with the presentation of data to the user, +modifying the C 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: There are two ways to do this, depending on what precisely +you need. If you just need to display a template, C +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 action, then you need to +ensure that you have an exported action, as described in +L: + + sub my_view :Exported { } + +=head3 Template Switcheroo + +An action doesn't have any data of its own to display, but needs to display +B. + +B: This is an B common hack. You've just issued an +action like C, 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> instead, but we can actually modify the Maypole +request on the fly and, after doing the update, pretend that we were +going to C> all along. We do this by setting the +objects in the C slot and changing the C