From: Simon Cozens Date: Thu, 15 Apr 2004 14:10:27 +0000 (+0000) Subject: Lots more documentation. X-Git-Tag: 2.10~227 X-Git-Url: https://git.decadent.org.uk/gitweb/?a=commitdiff_plain;h=43d50fc6cf12e98c44f485eea46ac35637dcce06;p=maypole.git Lots more documentation. git-svn-id: http://svn.maypole.perl.org/Maypole/trunk@135 48953598-375a-da11-a14b-00016c27c3ee --- diff --git a/doc/About.pod b/doc/About.pod index b5cf3e7..074f4bc 100644 --- a/doc/About.pod +++ b/doc/About.pod @@ -1,10 +1,114 @@ =head1 Introduction to the Maypole Request Model -=head2 What is MVC for web applications? +This chapter serves as a gentle introduction to Maypole and setting up +Maypole applications. We look at Maypole is, how to get it up and +running, and how to start thinking about building Maypole applications. =head2 What is Maypole? +Presumably you have some idea of what Maypole is all about, or otherwise +you wouldn't be reading this manual. But Maypole is good at many +different things, and you may have accidentally focussed on one aspect +of Maypole while missing the big picture. + +For instance, you may know that Maypole is extremely good at putting web +front-ends onto databases. This is true, but it's only a part of what +Maypole does. You may have heard that Maypole is a web application +framework, which is true, but it doesn't mean very much. There are a +huge number of things that Maypole can do, because it's very much a +blank slate. You can make it do what you will. In this manual, we'll be +making it act as a front-end to a database, as a social network site, as +an intranet portal, and many other things besides. It is a framework. + +I like to think that Maypole is a way of going from a URL to a method +call to some output. If you have a URL like C, +Maypole is a way of having it load up product number 12, call an +C method, and produce a page about what it's just done. The +reason Maypole is such a big deal is because it does all this for you. +You no longer have to care about your web server. You hardly have to +care about your database. You don't have to care about templating +modules, parsing CGI parameters, or anything else. You only need to care +about business logic, and the business logic in this instance is how you +C a product, and what you need to display about it once you've +done so. This is what programming should be: only caring about the work +that distinguishes one program from another. + +It does this using a technique called MVC for web applications. + +=head2 What is MVC for web applications? + +Maypole was originally called C, reflecting its basis in +the Model-View-Controller design pattern. (I had to change it firstly +because Maypole isn't tied to Apache, and secondly because +C is a really dull name.) It's the same design pattern that +forms the foundation of similar projects in other languages, such as +Java's Struts framework. + +This design pattern is found primarily in graphical applications; the +idea is that you have a Model class which represents and manipulates +your data, a View class which is responsible for displaying that data to +the user, and a Controller class which controls the other classes in +response to events triggered by the user. This analogy doesn't +correspond precisely to a web-based application, but we can take an +important principle from it. As Andy Wardley explains: + + What the MVC-for-the-web crowd are really trying to achieve is a clear + separation of concerns. Put your database code in one place, your + application code in another, your presentation code in a third place. + That way, you can chop and change different elements at will, + hopefully without affecting the other parts (depending on how well your + concerns are separated, of course). This is common sense and good practice. + MVC achieves this separation of concerns as a by-product of clearly + separating inputs (controls) and outputs (views). + +This is what Maypole does. It has a number of database drivers, a number +of front-end drivers and a number of templating presentation drivers. +In common cases, Maypole provides precisely what you need for all of +these areas, and you get to concentrate on writing just the business +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. Just type C. + +Debian users, hang in there. There's a package coming. + +For other Unices, the L or C modules will help with +this. If you don't have C installed, my recommendation is to +use C to install it and then throw +C 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 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, with the C module installed, but as +I said, it is a blank slate, and everything is customizable. There is a +C frontend available to run as a standalone CGI script. + =head2 The Beer Database example Throughout this manual, we're going to be referring back to a particular @@ -153,7 +257,7 @@ of this manual will be about how to do that. In order to do that, we need to look at what Maypole's actually doing. -XXX +As mentioned, Maypole is XXX =head2 The model class diff --git a/doc/Beer.pod b/doc/Beer.pod index 4c4093f..033d9ce 100644 --- a/doc/Beer.pod +++ b/doc/Beer.pod @@ -181,7 +181,50 @@ Maypole's default templates will use this information to display, for instance, a list of a brewery's beers on the brewery view page. This is the complete beer database application; Maypole's default templates -and the actions in the view class do the rest. But what if we want to a +and the actions in the view class do the rest. But what if we want to do a little more. How would we begin to extend this application? =head2 The hard way + +Maypole was written because I don't like writing more Perl code than is +necessary. I also don't like writing HTML. In fact, I don't really get +on this whole computer thing, to be honest. But we'll start by ways that +we can customize the beer application simply by adding methods or +changing properties of the Perl driver code. + +The first thing we ought to look into is the names of the columns; most +of them are fine, but that "Abv" column stands out. I'd rather that was +"A.B.V.". Maypole uses the C method to map between the +names of the columns in the database to the names it displays in the +default templates. This is provided by C, and +normally, it does a pretty good job; it turns C into +"Model Number", for instance, but there was no way it could guess that +C was an abbreviation. Since it returns a hash, the easiest way +to correct it is to construct a hash consisting of the bits it got +right, and then override the bits it got wrong: + + package BeerDB::Beer; + sub column_names { + (shift->SUPER::column_names(), abv => "A.B.V.") + } + +Similarly, the order of columns is a bit wonky. We can fix this by +overriding the C method; this is also a good way to +hide away any columns we don't want to have displayed, in the same way +as declaring the C configuration parameter let us hide +away tables we weren't using: + + sub display_columns { + ("name", "brewery", "style", "price", "score", "abv", "notes") + } + +Hey, have you noticed that we haven't done anything with the +beers/handpumps/pubs thing yet? Good, I was hoping that you hadn't. +Ayway, this is because Maypole can't tell easily that a C +object can call C to get a list of pubs. Not yet, at least; we're +working on it. In the interim, we can explicitly tell Maypole which +accessors are related to the C class like so: + + sub related { "pubs" } + +Now when we view a beer, we'll have a list of the pubs that it's on at. diff --git a/doc/Request.pod b/doc/Request.pod index 1505d36..fc51d1b 100644 --- a/doc/Request.pod +++ b/doc/Request.pod @@ -40,9 +40,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 @@ -99,10 +160,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 @@ -194,6 +251,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