LevSelector.com New York
home > Perl CGI::Application module

Perl CGI::Application module
- www.perldoc.com/cpan/CGI/Application.html
- http://www.mail-archive.com/cgiapp@lists.vm.com/
 
intro home - top of the page -

The idea is to build your site as a set of logical pieces called applications.
Each application has 5..20 different screens, called run-modes, and is represented by 2 files:
   - a small cgi script which you call with a parameter rm (run-mode). Just few lines. It does new() and run().
   - a module which inherits from CGI::Application and has the procedures implementing those run-modes.

Here is a sequence of main events for some generic CGI script call using CGI::Application:
#-----------------------------------------
new()
   create webappl object (CGI::Application)
   create query object (CGI.pm)
   run setup()
      define runmodes
      connect to database
#-----------------------------------------
run()
   determine which runmode to run
   $body = $self->some_run_mode_method( )
      update_something_in_the_database( )
      my $output = some_html( ) 
            #  for example, call method $self->show_list( )
            #  which may query database and return HTML for the list
            #  (it may use HTML::Template for that) 
      return $output
   get $headers
   my $output = $headers + $body
   pring $output   # here it is sent to the browser
   teardown()
     close database connection, etc.

There are also many other files (html templates, images, etc.)
Here is a directory structure:
 
${project_root_dir}/
   |--bin/
   |--devdocs/
   |--htdocs/
   |--modules/
   |    |--MyNamespace/
   |--templates/

Here's a description of the different directories:
  * bin/  -- In this directory you can store Perl scripts (or other executables) which need to be called via cron or by the sys-admin from the command-line.
  * devdocs/  -- Use this directory to store all the DOCUMENTATION you develop during the course of your project!
  * htdocs/  -- The web server's Document Root.  All your HTML files, images and CGI instance scripts are stored here.
  * modules/  -- Required Perl modules are stored here.  This path is set via the environment variable "PERL5LIB" and passed through the web server ("SetEnv" and "PerlSetEnv" in Apache).  "MyNamespace/" is the directory where you will put your custom modules (mostly CGI::Application modules).
  * templates/  -- HTML::Template files are stored here.  The environment variable "HTML_TEMPLATE_ROOT" is passed via the web server to tell HTML::Template where to find its files.

Sub-directories can be created in the "templates/" directory which match subdirectories and app modules under "modules/MyNamespace/".  For instance, if you have an administrative CGI::Application called the "User Manager" you might create MyNamespace::Admin::UserManager:

  ${project_root_dir}/modules/MyNamespace/Admin/UserManager.pm

This application might have three run-modes, "search_form", "results_list" and "edit_detail".  Each mode has one template.  These templates should be stored as follows:

  ${project_root_dir}/templates/Admin/UserManager/search_form.tmpl
  ${project_root_dir}/templates/Admin/UserManager/results_list.tmpl
  ${project_root_dir}/templates/Admin/UserManager/edit_detail.tmpl

In "htdocs/", you would have your instance script:

  ${project_root_dir}/htdocs/admin/usermanager.pl
 
 
From docs home - top of the page -

To write this application using CGI::Application you will create two files:

   1. WidgetView.pm -- Your "Application Module"
   2. widgetview.cgi -- Your "Instance Script"

  The Application Module contains all the code specific to your application functionality, and it exists outside of your web server's document root, somewhere in the Perl library search path.
  The Instance Script is what is actually called by your web server.  It is a very small, simple file which simply creates an instance of your application and calls an inherited method, run().
Following is the entirety of "widgetview.cgi":
#!/usr/bin/perl -w
use WidgetView;
my $webapp = WidgetView->new();
$webapp->run();

  As you can see, widgetview.cgi simply "uses" your Application module (which implements a Perl package called "WidgetView").  Your Application Module, "WidgetView.pm", is somewhat more lengthy:
 
   package WidgetView;

   use base 'CGI::Application';
   use strict;
   use DBI;   # Needed for our database connection

# -----------------------------------------
# -----------------------------------------
   sub setup {
 my $self = shift;
 $self->start_mode('mode1');
 $self->run_modes(
  'mode1' => \&showform,
  'mode2' => \&showlist,
  'mode3' => \&showdetail
 );

 # Connect to DBI database
 $self->param('mydbh' => DBI->connect());
   }

# -----------------------------------------
# -----------------------------------------
   sub teardown {
 my $self = shift;

 # Disconnect when we're done
 $self->param('mydbh')->disconnect();
   }

# -----------------------------------------
# -----------------------------------------
sub showform {
  my $self = shift;

 # Get CGI query object
 my $q = $self->query();

 my $output = '';
 $output .= $q->start_html(-title => 'Widget Search Form');
 $output .= $q->start_form();
 $output .= $q->textfield(-name => 'widgetcode');
 $output .= $q->hidden(-name => 'rm', -value => 'mode2', -override=>1);
   # CGI.pm has a feature to pass values of hidden parameters
   # use -override option to override this default behavior 
 $output .= $q->submit();
 $output .= $q->end_form();
 $output .= $q->end_html();

 return $output;
   }

# -----------------------------------------
# -----------------------------------------
 sub showlist {
 my $self = shift;

 # Get our database connection
 my $dbh = $self->param('mydbh');

 # Get CGI query object
 my $q = $self->query();
 my $widgetcode = $q->param("widgetcode");

 my $output = '';
 $output .= $q->start_html(-title => 'List of Matching Widgets');

 ## Do a bunch of stuff to select "widgets" from a DBI-connected
 ## database which match the user-supplied value of "widgetcode"
 ## which has been supplied from the previous HTML form via a 
 ## CGI.pm query object.
 ##
 ## Each row will contain a link to a "Widget Detail" which 
 ## provides an anchor tag, as follows:
 ##
 ##   "widgetview.cgi?rm=mode3&widgetid=XXX"
 ##
 ##  ...Where "XXX" is a unique value referencing the ID of
 ## the particular "widget" upon which the user has clicked.

 $output .= $q->a({href=>'widgetview.cgi?rm=mode3&widgetid=XXX'},'XXX');

 $output .= $q->end_html();

 return $output;
   }

# -----------------------------------------
# -----------------------------------------
   sub showdetail {
 my $self = shift;

 # Get our database connection
 my $dbh = $self->param('mydbh');

 # Get CGI query object
 my $q = $self->query();
 my $widgetid = $q->param("widgetid");

 my $output = '';
 $output .= $q->start_html(-title => 'Widget Detail');

 ## Do a bunch of things to select all the properties of 
 ## the particular "widget" upon which the user has
 ## clicked.  The key id value of this widget is provided 
 ## via the "widgetid" property, accessed via the CGI.pm
 ## query object.

 $output .= $q->end_html();

 return $output;
   }

  CGI::Application takes care of implementing the new() and the run() methods.  Notice that at no point do you call print() to send any output to STDOUT.  Instead, all output is returned as a scalar.
  CGI::Application's most significant contribution is in managing the application state.  Notice that all which is needed to push the application forward is to set the value of a HTML form parameter 'rm' to the value of the "run mode" you wish to handle the form submission.  This is the key to CGI::Application.

ABSTRACT
  The guiding philosophy behind CGI::Application is that a web-based application can be organized into a specific set of "Run-Modes." Each Run-Mode is roughly analogous to a single screen (a form, some output, etc.).  All the Run-Modes are managed by a single "Application Module" which is a Perl module.  In your web server's document space there is an "Instance Script" which is called by the web server as a CGI (or an Apache::Registry script if you're using Apache + mod_perl).
  This methodology is an inversion of the "Embedded" philosophy (ASP, JSP, EmbPerl, Mason, etc.) in which there are "pages" for each state of the application, and the page drives functionality.  In CGI::Application, form follows function -- the Application Module drives pages, and the code for a single application is in one place; not spread out over multiple "pages".  If you feel that Embedded architectures are confusing, unorganized, difficult to design and difficult to manage, CGI::Application is the methodology for you!
  Apache is NOT a requirement for CGI::Application.  Web applications based on CGI::Application will run equally well on NT/IIS or any other CGI-compatible environment.  CGI::Application-based applications are, however, ripe for use on Apache/mod_perl servers, as they naturally encourage Good Programming Practices.  As always, use strict!

================================================
In Apache, you can put something like this in your server config, virtual host config, directory config, or .htaccess
file:
   ErrorDocument 500 /internal_error.pl
 ...or....
   ErrorDocument 500 /path/to/some/static/file.html

Pointing ErrorDocument to a CGI can automatically inform of an error, or make a note of failures.
 
 
Examples of usage home - top of the page -

- http://summersault.com/software/cascade/ -
- CGI::Application::Mailform -
- CGI::Application::MailPage -