Tag: perl
Class::DBI
I was reading a thread on the cgiapp mailing list today from several of the core
developers about developing a book on CGI::Application
. In it, several mentioned
that it might/should center around CGI::App
and a handful of oft-used modules.
One of those modules is
Class::DBI.
I took a gander at Class::DBI
over at CPAN, and it looks absolutely amazing,
and at the same time perhaps too abstract. Basically, you create a number of
packages and/or packages, one for each table you'll be using in your
application, and one to establish your basic connection. Then, each package
creates an object instance of the connection, and defines a number of
properties: the name of the table, the columns you'll be using, and then the
relations it has to other tables (
has_a( col_name => 'Package::Name'); has_many( col_name => 'Package::Name'); might_have(col_name => 'Package::Name');
) etc.
Then you use the module/packages you need in your script, and you can then use object-oriented notation to do things like insert rows, update rows, search a table, select rows, etc. And it looks fairly natural.
I like the idea of data abstraction like this. I see a couple issues, however:
- I don't like the idea of one package per table; that gets so abstract as to make development come to a stand-still, especially during initial development. However, once development is sufficiently advanced, I could see doing this, particularly for large projects; it could vastly simplify many regular DBI calls.
- I like using SQL. If I need to debug why something isn't working when I interact with the database, I want to have absolute control over the language. Abstracting the SQL means I don't have that fine-grained control that helps me debug.
So, for now, I'll stick with straight DBI…. but this is an interesting avenue to explore.
use autouse ... or not
Due to my cursory reading in the Perl Cookbook, 2nd Edition, earlier this
week, I've been investigating the use autouse
pragma, to see if it will
indeed solve my issue of wanting to use different modules based on the current
situation. Unfortunately, I cannot find any documentation on it in perldoc
.
I remember seeing something about wrapping this stuff into a BEGIN
block, but
that would require knowing certain information immediately, and I might need
the code to work through some steps before getting there.
Fortunately, this node just appeared on Perl Monks today, and I got to see other ways of doing it:
- The
if
module lets you do something likeuse if $type eq 'x', "Some::Module";
However,$type
must be known at compile time (i.e., it's based on system info or on@ARGV
); this probably wouldn't work in a web-based application. - Use
require
andimport
instead:if $type wq 'ex') { require Some::Module; Some::Module->import if Some::Module->can("import"); }
If your module doesn't export anything, you can even omit the call toimport
. - Use an
eval
:if ($type eq 'x') { eval "use Some::Module"; }
This gets around theimport
problem, but could possibly run into other compile time issues.
So, basically, I already had the tools to do the job; just needed to examine the problem more.
More CGI::App research... Try the manual!
So, I'm a bit of an idiot… it's been so long since I looked at CGI::App
, and
yet I felt I had such a grasp on it, that I overlooked the obvious step: look
at the manual!
In particular, there's a whole series of methods that are used to tailor
CGI:App
to your particular needs, and these include cgiapp_init()
,
cgiapp_prerun()
, and cgiapp_postrun()
.
-
cgiapp_init() is used to perform application specific initialization
behaviour, and is called immediately before the
setup()
method. It can be used to load settings from elsewhere; if it were called only from a superclass from which other modules inherited, it would then provide common settings for all modules. -
cgiapp_prerun() is called immediately before the selected run-mode. If it
were called only by your superclass, you could perform items such as
authorization or even form validation; this would then be standard for all
your applications. (You can use the
$self->prerun_mode('mode')
call to to override the selected run-mode, for instance, thus allowing you to redirect to a different mode if a user isn't permitted there.) - cgiapp_postrun() is called after the run-mode has returned its output, but before http headers have been generated or anything sent to the web browser. Again, if defined in a superclass, it means that you could then place the run-mode output in a specific place within a larger template, and even call other routines to fill in other parts of the main template. You could even check to see if certain parameters were passed to the page, and change the type of output you send back (XML, PDF, image, etc.), allowing you to have a common query element that changes the output type (e.g., a 'print' parameter that returns a PDF or a stripped down template).
In addition, you could specify in the superclass that you're using
CGI::Simple
for the query object (using the cgiapp_get_query
method), or
you could rewrite the load_tmpl()
method to use Template::Toolkit
or some
other templating system, etc.
Doesn't look so crazy anymore…
CGI::Application Research
I've been wanting to redevelop my home website for some time using
CGI::Application
. The last time I rewrote it from PHP to perl, I developed
something that was basically a subset of the things CGI::App
does, and those
things weren't done nearly as well.
The problem I've been running into has to do with having sidebar content, and
wanting to run basically a variety of applications. I want to have a
WikiWikiWeb, a photo gallery, some mail forms, and an article database/blog;
CGI::App
-based modules for each of these all exist. But I want them all to
utilize the same sidebar content, as well — and that sidebar content may vary
based on the user.
My interest got sparked by this node on
Perl Monks. The author tells of an acquaintance who goes
by the rule that a CGI::App
should have 10-12 states at most; more than that,
and you need to either break it apart or rethink your design. And all CGI::App
s
inherit from a common superclass, so that they share the same DB connections,
templates, etc.
So, I've been investigating this problem. One node on PM
notes that his ISP uses CGI::App
with hundreds of run modes spread across
many applications; they created a module for session management and access
control that calls use base CGI::Application
; each aplication then calls
use base Control
, and they all automatically have that same session
management and access, as well as CGI::Application
.
Another node mentions the
same thing, but gives a little more detail. That author writes a module per
application, each inheriting from a super class: UserManager.pm
, Survey.pm
,
RSS.pm
, Search.pm
, etc. You create an API for that super class, and each
CGI::App
utilizes that API to do its work.
This also seems to be the idea behind CheesePizza,
a CGI::App
-based framework for building applications. (All pizzas start out
as cheese pizzas; you simply add ingredients.) The problem with that, though,
is that I have to learn another framework on top of CGI::App
, instead of
intuiting my own.
But how do I write the superclass? Going back to the original node that sparked
my interest, I found a later reply that described how you
do this. The big key is that you override the print
method — this allows you
to customize the output, and from here you could call functions that create
your sidebar blocks, and output the content of the CGI::App
you just called in
a main content area of your template.
Grist for the mill…