Maypole3

From Books

Jump to: navigation, search

Contents

[edit] Maypole Basics

Now we've seen what Maypole is capable of, let's now take a bit more detailed look at how it actually works. Here we're going to discuss the theory of operating a Model-View-Controller process like Maypole, but we'll also try to tie it into some practical examples as well.

[edit] Important concepts

There are at least two really important concepts that are central to Maypole's operation, and, annoyingly, they're both common words used in a jargony way. So it's important to know what we're talking about when we refer to a Maypole request and a Maypole action.

The view of Maypole from a thousand miles looks like this:


Figure 2.1. Maypole workflow (from space)

Image:Maypole-space.png

That is, we start with something from the user - a request for a page, typically, with maybe some form parameters. Basically, nothing much. This hits our Maypole application as a request, and we spend a little bit of time fleshing it out until it becomes a big fat object which has enough information to respond to the request, at which point it becomes something small again (a page).

"Fleshing it out" typically involves working out what we need to do to produce that page, pulling the data out of the database, munging it appropriately, finding the appropriate template to display the data, collecting together the variables which have to go into that template, and so on. Let's take a closer look at this workflow.

[edit] Maypole request

A Maypole request is analogous to an Apache mod_perl request; it contains all the information that Maypole gathers from the environment and the user in order to do something and spit out a page. The funky thing about Maypole requests, unlike mod_perl requests, is that your application is a specialisation of the Maypole class; the Maypole request will actually be an object in the Supporters class or whatever your application is called. This seems strange, but it turns out to be really useful later.

We'll see soon what data the request object actually gathers, and what happens when the request is processed, but for now you need to know that it's essentially an encapsulation of your application and the user's request for a web page.

[edit] Maypole action

Generally when you're requesting a page from a Maypole application, you want something to happen. At the very least, you're going to want some text to spit back as a web page; (this is the "View" of the MVC web application paradigm) additionally, you're going to want to do something with your data - either to load up some data to view it, to edit a record in the database, and to delete one, or some other activity. (This is the "Model" part. Loosely.)

[edit] An example

We access the "supporter/list" page of our supporters application. "supporter" is the database table, and "list" is the action. The job of the model part is to grab a bunch of database rows from the database. This happens like so: Maypole turns the table name "supporter" into the class name Supporters::Supporter, since this is the model class which governs the "supporter" table. (We'll see in the CDBI chapter why this is.) Supporters::Supporter inherits from one of the default Maypole model classes, which provides a "list" method. So Maypole calls Supporters::Supporter->list to grab the database rows, which are modelled as Supporters::Supporter objects.

These objects are associated with the Maypole request object. The view part then puts the objects it has gathered into a template variable, and processes that template to produce a page containing the details of the database rows.

Therefore the response to a Maypole request breaks down as the execution of a method call on a model class, and the selection and processing of a template in a view class. Together, these two stages are known as an action. A useful thing to know is that the view part is generally good enough for almost everything you need to do with Maypole - typically you're just processing templates, and customization of this part of the action is done by writing better templates. You don't need to write Perl code for this bit, just templates.

[edit] Exception to general rule...

We've just said that the second part of an action requires writing templates. Of course, there are times when this isn't true, and it doesn't need to be true. So when, for instance, you're pulling a picture out of the database, you don't want to put the JPEG data through a template, you want to just spit it out to the browser. Don't worry, you can do that - the template step is optional - and we'll see how to do it later.

On the other hand, you will need to write methods, because I can't tell in advance everything that your Maypole application needs to do! If you want a page which processes a credit card request, you'll need to write a method which does the credit card processing. That's the bad news. The good news is that Maypole does everything else for you.

So "writing an action" in Maypole-speak typically means writing a method in a model class, and writing a template. Since writing a template is to be expected, sometimes an "action" just refers to the method.

[edit] Note

Key point: in particular, an action is a method in a model class which is marked by the :Exported attribute. The purpose of this attribute is to prevent a user in front of a web browser from being able to call any method at all on your model classes! Actions are therefore declared like so:


sub something :Exported {
   my ($class, $r) = @_;
   $r->objects([ $class->get_the_appropriate_objects ]);
}

where $r is the Maypole request object.

[edit] The scary stuff with the model class

This is probably the messiest and hardest to understand part of Maypole's operation. Once you get it, everything becomes clear.

The concept of a "model class" in Maypole is a multi-layered one. This is because Maypole wants to provide you with somewhere you can place your own actions specific to each table, but of course it wants to do a lot of the work for you. First, it wants to provide default actions for things like listing, editing and viewing rows; second, it wants a basis for talking to the database table in the first place - something like Class::DBI. So your model class actually looks something like this:

Figure 2.2. Model class inheritance

Image:Modelclass.png

[edit] Warning

Maypole causes the per-table model classes to inherit from Maypole::Model::CDBI at runtime; this can occasionally cause a gotcha if you're writing your own actions. For instance:

 package Supporters;
 use Maypole::Application;
 Supporters->setup("...");
 # ...
 package Supporters::Supporter;
 
 sub total :Exported {
     # Calculate the total given by this person,
     # and display it
 }
       

This won't work. This is because Supporters::Supporter doesn't know anything about the :Exported attribute until Supporters->setup has run, and so when Perl tries to compile the attribute, it dies horribly. Putting BEGIN { } around the call to setup is one way to take care of this.

[edit] The Maypole workflow

Now we're in a position to have a closer look at the Maypole workflow. Here's a diagram which adds more details to the "view from space" we saw at the beginning of the chapter:

Figure 2.3. Maypole workflow

Image:Maypole process.png

Another view of this process can be found in the Maypole::Workflow manual page which comes with Maypole.

XXX

[edit] The Maypole request object

Once the Maypole process of dealing with a request is kicked off (generally by Apache calling YourApp->handler, which Maypole provides for you) a request object is instantiated. As mentioned previously, this is actually an object of YourApp's class, and it is generally referred to, in Apache mod_perl style, as $r. The object contains the following slots, which get filled in by the handler:

Table 2.1. $r

config The configuration object for this application
view_object An object used to view the data; typically a Template object
ar The Apache::Request object for this request, if applicable
path The full path requested, as returned from Apache or from the CGI environment; let us assume this is /supporter/view/4. It would then be further split up into:
table The database table to act on, in this case supporter.
action The requested action: view
args Any further arguments in the path - in this case, 4, which will later be turned into an object representing the Supporter with ID 4 in the database.
params Any CGI form parameters
model_class The table-specific model class in use; ie, Supporters::Supporter
template The name of the template file to be processed. In the example above, this would be view unless the view method decided to change it.
objects Populated with the objects to be acted on; for instance, the 4 argument would be converted to a Supporters::Supporter object here. The list action would populate this with multiple objects.
template_args Any user-supplied arguments to be handed on to the template; this is populated either in the action or in the additional_data method. (Or both, of course)
content_type Used to mark the content type to be returned to the browser.
output Eventually filled in by the view class, unless the action wants to intervene, this is the data that gets sent back to the browser.

[edit] Template Selection

Maypole searches for templates in three different places: first, it looks for a template specific to a class; then it looks for a custom template for the whole application; finally, it looks in the factory directory to use the totally generic, do-the-right-thing template.

The basic application we saw in the first chapter used factory templates to achieve everything; serious Maypole applications will generally want to use their own templates for everything.

Therefore underneath whatever you specify to be the template root for your Maypole application, we expect at least three subdirectories; if you're using factory templates, you should copy all the templates which ship with Maypole into a subdirectory called factory, as we did in the first chapter. Second, if you're customizing some of those templates, or want a common area to place, for instance, macro files, there should be a custom subdirectory. Finally, for each table there should be a subdirectory containing the templates you want to use. For instance, if you want to customize the way supporter/view looks, you place your own view template in the supporter subdirectory. It's as simple as that.

Here's the code in Maypole which makes it all happen:


sub paths {
    my ( $self, $r ) = @_;
    my $root = $r->config->template_root || $r->get_template_root;
    return (
      $root,
      (
        $r->model_class
         && File::Spec->catdir( $root, $r->model_class->moniker )
      ),
      File::Spec->catdir( $root, "custom" ),
      File::Spec->catdir( $root, "factory" )
    );
}

XXX

[edit] Configuration

Maypole gives each application a hash in which it can configure itself; in our application, that's accessed through Supporters->config. Here are the various slots in the configuration hash:

Table 2.2. Maypole configuration hash

dsn The DBI DSN provided to setup.
template_root The path underneath which the template files can be found.
uri_base The application's root on the web site; that is, the base URL of this application
model The model class for this application - typically either Maypole::Model::CDBI or Maypole::Model::CDBI::Plain.
tables / classes The tables in the database we can play with, and the associated model classes; these are typically set up by the call to setup - the tables and classes are determined automatically in the Maypole::Model::CDBI case, and manually supplied to setup in the case of ::CDBI::Plain
loader In the case of Maypole::Model::CDBI, the Class::DBI::Loader (see next chapter) object is placed here
rows_per_page When Class::DBI::Pager (again, see next chapter) is used, the number of data rows to be displayed on a single page
Anything else The config hash is to be used as a generic place for the application or any plugins it uses to specify their configuration details, so all kinds of things may appear in other slots.

[edit] Review of workflow

Let's once again trace the path of a request through Maypole.

When a request is made for, say, /XXX

[edit] Exercises

XXX

Personal tools