On this list there was just a thread which brought to light how 
people access databases through CGI::Application. There were three primary
groups of answers:
 1. Custom SQL called directly from the CGI::Application project. 
 2. Using custom  "Data Access Objects" to abstract the issue 
 3. Using Class::DBI as a standard solution.

I'd like to discuss this issue a little more broadly: What's an
effective way to to structure your application once you realize it makes  
sense to move some logic into helper modules? Munging data as it comes 
in and out of the database is probably the most common need for this.

Here are some approaches I can think of:

1. A purely orthogonal module

Here, the helper module has no dependency or relation to
CGI::Application. Most other modules you might employ from CPAN would
fall into this category. For example, using CGI::Simple, SQL::Abstract
or Data::Grouper.

In many ways, this is the ideal setup, because the lack of
interdependence reduces the overall complexity of your application. 

Unfortunately, writing this of module for a custom solution can tedious
if there /are/ a lot of interdependencies you want to consider, such as
sharing configuration data, a database handle, session information or
CGI query parameters. 


2. A plug-in module.

An example of a plug-in module for CGI::Application is
CGI::Application::Plugin::ValidateRM.

This technique is technically known as creating a "mix-in" method,
although I think "plug-in" is a better name. 

Plug-ins nave a number of benefits:

 - They have direct access to your CGI::Application object, 
   possibly including a database handle, configuration variables,
   session information and the query object.

 - They are easy to write using standard, simple Exporter techniques.

For these reasons, it's very convenient to write your own 'plug-ins'
when you want to share some extra CGI::App methods between 2 or
applications. I use this technique frequently in large applications.

When writing plug-in methods, I suggest following the advice of the
'Exporter' docs and don't export the methods by default. This will
make it easier to trace the origins of where the new methods came
from. 

However, sometimes a plug-in module just isn't a good fit. What you need
is not more methods for a CGI::App object, but a new kind of object with
it's own identity. 

3. An OO module with a wrapper interface

[ I'm still evaluating this approach and would like to feedback on the
idea. ]

The problem here is that you want to write a related custom OO module,
but you want easy access to a number of parts of your CGI::App object
such as the database handle, configuration settings, session information 
and the query object. 

There are a couple parts to this problem:
 A. It would be a pain to pass all 4 of these elements to an object
   constructor each time, and making them global variables would only
   make things messier. (I've tried that!).  

 B. You want easy access methods for the session, database handle, and
 config variables, like the CGI::Application plug-ins can provide. 

Here's one idea for addressing "A", the constructor issue. Let's say 
we've got a skatepark object. We may want a constructor that looks like
this:

 my $sk8_obj = Skatepark->new( id => 29 );

We pass in the extra information we want automatically by using a
wrapper method:

 my $sk8_obj = $self->init_skatepark( id => 29 );

The wrapper would simply pass additional info into the constructor. 
This keeps the Skatepark object independent of CGI::Application,
allowing it to be easily re-used by other front-ends. Our wrapper
method might look like this:

sub init_skatepark {
        my $self = shift;

        require Skatepark;
        return Skatepark->new(
                dbh     => $self->dbh,
                cfg         => $self->cfg,      
                session => $self->session,
                query   => $self->query,
                @_,
        );
}

Now, here's a stab at making the data easy to access from the plugin class.
I simply use Class::MakeMethods to care of creating these simple methods for me:

 package Skatepark;
 use Class::MakeMethods::Standard::Hash (
     scalar => 'dbh',
     scalar => 'cfg',
     scalar => 'session',
     scalar => 'query',
 );
 
 sub new {
     my $callee = shift;
     my %args = @_;
     my $self = bless { %args }, (ref $callee || $callee);
 
     $self->dbh(     $args{dbh}     );
     $self->cfg(     $args{cfg}     );
     $self->session( $args{session} );
     $self->query(   $args{query}   );
 
     return $self;
 }

####

There is a slight "gotcha" hidden in this code. The "cfg" methods does
not usually work like others. You have to careful of this when using it
in the plug-in, or add a compatible method to the plug-in module. 


I think with these 3 major patterns, most any "helper module" design
problem can be solved for a CGI::Application based projects. 


What Not To Do
------------------------

1. Global variables.

Before I began using these techniques, I tried another. I made these
things global variables: ($DBH, %CFG, $SES, %FORM). What's the harm I
thought? I'll treat them as "read only", and thus won't run into many of
the problems of global variable abuse.

First, some exceptions to the "read only" policy snuck in, despite my
good intentions. Next, how was I going to get these global variables
into the helper modules? The options didn't look ideal:

 - I could declare the helper module to be the same in the same name space
   as the parent module. This creates dependence and reduces usability. 

 - I could 'use' the parent module from the helper module. This creates   
   a circular dependency, which not only inter-dependent, but it takes
   greater mental effort just to consider debugging it. 

It took me years to fully recover from "a few read only globals
variables won't hurt". Learn from my mistakes. 

2. Re-process the CGI object everywhere.  

I was optimizing some code that used this construct a lot:

 my %FORM = $self->query->Vars;

Instead of passing around the CGI query parameters I needed, I just
relied on having the query object handy, and re-created a convenient
hash of the query parameters. It turns out this is an expensive
operation in CGI.pm, and it was a primary source of slowness in my code. 

As one routine called another, each was re-processing the CGI params,
so that some lower level CGI.pm functions were being called thousands of
times for a single request. Oops!

Instead, the code could be made clearer by explicitly passing around the
form parameters being used, or least a reference to the %FORM hash.
Finally, just using the OO syntax would avoid this penalty:

        $q->param('my_field_name');

I also looked into using CGI::Simple for some frequently accessed cases,
to speed up the parameter parsing even more. It did provide a noticeable
improvement, although there are edge cases of (mostly documented) incompatibility to
consider.

        Mark







---------------------------------------------------------------------
Web Archive:  http://www.mail-archive.com/[EMAIL PROTECTED]/
              http://marc.theaimsgroup.com/?l=cgiapp&r=1&w=2
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to