X-Git-Url: https://git.decadent.org.uk/gitweb/?a=blobdiff_plain;f=doc%2FRequest.pod;h=1782cd5df5b23c5b1c5600aade6e0e0e8d8fabfb;hb=b90c6e146c423746001d304953b6b136acc666db;hp=80e4037cd9bd216f3bf707880efa213327ff456f;hpb=81302aff5cf7e25674ca3af7ba79c5b3fa5f4e20;p=maypole.git diff --git a/doc/Request.pod b/doc/Request.pod index 80e4037..1782cd5 100644 --- a/doc/Request.pod +++ b/doc/Request.pod @@ -17,6 +17,38 @@ 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 routines in a separate module, +so you say: + + package BeerDB::Beer; + BeerDB::Beer->has_a(brewery => "BeerDB::Brewery"); + sub foo :Exported {} + +And in F, you put: + + use BeerDB::Beer; + +It doesn't work. + +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 @@ -40,9 +72,70 @@ 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 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: 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} ]; + } -=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 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 @@ -50,6 +143,24 @@ 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 @@ -81,10 +192,6 @@ a user's page. 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 @@ -176,6 +283,47 @@ 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{output}>. +=head3 REST + +You want to provide a programmatic interface to your Maypole site. + +B: The best way to do this is with C, which uses a +descriptive URL to encode the request. For instance, in L 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, 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 or L) request +a URL of the form +C. +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 +action, and use it to fill in the output in the same way as we did when +displaying a picture. Since C is not the ID of a +row in the user table, it will appear in the C 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 @@ -201,6 +349,9 @@ C will be placed in the C 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