]> git.decadent.org.uk Git - maypole.git/commitdiff
Lots more documentation.
authorSimon Cozens <simon@simon-cozens.org>
Thu, 15 Apr 2004 14:10:27 +0000 (14:10 +0000)
committerSimon Cozens <simon@simon-cozens.org>
Thu, 15 Apr 2004 14:10:27 +0000 (14:10 +0000)
git-svn-id: http://svn.maypole.perl.org/Maypole/trunk@135 48953598-375a-da11-a14b-00016c27c3ee

doc/About.pod
doc/Beer.pod
doc/Request.pod

index b5cf3e7a2774f89ffb36875eef06f2ba0ae9c032..074f4bc1a223810e197a26c4a5447b7c5ba4b4ed 100644 (file)
 
 =head1 Introduction to the Maypole Request Model
 
 
 =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?
 
 
 =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</product/order/12>,
+Maypole is a way of having it load up product number 12, call an
+C<order> 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<order> 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<Apache::MVC>, 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<Apache::MVC> 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<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.
+
 =head2 The Beer Database example
 
 Throughout this manual, we're going to be referring back to a particular
 =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.
 
 
 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
 
 
 =head2 The model class
 
index 4c4093fa3c31e93f0e367672a63e4f3754c08e7e..033d9ce1acb8e10dc9f2d52a49c257b71757bc7b 100644 (file)
@@ -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
 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
 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<column_names> 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<Maypole::Model::Base>, and
+normally, it does a pretty good job; it turns C<model_number> into
+"Model Number", for instance, but there was no way it could guess that
+C<abv> 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<display_columns> 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<display_tables> 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<BeerDB::Beer>
+object can call C<pubs> 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<BeerDB::Beer> 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.
index 1505d361d9a014b2f0c058e4e9c3f64b7676fb0e..fc51d1ba854ea87900250ce5a6f90d83bf96e602 100644 (file)
@@ -40,9 +40,70 @@ 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.
 
 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
 
 
 =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.
 
 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
 =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<gt>{output}>.
 
 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
 =head3 Component-based Pages
 
 You're designing something like a portal site which has a number of