Listen Print

Rapid Web Application Deployment with Maypole

by Simon Cozens
April 22, 2004

You have a database. You have a web server. You have a deadline.

Whether it's bringing up an e-commerce storefront for a new venture, implementing a new front-end to HR's employee database, or even providing a neat way to track citations for U.S. English slang terms, it's always the same story -- and the deadline is always yesterday.

For this month of April, I'm working on a Perl Foundation sponsorship to develop a project of mine called Maypole, which enables Perl programmers to get web front-ends to databases, as well as complex web-based applications, up and running quickly.

Extremely quickly, and with very little Perl coding required. I've used Maypole to set up an Intranet portal, a database and display system for choosing menus and recipes, song lyric and chord sheet projection software, an open-source social network site, and a web database of beer-tasting notes; and that just was in the past two weeks.

Maypole's flexibility stems from three fundamentals:

To demonstrate these three principles, we're going to look at a bread-and-butter web application -- an online shop's product catalogue -- and see how quickly we can put it together with Maypole.

Separation of Concerns

Maypole was originally called 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 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 that represents and manipulates your data, a View class that is responsible for displaying that data to the user, and a Controller class that 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 is 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 byproduct 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.

Let's begin, then, by choosing what elements are going to make up our product database. We will actually be using what is by far the most common configuration of model, view, and controller classes: Maypole provides a model class based on Class::DBI, a view class based on Template::Toolkit, and a controller class based on Apache mod_perl. We'll come to what all of this means in a second, but because this configuration is so common, it is the default; no code is required to set that up.

We will, however, need a database. Our client is going to be iSellIt, a fictitious supplier of computer components and software. We will have database tables for products, manufacturers, and categories of stuff, and subcategories of categories. Here's what that database might look like.

    CREATE TABLE product (
        id int NOT NULL auto_increment primary key,
        category int,
        subcategory int,
        manufacturer int,
        part_number varchar(50),
        name varchar(50),
        cost decimal(6,2),
        description text
    );

    CREATE TABLE manufacturer (
        id int NOT NULL auto_increment primary key,
        name varchar(50),
        url varchar(255),
        notes text
    );

    CREATE TABLE category (
        id int NOT NULL auto_increment primary key,
        name varchar(50)
    );

    CREATE TABLE subcategory (
        id int NOT NULL auto_increment primary key,
        name varchar(50),
        category integer
    );

We're going to assume that we've loaded some data into this database already, but we're going to want the sales people to update it themselves over a web interface.

O'Reilly Open Source Convention.

In order to use Maypole, we need what's called a driver module. This is a very short Perl module that defines the application we're working with. I say it's a Perl module, and that may make you think this is about writing code, but to be honest, most of it is actually configuration in disguise. Here's the driver module for our ISellIt application. (The client may be called iSellIt, but many years exposure to Perl module names makes me allergic to starting one with a lowercase letter.)

    package ISellIt;
    use base 'Apache::MVC';
    use Class::DBI::Loader::Relationship;

    ISellIt->setup("dbi:mysql:isellit");
    ISellIt->config->{uri_base} = "http://localhost/isellit";
    ISellIt->config->{rows_per_page} = 10;
    ISellIt->config->{loader}->relationship($_) for 
        ("a manufacturer has products", "a category has products",
         "a subcategory has products", "a category has subcategories");

    1;

Ten lines of code; that's the sort of size you should expect a Maypole application to be. Let's take it apart, a line at a time:

    package ISellIt;

This is the name of our application, and it's what we're going to tell Apache to use as the Perl handler for our web site.

    use base 'Apache::MVC';

This says that we're using the Apache front-end to Maypole, and so we're writing a mod_perl application.

    use Class::DBI::Loader::Relationship;

Now we use a Perl module that I wrote to help put together Maypole driver classes. It allows us to declare the relationships between our database tables in a straightforward way.

    ISellIt->setup("dbi:mysql:isellit");

We tell ISellIt to go connect to the database and work out the tables and columns in our application. In addition, because we haven't changed any class defaults, it's assumed that we're going to use Class::DBI and Template Toolkit. We could have said that we want to use Apache::MVC with DBIx::SearchBuilder and HTML::Mason, but we don't.

Maypole's Class::DBI-based class uses Class::DBI::Loader to investigate the structure of the database, and then map the product table onto a ISellIt::Product class, and so on. You can read more about how Class::DBI's table-class mapping works in Tony's article about it.

    ISellIt->config->{uri_base} = "http://localhost/isellit";

ISellIt sometimes needs to know where it lives, so that it can properly produce links to other pages inside the application.

    ISellIt->config->{rows_per_page} = 10;

This says that we don't want to display the whole product list on one page; there'll be a maximum of 10 items on a page, before we get a page-view of the list.

    ISellIt->config->{loader}->relationship($_) for 
        ("a manufacturer has products", "a category has products",
         "a subcategory has products", "a category has subcategories");

Now we define our relationship constraints, in reasonably natural syntax: a manufacturer has a number of products, and a category will delimit a collection of products, and so on.

Ten lines of code. What has it got us?

Pages: 1, 2

Next Pagearrow





Contact Us | Advertise with Us | Privacy Policy | Press Center | Jobs | Submissions Guidelines

Copyright © 2000-2008 O’Reilly Media, Inc. All Rights Reserved. | (707) 827-7000 / (800) 998-9938
All trademarks and registered trademarks appearing on the O'Reilly Network are the property of their respective owners.

For problems or assistance with this site, email