Article

Home » Server-side Coding » CGI & Perl Tutorials » CGI::Application: A Simple, Extensible Web Framework

About the Author

Dan Horne

Dan Horne Dan has been working in IT for twelve years. He owns Redbone Systems with his wife, Katherine.

View all articles by Dan Horne...

CGI::Application: A Simple, Extensible Web Framework

By Dan Horne

May 3rd, 2005

Reader Rating: 7

Page: 1 2 3 4 Next

Last time, we introduced the concept of placing our HTML, instead of our Perl code, in separate templates. As well as providing an easy way to maintain the look and feel of a site, this approach also removes a lot of unwanted noise from our programs. As developers, our task is to consider the business need at hand; having to worry about the user interface can be a distraction.

The next step is to consider how we can actually add structure and maintainability to our code. When we write simple apps, we may have a script that runs from the first line to the last, but ultimately, we want a structured way of building our application that encourages re-usability and maintainability. Basically, we want a well-defined structure.

Many Web applications have the concept of a "run mode", although they may not use this terminology. Example run modes might be:

  • create user
  • edit user
  • list usrs

The run modes are typically encoded in the page parameters and it's not uncommon to see convoluted logic like that found in the following pseudo-code:

if (run_mode  eq 'create_user') {
   perform create user code
} elsif (run mode eq 'edit_user') {
   perform edit user code  
} elsif (run mode eq 'list_users') {
   perform list code
} else {
   error - invalid run mode
}

CGI::Application aims to provide a framework for re-usable code that is based on the run mode concept, but in a way that avoids a lot of spaghetti code. Because it's Object Oriented (OO), it can easily be extended and enhanced, and later we'll be looking at some plugins that will make your job a whole lot easier. But if you're not an OO expert, have no fear: you aren't required to make heavy use of OO techniques if you don't want to.

CGI::Application encourages you to adopt a Model-View-Controller (MVC) framework. In the Web development world, MVC has emerged as a popular concept for engineering applications, although its roots predate the Web. The model is the actual business domain that you are working in. It includes the logic and rules that the application understands and enforces. The view is simply the user interface, and by default CGI::Application supports HTML::Template, although any templating solution can be used. Finally, the controller mediates between the model and the view. It takes input, passes it to the model components and invokes the view components as appropriate.

A Sample Application

Now that we've got the buzzwords out of the way, it's time to discover that implementing a simple application is really quite easy. Imagine, for example, that we need to maintain users in our application, and we need to record the following details:

  • username -- the user's login name
  • first_name
  • last_name
  • email
  • password
  • receive_newsletter -- a flag denoting whether the user wishes to receive the newsletter

We know in advance that we'll need the following run-modes:

  • display_user_form -- This is required regardless of whether we're creating a new user, or editing an existing one
  • maintain_user -- Takes the input from the form and either creates or updates the user information
  • list_users -- When we want to edit a user, we'll need to list the current ones from which the selection can be made

The skeleton of our application will look like this:

 1  package MyApp::User;
 2  use strict;
 3  use base 'CGI::Application';
 4  use CGI::Application::Plugin::DBH        (qw/dbh_config dbh/);
 5  use CGI::Application::Plugin::ConfigAuto (qw/cfg cfg_file/);
 6  use Data::Dumper;
 7
 8  # Define our run modes
 9  sub setup {
10      my $self = shift;
11      $self->start_mode('display_user_form');
12      $self->error_mode('error');
13      $self->mode_param('rm');
14      $self->run_modes(
15          'display_user_form' => 'display_user_form',
16          'maintain_user'     => 'maintain_user',
17          'list_users'        => 'list_users',
18      );
19  }
20
21  # Execute the following before we execute the requested run mode
22  sub cgiapp_prerun {
23  }
24
25  # Process any fatal errors
26  sub error {
27      my $self  = shift;
28      my $error = shift;
29      return "There has been an error: $error";
30  }
31
32  # Display the form for creating/maintaining users
33  sub display_user_form {
34  }
35
36  # Process the submitted form
37  sub maintain_user {
38  }
39
40  # List users for editing
41  sub list_users {
42  }
43
44  # Rules for validating the submitted form parameters
45  sub _user_profile {
46  }
47
48  # We don't want to store plain text passwords....
49  sub _encrypt_password {
50  }
51
52  # Create a new user record in the database
53  sub _create_user {
54  }
55
56  # Update an existing user record in the database
57  sub _update_user {
58  }
59
60  # Retrieve the list of users from the database
61  sub _get_users {
62  }
63
64  # Retrieve the record for a given user_id
65  sub _retrieve_user_details {
66  }
67  1;

Line 1: Our application is implemented in a package, which we've named MyApp::User. Perl implements its classes in packages.

Line 2: This pragma turns on strict checking. It catches mistakes that are often made in scripting languages, such as making a typo in a variable name by forcing the developer to declare a variable before using it ($banana is not the same as $bananana). There are other advantages that we don't have time to discuss here. You should always use this pragma.

Line 3: This is where we inherit all of CGI::Application's features. For many applications, this is almost as much OO as you need to know.

Line 4: This plugin processes configuration files.

Line 5: This plugin provides an API to the standard Perl database interface.

Lines 8-18: This is where we set up our run modes.

Line 10: If no run mode is supplied, use display_user_form as the default.

Line 11: We can specify the method that is called when a fatal error occurs.

Line 12: The query parameter rm will contain the run mode name.

Lines 13-17: We map each run mode name to a method that will be invoked when the run mode is requested. Note that the run mode name doesn't have to be the same as the method name, but my preference is to keep them the same to avoid confusion.

Lines 21-22: CGI::Application will invoke the cgiapp_prerun method before it calls any run mode. This useful if you need to set up any application configuration or database connection.

Lines 25-29: Handle any fatal errors. In this case, the error handler doesn't do much -- the trapped error is simply displayed in the browser. However, in your production code, you'd probably log the message to a file or a database, and possibly send an email to the system administrator. The user would receive a simple message saying, "We apologise for technical difficulties." You don't want to show the SQL statement that failed, or anything that would reveal the inner workings of the application.

Lines 32-41: Stubs for the run mode methods that will be invoked (essentially the controller component of our MVC). We'll flesh some of these out later on.

Lines 44-45: This is where we'll place our validation code. By convention, we prefix an underscore to any method that should be considered private.

Lines 48-65: These lines represent the model component of our MVC. We could place them in a separate package, but it makes sense in this application to keep everything together.

We'll need an instance script, user.cgi, to call the package:

 1  #!c:/perl/bin/perl -T
 2  use strict;
 3  use CGI::Carp qw(fatalsToBrowser);
 4  use MyApp::User;
 5  my $webapp = MyApp::User->new(tmpl_path => '/path/to/templates/');
 6  $webapp->run();

And that's it: our CGI script is only six lines long! A quick explanation:

Line 1: Lets the Web server know where our Perl interpreter is located.

Line 3: This is useful for debugging, as it displays any CGI script error in the browser (other errors will be trapped by our package's error run mode). You should remove this from production code.

Line 4: Use our new package.

Line 5: Create a new object based on our package. We pass the name of the directory where our templates are located.

Line 6: Run the application.

Perl looks for its packages in the directories defined in the @INC array. In other words, the interpreter will be search for a file called MyApp/User.pm in any of the @INC directories. Alternatively, if none of these directories suit, you can programmatically add to @INC at run time with use lib:

 1  #!c:/perl/bin/perl -T
 2  use lib 'path/to/my/libs';
 3  use strict;
 4  use CGI::Carp qw(fatalsToBrowser);
 5  use MyApp::User;
 6  my $webapp = MyApp::User->new(tmpl_path => '/path/to/templates/');
 7  $webapp->run();

A side note: one of the many advantages of CGI::Application is that the main application is implemented in a package, not a CGI script. This means that the bootstrap CGI instance script could be replaced with a mod_perl instance script. For those who don't know, mod_perl is an Apache module that compiles the Perl interpreter into the Apache kernel. There are many benefits to this, one of which is that it makes your code blindingly fast. You can find out more about mod_perl at http://perl.apache.org.

If you liked this article, share the love:
Print-Friendly Version Suggest an Article

Sponsored Links

Follow SitePoint on...