Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

Re: Yet another "why CGI-Application" question

by dragonchild (Archbishop)
on Nov 29, 2004 at 15:58 UTC ( #410951=note: print w/replies, xml ) Need Help??

in reply to Yet another "why CGI-Application" question

I would like to create a mix between what dragonchild calls "clannish" and "socialist" (see Re: Re: Why CGI::Application?). One directory per functional area, and within that, one script per run mode.

*sighs* I was afraid that post was going to come back and haunt me. It was mostly tongue-in-cheek, attempting to get from "The Monolithic Script" to a C::A solution in a few easy steps. Don't read too much into it.

As for answering your question(s) ... hmmm ... In no particular order:

  • I don't want to use yet another external module ...

    Get over it. CGI::Application is pure Perl, which means it will run on any system Perl runs on. Many modules you might end up needing to install, including every database module, can't say this.

  • I don't understand how it works

    Don't bother. I didn't understand how it worked until I wanted to add something to it. Then, I did some sourcediving. All you need to know is that if you follow the published API, it will do what it promises. Period. Unless you need something more, that should be good enough. For now.

  • Seems like most of what I am already doing is what CGI-App does. And I don't like the fact that all my code gets lumped into one single file

    All your code will not be lumped into one single file. In fact, if you lump your code into one single file, you will be doing it wrong. I'll give an example a little further down.

  • using as few external modules as possible

    Then, you go on to list several external modules, some of which are the hardest to install on different systems. I have built Perl applications on some 7 different operating systems, including Solaris, Linux, and Windows. The hardest module to install? DBD::Oracle. The second hardest? DBD::mysql. The easiest? CGI::Application. I'm serious. I have NEVER had a problem installing C::A from the CPAN shell. Not once, in over 100 installs on various machines.

Now, for an example. This example assumes you understand that basic concepts of OO theory. (You don't have to understand Perl OO programming - just OO theory.)

The idea is that we have three functional areas to our application.

  • The public area, which contains stuff everyone wants. This is unsecured.
  • The member's only area, which contains stuff you have to pay for. You have to login for this.
  • The admin area. Again, you have to login for this.

A few further, somewhat arbitrary, requirements:

  • You should only ever have to login once for both member's and admin, if you're allowed in both.
  • The code should be easy to test
  • The pages should be viewable in both HTML and PDF.

Stop and think about how you'd do this in a bunch of CGI scripts. You'd have to at least have

  • a print() function, to handle the dispatch between HTML and PDF
  • a set of login functions, to handle the logging in/out of users
  • a set of cookie functions, to handle setting/getting the cookie
  • a set of session functions, to deal with the session identified by the cookie
  • a dispatching function, to dispatch to the right CGI script

Now, this is no different that with C::A - you will have to have all those same functions. And, frankly, the code will be almost the same, line for line. But, there's a difference - with the CGI script method you're proposing, you will need to make sure you call all those functions in the right order in every single file. If you want to change that order, you will need to change every single CGI script. Every single one. That's a lot of work!

With C::A, you put that kind of code in one place, and only one place. Then, C::A guarantees that the code you specify will be called before the runmode. This way, by the time you get into the runmode, you know a whole bunch of stuff has already happened. For example, you know that

  • Security has already been checked
  • The cookie has already been retrieved
  • The session has already been retrieved
  • Any database handles have already been instantiated

And, when you are done, you will also be guaranteed than any cleanup work will be done for you.

Every. Single. Time.

So, how do you get C::A to do all this magic for you? By using the power of subclassing. When I use C::A, I create a child-class, generally called Generic::Application. This is my personal C::A, modified and customized to meet my personal needs. I'll generally have 2-3 things in it:

  • That print() function, in all its glory. I prefer Template Toolkit, but HTML::Template is also good. I use PDF::Template and Excel::Template for other formats, but YMMV.
  • Basic configuration file handling. I like Config::ApacheFormat, but it doesn't work for everyone.
  • Basic cookie handling. Generally, I'm looking for bake_cookie() and read_cookie(), or something like that.
This class will not have anything in cgiapp_prerun() or cgiapp_postrun(). This is just to provide some common functionality.

Next, I'll create Specific::Application which will be a child of Generic::Application. This will be the base class for the specific web application I'm working on. In here will be the cgiapp_prerun() and cgiapp_postrun() that will generally do things like

  • set up and teardown the config files
  • set up and teardown database handles
  • set up and teardown the template object (including template paths)
  • set up and teardown the cookies / session / authentication
Each of those things may or may not be in separate methods.

Note, we haven't actually written any runmodes yet. This is all just infrastructure work - code we'll need later on down the road.

Now, remember back to our requirements - we need three areas. That sounds like we need three child classes. Specific::Application::Public, Specific::Application::Members, and Specific::Application::Admin. Each of these will also have a very basic .cgi file in the cgi-bin directory, corresponding to the last part of the classname.

Now, Specific::Application assumes that every page requires a login. We can then override that in Specific::Application::Public to say that runmodes in this class do not require a login. That's the more secure way of doing things. The admin stuff can be handled by the ::Admin class requiring that the session generated by authentication contain a certain flag set to true, indicating this is an admin user. All of this happens in the cgiapp_prerun() methods in the various classes. The same goes for cookies, databases, and the like.

Every runmode will look something like:

sub runmode { my $self = shift; my $session = $self->param( 'session' ); my $dbh = $self->param( 'dbh' ); # Do stuff here return $self->print( $template_name, $format, %parameters_to_pass_to_template_object, ); }

Everything else is handled for you. In fact, most of your developers will never need to know how things work - just that they do. Does that help?

Being right, does not endow the right to be rude; politeness costs nothing.
Being unknowing, is not the same as being stupid.
Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Replies are listed 'Best First'.
Re^2: Yet another "why CGI-Application" question
by punkish (Priest) on Nov 29, 2004 at 18:19 UTC
    Thanks much everyone. After (not-so-much) pondering, I have decided to --
    • follow a mix of Arunbear's, dragonchild's, and weierophinney's advice. I will try once more to understand a bit more of OOP, and read some of CGI-App's source (many speak highly of the framework, so there is some "capital" to be "expended," so to say).
    • Unfortunately, Damian's book will have to wait -- I have no money to buy another book -- I think I have enough in this discussion to go on forward

    dragonchild, thanks for a really detailed expounding of a really detailed expounding. I really did like your "socialist" and "clannish" nomenclature -- made more sense than other pure technical stuff (frankly, words like "class" and "inheritance" leave me cold -- esp. "polymorphism" -- that one is really frigid).

    Perl has benefited so much from its laissez-faire approach -- TMTOWTDI is a flag-bearing slogan. And that is good.

    However, I feel, Perl is used so much in web apps, and given that is a part of the core, maybe it would be beneficial to have something like CGI-App and H-T also a part of the core. Python-ers talk highly of Zope, and PHP-ers speak of Zend. As far as I can see, the closest thing Perl-ers have to a standardized "framework" is mod_perl, but that is such a pain in the behind to implement because it is tied to the webserver, and requires re-jigging your scripts more often than not. It might be worthwhile to have a web-application framework be a part of the language core -- compelte with all the services, session management, etc.

      (This is going wayyy off the original topic, but I follow where the conversation leads.)

      (frankly, words like "class" and "inheritance" leave me cold -- esp. "polymorphism" -- that one is really frigid)

      Words are nothing more than carriers of meaning. However, each person has their own understanding of each meaning, leading to the ideas of denotation (dictionary meaning) and connotation (personal meaning). Denotations tend to be very dry, as they're trying to pin down something that, frankly, cannot be pinned down. Connotations tend to be very emotional, like your reactions to certain words.

      Now, neither is good or bad. We use both in our daily lives to great effect. Just try to imagine a world without connotations - jokes would suck. Persuasive speech would be impossible. Politicians would ... well, maybe that's a reason for such world, but that's another discussion, preferably over several beers.

      To bring this back to your comments ... I would stop and look at the denotations of words that "leave you cold". Often, words leave me cold when I don't really understand what's going on behind them. Sometimes, I think that's why I didn't get that philosophy minor - too many words I didn't understand, so I didn't bother. Frankly, I'm indebted to this site for explaining many of the concepts behind the words of programming. Without that, I may have given up on excelling in my chosen Art.

      I would do some searching through the bowels of this site before accepting the cold shoulder from certain words. Personally, I'd look through the Meditations of the Saints. I'm particularly fond of tilly and tye, though all of them are excellent writers.

      Being right, does not endow the right to be rude; politeness costs nothing.
      Being unknowing, is not the same as being stupid.
      Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
      Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://410951]
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (1)
As of 2023-09-27 00:55 GMT
Find Nodes?
    Voting Booth?

    No recent polls found