You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl@perl.apache.org by Matthew Pressly <mp...@claborn.net> on 2002/01/09 21:33:28 UTC

mod_perl framework + code reuse question

Background: 
I'm working on a site that is a mix of php (which I'm gradually converting to mod_perl) and mod_perl (running under Apache::Registry and using Template Toolkit (TT2)) using an apache configuration like this:

<Directory />
    [...]
    <Files "*.par">
      SetHandler  perl-script
      PerlHandler Apache::Registry
      Options ExecCGI
      PerlSendHeader On
    </Files>
</Directory>

I took the <Files> approach because, at the time, it seemed like the easiest way to have *.php and *.par files coexist during the conversion.

Problem:
There are many *.par pages (estimate: 70-100 when conversion is complete), and they all contain the following code with minor variations that could be made consistent (like what constants are imported, what modules are "use"d, etc.).  I'd like to find a way to prevent having that code (below) show up over and over again so I can eliminate a potential maintenance headache, but I'm not sure of what's a good way to bundle it up for reuse.  

One idea I had was to write a handler that acts as a sort of minimal application framework that contains the code below and determines what perl module should be "require"d and executed based on $apache->path_info and $apache->uri.  This sounds like a substantial effort, but would provide a centralized framework where I could add features that apply to all pages by only editing code in one place.  Is this a reasonable approach?  Are there other simpler approaches?

I'd appreciate any input on how other people are structuring similar type applications in mod_perl, where the output is generated by Template Toolkit based on data obtained via SQL queries using parameters received mostly in the URL.

#--- BEGIN SAMPLE PAGE testpage.par ---

#!/usr/local/bin/perl -wT
#------------------------------------------------------------
BEGIN {
  require "$MyCompany::Basepath->{sitename}/MyCompany/ProjectName/Config/Startup.pl";
}

use Apache ();
use Apache::Constants qw(:common SERVER_ERROR REDIRECT);
use Apache::DBI;
use strict;

our $apache = Apache->request;
### I realize that I should be handling header_only requests a little differently, but code not updated yet.
$apache->content_type('text/html');
if ($apache->header_only) {
  $apache->send_http_header;
  return OK;
};
my $parm = {$apache->args, $apache->content};
my $dbi = $MyCompany::ProjectName::Config::dbi;
my $dbh = DBI->connect($dbi->{dsn}, $dbi->{user}, $dbi->{password},
                       {RaiseError => 1});

# template object is built in apache startup.perl file (before fork)
my $out = &dispatch($apache, $parm, $dbh, $MyCompany::ProjectName::template);
$apache->send_http_header;
print $out;
return OK;

sub dispatch {
        my ($apache, $parm, $dbh, $tpl) = @_;
my $template_data;
 ### [... fill $template_data with something meaningful here ...] ###

  my $output = "";
  $tpl->process('viewsurveyusers.tpl', $template_data, \$output)
    or die "Error processing template: ".$tpl->error;
  return $output;

}
#--- END SAMPLE PAGE testpage.par ---

Relevant piece of startup.perl:
#---- BEGIN ---
use Template;
$MyCompany::ProjectName::template = Template->new(INCLUDE_PATH => "tt",
                                          PRE_PROCESS => "config.tpl",
                                          PRE_CHOMP => 1,
                                          );
#--- END ---


Matthew Pressly


Re: mod_perl framework + code reuse question

Posted by Perrin Harkins <pe...@elem.com>.
> For file organization, I'm thinking of making all "page" modules start
> with a common namespace substring (e.g. Projectname::Page) to distinguish
> them from the support (model) modules

I like to name the top level modules SiteName::Control::* and the model
modules SiteName::Model::*.  Calling the modules "Page" makes it sound like
each one corresponds to a single page, which is not always true, i.e. you
might have an adress book module that generates many different pages (with
different templates) based on the parameters passed to it.

Glad to hear the handlers are working out for you.

- Perrin


Re: mod_perl framework + code reuse question

Posted by Matthew Pressly <mp...@claborn.net>.
At 01:48 PM 1/10/2002 -0500, Perrin Harkins wrote:

>You can actually use subroutines without fear!  Also, reducing the amount of
>magic (i.e. all of that package generation and eval stuff that Registry
>does) can help clear up confusion.  And you can use __END__ and __DATA__.

Good point.

I started writing a handler and have something basically working.  It's still pretty barebones, but I'm happy with it so far, and performance is good.  It "require"-s the appropriate "page" module based on $r->filename, $r->path_info, and $r->document_root then calls that module's "run" method as a class method (both steps wrapped in separate evals).  Apache::Reload is reloading both handler module and "page" module as needed.

For file organization, I'm thinking of making all "page" modules start with a common namespace substring (e.g. Projectname::Page) to distinguish them from the support (model) modules for that site and moving them into the same directory tree that contains those support modules, then moving templates (currently in ./tt/*.tpl files relative to *.par scripts) into either that directory tree alongside the modules they are used by or into an entirely separate tree that only contains templates.  The latter is probably better for sites that have some division of labor between coders and HTML designers, but that's not the case on this one.

Thank you for all the help.

Matthew Pressly


Re: mod_perl framework + code reuse question

Posted by Perrin Harkins <pe...@elem.com>.
> What are the basic advantages, disadvantages, and limitations of:
> (a) stuffing all this setup/framework code into a module (either a new
module or subclassing Apache::RegistryNG as you mention below),
> versus,
>  (b) stuffing it into a handler that all requests for a large subset of
the pages on this site have to go?

Subclassing RegistryNG is pretty much identical to making your own handler.
In both cases, you have some common code that runs on every request and then
it decides what other code should be run to handle this specific action.  In
my opinion it's clearer this way than if you make a module with the init
code and call it explicitly, but I can't think of a very good technical
argument for it.

The handler/RegistryNG approach lets you do neat things with subclassing,
like create a special subclass that does some additional setup for a certain
group of scripts.  That wouldn't be as clean if you use the init module
approach, since you'd have different scripts calling different init modules.

> Other than the speedup from reduced overhead, what are the primary
advantages
> to using handlers rather than Apache::Registry for content handlers?

You can actually use subroutines without fear!  Also, reducing the amount of
magic (i.e. all of that package generation and eval stuff that Registry
does) can help clear up confusion.  And you can use __END__ and __DATA__.

The big change in moving from Registry (or RegistryNG) to a handler is that
you have to move your code from the main part of the script into a
subroutine and turn the script into a proper module.  There's good stuff on
this in the guide.

> The primary disadvantage seems to be that I have to restart httpd an awful
> lot, but maybe Apache::Reload can help here.  Can it be used to reload
> modules that implement handlers?

Yes, it should cover your needs.

> This is a very helpful article.  I have read it several times and still
> keep coming back to it.  I would also like to learn more about the
> "model-view-controller" approach in general.

A Google search will give you tons to read, but most of it refers to Java.
It's all applicable to mod_perl though.

- Perrin


Re: mod_perl framework + code reuse question

Posted by Matthew Pressly <mp...@claborn.net>.
At 09:09 PM 1/9/2002 -0500, Perrin Harkins wrote:

>Normal Perl rules apply.  Modules are good for sharing code.  You could
>stuff the shared parts into a sub in a module that every script would
>call.  Or you could use an OO approach, with a base class that holds all
>of the boiler-plate stuff.

The question I'm having a hard time answering without going down both paths is:

What are the basic advantages, disadvantages, and limitations of:
(a) stuffing all this setup/framework code into a module (either a new module or subclassing Apache::RegistryNG as you mention below),
versus, 
 (b) stuffing it into a handler that all requests for a large subset of the pages on this site have to go?

Both approaches achieve the same result of funnelling all requests through a common set of code, which is mainly what I'm trying to achieve, but other than being different implementations, I'm not real clear on how they differ.  

It would seem like the  "put framework in a module" approach might be more flexible (esp. subclassing Apache::RegistryNG) because different scripts could take-it-or-leave-it or even modify it through further subclassing.

On the other hand, then "common framwork in a handler" approach might be faster because it could do just what is needed by all pages but nothing more, and as long as there is not too much code wasted by differences in the needs of different scripts, it could be that less code is being loaded and executed.

Am I missing other things here?

>> One idea I had was to write a handler that acts as a sort of minimal
>> application framework that contains the code below and determines what
>perl
>> module should be "require"d and executed based on $apache->path_info
>and
>> $apache->uri.
>
>That's a good way to go too.  Moving from Apache::Registry to handlers
>can be empowering.


Other than the speedup from reduced overhead, what are the primary advantages to using handlers rather than Apache::Registry for content handlers?  The primary disadvantage seems to be that I have to restart httpd an awful lot, but maybe Apache::Reload can help here.  Can it be used to reload modules that implement handlers?

>> This sounds like a substantial effort
>
>Only if your code currently depends on the CGI emulation features of
>Apache::Registry.  If it's clean code, you should be able to convert it
>without much trouble.  You could also try subclassing Apache::RegistryNG
>and adding your setup code to the beginning of each request there.

I will look into subclassing Apache::RegistryNG.  This could be useful for other things as well.

Maybe this is easier than what I'm picturing.  I was thinking that writing the handler would be most of the effort, but it may be that it is mostly just designing an appropriate interface between the handler and the page code, re-partitioning the common code out of the "pages" and into the handler, and writing a simple dispatcher.  Probably the biggest piece is the careful interface design so that future framework enhancements don't require changing old "page code".

>> I'd appreciate any input on how other people are structuring similar
>type
>> applications in mod_perl, where the output is generated by Template
>Toolkit
>> based on data obtained via SQL queries using parameters received
>mostly in
>> the URL.
>
>I use handlers, with a model-view-controller design.  The approach is
>documented in my Perl.com article about the eToys design.  I keep all
>the common setup stuff in a base class that the other controllers
>inherit from.

This is a very helpful article.  I have read it several times and still keep coming back to it.  I would also like to learn more about the "model-view-controller" approach in general.


>- Perrin

Matthew Pressly


Re: mod_perl framework + code reuse question

Posted by Perrin Harkins <pe...@elem.com>.
> There are many *.par pages (estimate: 70-100 when conversion is
complete),
> and they all contain the following code with minor variations that
could be
> made consistent (like what constants are imported, what modules are
"use"d,
> etc.).  I'd like to find a way to prevent having that code (below)
show up
> over and over again so I can eliminate a potential maintenance
headache, but
> I'm not sure of what's a good way to bundle it up for reuse.

Normal Perl rules apply.  Modules are good for sharing code.  You could
stuff the shared parts into a sub in a module that every script would
call.  Or you could use an OO approach, with a base class that holds all
of the boiler-plate stuff.

> One idea I had was to write a handler that acts as a sort of minimal
> application framework that contains the code below and determines what
perl
> module should be "require"d and executed based on $apache->path_info
and
> $apache->uri.

That's a good way to go too.  Moving from Apache::Registry to handlers
can be empowering.

> This sounds like a substantial effort

Only if your code currently depends on the CGI emulation features of
Apache::Registry.  If it's clean code, you should be able to convert it
without much trouble.  You could also try subclassing Apache::RegistryNG
and adding your setup code to the beginning of each request there.

> I'd appreciate any input on how other people are structuring similar
type
> applications in mod_perl, where the output is generated by Template
Toolkit
> based on data obtained via SQL queries using parameters received
mostly in
> the URL.

I use handlers, with a model-view-controller design.  The approach is
documented in my Perl.com article about the eToys design.  I keep all
the common setup stuff in a base class that the other controllers
inherit from.

- Perrin