Overview of Gantry
This document explains Gantry from a high level. But it starts by explaining how to install the framework and the sample apps which do the most to help you use it.
Prerequisites
To use this framework, you need Apache and mod_perl. You may choose either version 1.x or version 2.x for these (but obviously the versions must match).
For instructions on how to complete these installations, consult:
http://perl.apache.org/docs/1.0/guide/install.html
or
http://perl.apache.org/docs/2.0/user/install/install.html
Following their instructions will ensure that you have the most accurate information.
Installation
This project has two distinct parts: the framework and the sample apps. These are developed and distributed separately. The framework and each sample app is a regular Perl distibution like you would find on CPAN (though they may be bundled together). They use Module::Build instead of ExtUtils::MakeMaker. This means that the procedure for installation (after you untar the distribution) is:
perl Build.pl
./Build
./Build test
./Build install
You need to do this for the framework itself and again for each sample app you want to deploy.
Configuration
Once you have installed the framework and a sample app, you need to configure the app. This amounts to including a few directives in your httpd.conf. You can just add this to you system httpd.conf or to a virtual host within it:
Include /path/to/Billing/docs/httpd.conf
See that file for clues about configuration, details follow later in this doc.
To run the billing sample app, you must also set up a database for it. To create the database, use billing.sql in the docs directory of the distibution. Choose any name you like for the database, but change the PerlSetVars in your httpd.conf appropriately (look for the ones that start with db).
In order for the apps to serve images and style sheets, you need to copy the css and images directories into your document root and set the PerlSetVars for css_root and img_root to point to the Apache serveable directory where you put them.
A Sample App (or two)
There are three sample apps that come in one bundle. You must install them separately. They are:
- Billing
-
A private contractor billing system. It keeps track of who you work for, what you did for them, and the invoices you sent to them. The invoices can be viewed as PDFs for printing, but are also stored in the database.
- Dochive
-
A small CMS for storing journal articles.
- KrKit
-
A tiny app to convert uploaded files from one format to another.
How It Works
Now that you have installed the framework and the sample apps, let's see how the pieces work together to server your users.
Consider a typical request to the Billing sample application. The user enters an address (or clicks on a link to it):
http://somehost/billing/companies/something/param1/param2
Apache handles the initial dispatch through a Location
directive like this one from the billing app's httpd.conf:
<Location /billing/companies>
SetHandler perl-script
PerlHandler Apps::Billing::Companies
</Location>
The Apps::Billing::Companies
inherits from Apps::Billing
, which in turn inherits from Gantry
. The only handler you should ever need is in the Gantry
. (Though you could override it, we never have).
The handler is a template method, meaning that it performs a standard outline of steps calling particular methods of your subclass as it needs specific help or information. Typically this sequence is
init
do_something
clean_up
In init, you typically do two things: fish PerlSetVars out of the request object through its dir_config method and setup your database access. We usually have two inits for the entire app, see init in Billing.pm and Gantry.pm for example. Grouping all of an apps configuration parameters makes them easier to find, since they all live in the same Location directive in the conf file. Further, for those reading the code, they are all in only one or two places, again making them easier to find.
In cleanup, you release any resources you need to. But, note that Class::DBI based modules handle the database connection pools for you, so you don't usually need to put database disconnects in cleanup. We usually have a single cleanup for the entire app, see Billing.pm for example.
All of the action methods begin with do_. What comes after that is taken directly from the URL.
In the do_ method, you perform whatever action you need to for the page the user requested. This includes populating the view data portion of the site object ($site). That view data goes to the template.
In reality, the handler is a bit more complex. But you can think of it like it is shown above until you care to adjust its behavior.
The main mystery is, "How does it make output?" The short answer is, it calls do_process (after the steps shown above). That method is supplied by the template engine of your choice (ours is TT). When you use Gantry
, you do it like so (this is taken from Billing.pm):
use Gantry qw/-Engine=MP20 -TemplateEngine=TT/;
The Engine here is mod_perl 2.0. The TemplateEngine is Template Toolkit.
Principles
There are many principles we used in developing this framework. See principles.pod.
Engines
A typical use statement for Gantry looks like this:
use Gantry qw/-Engine=MP20 -TemplateEngine=TT/;
The framework handles Engine and TemplateEngine options specially. See below for how it handles TemplateEngine options. Engines must be in the Gantry::Engine namespace. The engine is responsible for exporting the following methods (yes, I said exporting and methods):
- apache_request
-
Must convert the value in $$site{r} to an apache request object. $$site{r} is the second parameter to the handler when it is called as a method.
- apache_param_hash
-
Receives the apache request object and returns a hash of the form parameters in it.
- err_header_out
-
Like header_out, but for errors (see below).
- header_in
-
Receives the name of a request header. Must return the value in that header. Useful for retrieving cookies.
- header_out
-
Receives a key/value pair for one header. Must store the header so that when output is later generated, it will be sent.
- remote_ip
-
Receives the request object and returns the ip address of the client.
- send_http_header
-
Must ensure that any headers previously store via header_out and err_header_out are sent. (Not all engines need to do anything for this. For example, mod_perl 2 sends headers during the first print.)
- status
-
Receives a status string and returns the corresponding constant which the apache server understands. The possible status strings are:
DECLINED OK REDIRECT FORBIDDEN SERVER_ERROR HTTP_BAD_REQUEST HTTP_UNAUTHORIZED
Template Engines
Template engines handle the view part of MVC processing. Typically, you don't have to write any code for them, you can just use one included in the distribution. But, should you want to use Mason or some other templating system, you will have to write a template engine. These must live in the Mutant::KrKit::Template namespace.
A template engine must export the following methods:
- do_action
-
This is the actual dispatcher which calls the do_* methods of the modules you write. While most do_methods merely store their data in the site object for later retrieval in the template, some return their output instead. This method is responsible for storin such returned data in the site object stash.
- do_error
-
Makes sure that errors are correctly logged. Note that this does not affect what the user sees, only what is logged.
- do_process
-
Receives the site object and must produce output directed to the requrest object. (It must NOT send headers. They are handled prior to calling do_process).
Plugins
In addition to loading an engine and a template engine, use Gantry can also load any other plugins you request. All plugins must live in the Gantry::Plugins namespace.
Plugins are very simple. They are a lot like mixins from Ruby. Each one is a package which exports methods. These are then available throughout the app as methods of the site object. A plugin could be as simple as:
package Gantry::Plugins::TimeStamp;
use strict;
use Exporter;
our @ISA = ( 'Exporter' );
our @EXPORT = ( 'set_stamp' );
sub set_stamp {
my $site = shift;
$$site{time_stamp} = scalar localtime;
}
To use this plugin, simply add it to the list in the use statement:
use Gantry qw/-Engine=MP20 -TemplateEngine=TT TimeStamp/;
Then, whenever you need it, simply call it like so:
$site->time_stamp();
Who Uses Gantry
This web application framework was originally developed by Sunflower Broadband of Lawrence, KS (USA). Sunflower continues to use it to deliver both internal and customer facing web sites.