Copyright Notice

This text is copyright by InfoStrada Communications, Inc., and is used with their permission. Further distribution or use is not permitted.

This text has appeared in an edited form in Linux Magazine magazine. However, the version you are reading here is as the author originally submitted the article for publication, not after their editors applied their creativity.

Please read all the information in the table of contents before using this article.

Linux Magazine Column 71 (Jun 2005)

[Suggested title: ``Introduction to CGI::Prototype (part 2)'']

Last time, I talked about why I had created my CGI::Prototype framework, and the basics of creating an application. I'll continue the discussion from where we left off, from where I had just described the basic class as:

  sub activate {
    eval {
      my $self = shift;
      my $this_page = $self->dispatch;
      my $next_page = $this_page->respond;
      $next_page->render;
    };
    $self->error($@) if $@;
  }

In this case, the application provides a dispatch method to determine which page object will get to look at the incoming parameters. That respond page object then has the responsibility to return a render page object, which returns the results to the user.

Let's look at this framework to implement the classic ``two-pass'' application. A CGI script responds first with a form to fill out, and then handles the submission of that form to generate a result.

First, we'll create MyApp.pm:

  package MyApp;
  use base CGI::Prototype;

(I'm omitting the 1; from these example modules, just for brevity.)

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''. We'll need to create page objects for the first and second passes. For the first pass, we'll just override the template:

  package MyApp::One;
  use base MyApp;
  sub template { 'the_form.tt' }

We'll dispatch to the second pass if there are any incoming parameters. Because Template Toolkit code can access the CGI object (more on this later), we still have no code to write! So the second pass looks similar:

  package MyApp::Two;
  use base MyApp;
  sub template { 'the_response.tt' }

We'll now need a dispatcher to select which one of these two pages should be in charge. So, back in to MyApp.pm:

  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.''

Where is param? This method is provided by the base class. Similarly, a CGI method returns the CGI.pm object to enable 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 to get the first_name incoming parameter simply with self.param('first_name'). So, in the_form.tt, we will find:

  [% 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>

Note that we're using CGI.pm's header and form methods, but writing the rest of the HTML ourselves. Therefore, this file is mostly editable by someone without a lot of Perl knowledge, except for nudging to ``put this magic code here and there'' to get to the parameters or creating the fields.

We'll also use callbacks 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 less-thans and greater-than's don't ruin my day with a cross-site scripting attack.

And now, in one master CGI script of a few lines, three controller classes (application, page one, page two) in separate modules, and two templates, I now have a working application again. 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 they are incomplete, we should render the form again instead of the response. We could make this the responsibility of the current dispatcher, but let's show an alternative by making the MyApp::Two page decide when it doesn't have enough data. We'll do this by adding a respond method from its prior return $self to something like:

  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 this is 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 we can implement by a responder on page one instead of two. First, we'll 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
  }

We'd also remove the respond 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, we then render from page two instead of page one. Until this is successful, we'll keep redisplaying page one. (In practice, we'd also set a variable that the form would consult to indicate why the user is seeing the same form again.) And 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, this gets a bit trickier. We need to figure out what state we are in, 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 will the dispatcher get the state information?

We 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.

We could determine the current state with a mangled URL. This means that some part of the URL includes the state, usually in the ``pathinfo'' portion after the name of the CGI script. For example, if foo.cgi is our CGI script, both:

  /my/path/to/foo.cgi/one

and

  /my/path/to/foo.cgi/two

invoke foo.cgi. But within the script, we can examine the value of CGI.pm's path_info, which will be /one or /two, respectively. And this can give us the right state information to dispatch back to the proper page object.

We could also determine the current state with a cookie. On each page display, we could 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 that may be in different states in the application.

We might even mix together some combination of those, or combine them with some server-side database. The important thing is that we have to know how to associate this 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 MUMBLE_enter and MUMBLE_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 is 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 here's the full activate method defined in CGI::Prototype:

  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 (default empty). 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 L. Schwartz is a renowned expert on the Perl programming language (the lifeblood of the Internet), having contributed to a dozen top-selling books on the subject, and over 200 magazine articles. Schwartz runs a Perl training and consulting company (Stonehenge Consulting Services, Inc of Portland, Oregon), and is a highly sought-after speaker for his masterful stage combination of technical skill, comedic timing, and crowd rapport. And he's a pretty good Karaoke singer, winning contests regularly.

Schwartz can be reached for comment at merlyn@stonehenge.com or +1 503 777-0095, and welcomes questions on Perl and other related topics.