dcsimg

CGI::Prototype, Part Two

Need to create a CGI application? Save time and lines of code with Randal’s new CGI::Prototype.
Last time, I explained why I created my CGI::Prototype framework and I demonstrated the basics of creating an application. This month, let’s pick up where I last left off.
The basic class is fairly spartan:
sub activate {
eval {
my $self = shift;
my $this_page = $self->dispatch;
my $next_page = $this_page->respond;
$next_page->render;
};

$self->error($@) if $@;
}
Here, the application provides a dispatch() method to determine which page object should respond to the incoming parameters. That page object’s respond() method must also return a page object to render() the page that returns the results to the user.
Let’s look at how to implement a classic “two-pass” CGI application with the CGI::Prototype framework.
During the first pass, a CGI script responds with a form to fill out; in the second pass, it processes the submission of that form and generates a result. So, first, create MyApp.pm
package MyApp;
use base CGI::Prototype;
… and then use it…
#!/usr/bin/perl
use MyApp;
MyApp->activate;
As stated last time, the default behavior simply prints “This page intentionally left blank”. (I’m omitting the 1; from these example modules for brevity.)
To continue, create page objects for the first and second passes. For the first pass, just override the template:
package MyApp::One;
use base MyApp;
sub template { ’the_form.tt’ }
If there are any incoming paramaters, dispatch to the second pass. Because Template Toolkit code can access the CGI object (more on this later), there’s still no code to write! So, the second pass looks similar:
package MyApp::Two;
use base MyApp;
sub template { ’the_response.tt’ }
Now you need a dispatcher to select which of the two pages should be in charge. So, back in MyApp.pm, add:
sub dispatch {
my $self = shift;
return $self->param ? ’MyApp::Two’ : ’MyApp::One’;
}
This says, “If there are any parameters, use page object MyApp::Two, otherwise use MyApp::One. ” But where is param()? It’s provided by the base class. Similarly, the base class provides a CGI() method to return the CGI.pm object, which enables form generation and miscellaneous incoming parameter access.
When a template is called, the page object is passed as the self variable. Thus, a template can call the param() method with self.param(’first_name’) to get the first_name incoming parameter.
So, in the_form.tt, you’d write:
[% self.CGI.header %]
<html><head></head><body>
<h1>Welcome!</h1>
Please enter your first and last name:
[%
self.CGI.start_form;
self.CGI.textfield(’first’);
self.CGI.textfield(’last’);
self.CGI.submit;
self.CGI.end_form;
%]
</body></html>
The code uses CGI.pm ’s header() and form methods, but the rest of the HTML is manually created. Therefore, this file is mostly editable by someone without a lot of Perl knowledge, although you may have to nudge them to “put this magic code here and there” to get to the parameters or to create the fields.
Callbacks are used to fetch the response in the_response.tt:
[% self.CGI.header %]
<html><head></head><body>
<h1>Welcome!</h1>
Greetings, [% self.param(’first’) | html %]
[% self.param(’second’) | html %]!
</body></html>
Here, I’m using Template Toolkit’s html filter to ensure that special characters like <(” less than”) and >(” greater than”) are elided, avoiding a cross-site scripting attack.
So, in one master CGI script of just a few lines, three controller classes in separate modules (application, page one, page two), and two templates, I have a working application. The view code is in Template Toolkit format, which I can hand off to a web designer, and the controller code is in Perl, as it belongs.
But now, let’s deal with the problems.
If there are parameters, but some are missing, the code should render the form again instead of the generating the response. Yes, you could make this the responsibility of the current dispatcher, but instead, let’s add a respond() method to MyApp::Two that allows that page to decide when it doesn’t have enough data.
sub respond {
my $self = shift;
unless ($self->param(’first’) =~ /\S/ and
$self->param(’last’) =~ /\S/) {
return ’MyApp::One’; # back to the form
}
return $self;
}
Now, when dispatch() hands control to MyApp::Two for the response, the response can decide that it’s the wrong state and send the control back to the MyApp::One page object for the rendering.
Although this solves the problem one way, it tightly couples the error checking for page two with the form elements of page one. A better model is “stay here until you get it right,” which you can implement by placing a responder on page one instead of page two.
First, let’s tell the dispatcher to always send to state one (in MyApp.pm):
sub dispatch { return ’MyApp::One’ }
And now in MyApp/One.pm:
sub respond {
my $self = shift;
if ($self->param(’first’) =~ /\S/ and
$self->param(’last’) =~ /\S/) {
return ’MyApp::Two’; # good params, move on
}

return $self; # bad params? stay here
}
(Be sure to also remove the respond() method introduced earlier into MyApp/Two.pm.) Thus, when someone has provided good values (containing at least one non-whitespace character) for both the first and last parameters, page two renders instead of page one. And until the form is completed successfully, page one is redisplayed over and over. (In practice, you might also set a variable that the form would consult to indicate why the user is seeing the same form again.) CGI.pm ’s sticky-form-field mechanism also ensures that the default value for the form parameters retain their value from one invocation to the next.
For multi-page applications, thing are a bit trickier. The code must determine its state and dispatch to the proper corresponding page object. We don’t want the dispatcher to always dispatch to a hardwired page object, as that would merely shove the problem down one level. But where can the dispatcher get the state information?
You could do it with a hidden field in a form. (Hint: this is what CGI::Prototype::Hidden does.) This requires that each page use a form that has a hidden field somewhere.
You could determine the current state with a mangled URL, leaving the state in part of the URL, usually in the “pathinfo” portion after the name of the CGI script.
For example, if foo.cgi is the CGI script, both /my/path/to/foo.cgi/one and /my/path/to/foo.cgi/two invoke foo.cgi. But within the script, you can examine the value of CGI.pm ’s path_info, which will be /one or /two, respectively. path_info gives the right state information to dispatch back to the proper page object.
You could also determine the current state with a cookie. On each page display, you’d send out a cookie that has that state and then read the cookie on the reply. However, this method prevents the user from having two browser windows open at once in different states in the application.
You might even mix together some combination of those, or combine them with some server-side database. The important thing is that the application has to know how to associate an incoming hit with some prior rendering from a page object.
The activate() method shown earlier is actually a bit more customizable. Each action is bracketed in a pair of …_enter() and …_leave() calls. For example, app_enter() is called before anything else happens, and app_leave() is called after everything else is done. By default, these hooks do nothing, but they provide a nice place for setup and teardown steps.
For the remaining hooks, let’s define control() for a page object as the time for which it is acting as either a respond or render page. The control_enter() and control_leave() hooks bracket this period of time. Because these are called only once per hit against a given page, they’re a great place to put database connect and disconnect calls, for example.
A page also gets respond_enter() and respond_leave() calls when it’s acting as a responder, and corresponding render_enter() and render_leave() calls when acting as a renderer. For example, a good use for render_enter() is to preload data needed for sticky fields.
This complicates the activate() method a bit, so Listing One shows the complete activate() method defined in CGI::Prototype:
LISTING ONE: CGI::Prototype s complete activate() method

sub activate {
my $self = shift;
eval {
$self->app_enter;
my $this_page = $self->dispatch; ### DISPATCH ###
$this_page->control_enter;
$this_page->respond_enter;
my $next_page = $this_page->respond; ### RESPOND ###
$this_page->respond_leave;
if ($this_page ne $next_page) {
$this_page->control_leave;
$next_page->control_enter;
}
$next_page->render_enter;
$next_page->render; ### RENDER ###
$next_page->render_leave;
$next_page->control_leave;
$self->app_leave;
};
$self->error($@) if $@;
}

The default render() engine takes the result of calling the template() method, running it through the Template Toolkit processor created by calling the engine() method. The result is captured, and sent to the output() method. (I override output() for testing, sending the output into a variable, for example.)
The default engine() creates a Template Toolkit object, configured according to the hashref returned by the engine_config() method (which is empty by default). This parameter is autoloaded (thanks to Class::Prototyped), so we don’t keep recreating the engine in a persistent environment (such as mod_perl).
There’s nothing stopping you from easily overriding render() to call your favorite templating engine instead, even using template() if it makes sense to you. I tried to keep it easy to use Template Toolkit (the best templating engine for Perl today), but I realize this is a religious issue for some folks.
And that’s the basics of CGI::Prototype. Nothing revolutionary, but a lot of the right stuff in the right place.
Next time, I’ll take a look at a longer application, created with CGI::Prototype::Hidden. Until then, enjoy!

Randal Schwartz is the chief Perl guru at Stonehenge Consulting. You can reach Randal at class="emailaddress">merlyn@stonehenge.com.

Comments are closed.