http://qs1969.pair.com?node_id=410801

punkish has asked for the wisdom of the Perl Monks concerning the following question:

Wise siblings,

Forgive me in advance for flogging a moribund equine, but as long as there are us n00bs (and may there be many, because that means new users of an old language are still emerging), we will continue to want light shed on old darkness. I have read the following --

To my relief, I have discovered that I already have been doing much of what CGI-App preaches --

I have a "run mode" param -- I typically call it "do" or "action". I also use a "target," as in "do" something to "target". This is based on my premise that all imperative sentences require at least a verb ("do") and an object ("target").

I have a "dispatch table" which maps a run mode to a sub-routine.

I separate the code from the display by using my much-loved HTML-Template.

Herein lies the problem -- CGI-App does the same things as above, and yet, I find my method cumbersome. The reason is -- I really, really want to break up my code into "one file for one run mode" because I find it very difficult to work with one, very large file -- be it a single index.cgi, or a CGI-App style Perl module.

Why don't I want to use CGI-App? Many reasons --

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.

What I can't figure out is how to migrate all the common stuff to one-single external file. For example, some CGI params are common to all the scripts, so I would like to grab them all in just one place. However, most scripts will have CGI params specific to just themselves. Of course, I would like those to remain within the respective scripts. Similarly, some H-T params are common to all display screens, and I would like those to be set in one place, and then have the scripts add to the table of template params as and where needed. This is the portion I can't figure out. In other words, how to set some of the CGI and H-T params in one file, and then just use them or add to them in another file.

So, to summarize , I would like --

I don't know how to proceed. I guess, it would be really helpful to see actual applications that work similarly to above (if there are any) and learn from them.

Much appreciation awaits all advice.

  • Comment on Yet another "why CGI-Application" question

Replies are listed 'Best First'.
Re: Yet another "why CGI-Application" question
by Arunbear (Prior) on Nov 28, 2004 at 16:43 UTC
    I would say leave CGI::Application alone until you improve your understanding of OO programming. What tutorials have you tried?

    Have you looked at

    Obviously, don't just read. Write code and experiment, that's the best way to learn. OOP isn't that hard, and is well worth learning.

    Update: BTW, the approach you are already using is very sensible. Here is one way to break out the run-modes (untested):

    use strict; use CGI; # or CGI::Simple my %dispatch = ( runmode1 => 'My::Package1', runmode2 => 'My::Package2', runmode3 => 'My::Package3', ); my $rm = $q->param('rm'); eval "require $dispatch{$rm}" or die "runmode handler not found"; $dispatch{$rm}->handler($q);
    In each package create a sub called handler that looks like:
    sub handler { shift; # ignore 1st arg (package name) my $q = shift; ... # do what ever runmode requires # return html output }

    May the force be with you :)

      I would say leave CGI::Application alone until you improve your understanding of OO programming.
      I was fearing that answer ;-).

      I have read through perlboot, perltoot, LPORM, the relevant chapter in "Advanced Perl Programming' (which, of all the tutorials, I found to be the most helpful, followed by), the tutorial right here on PM. I understand OO to a degree, but then, I fail to understand how it would benefit me in my own application. And, because of a lack of time, I end up doing things my own way instead of taking time to learn something new. (the same thing happens whenever I sit down to learn Python -- after ten minutes or so I fail to see the point of learning something that will help me do what I can already do using Perl).

      Something tells me that at some point I will have to learn OOP enough to actually think that way. Until then, I want to get with creating something with what I know, unless I hit an otherwise unsurmountable wall that can only be climbed with the help of OOP. (tilly's The world is not object oriented was enlightening). Until then, something tells me that OOP is worth it only for really, really large and complicated applications. I haven't encountered that largeness yet.

      Update: I do intend to learn OOP, but I want to do that via Obj-C (I bought "Cocoa Programming for Mac OS X"). In time...

        Well if you haven't yet read Damian's book, I strongly recommend that you do. IMO it is the best guide to OOP in Perl out there.
        Please do not use my post as an excuse to avoid OO.

        I can safely criticize OO's limitations because I understand it and know to use it where it makes sense. That it has limitations does not bother me - every tool in my toolbox has limitations. As long as I understand the limitations, I can prepare for them and avoid unexpected encounters of the painful kind.

        For the record, I've written 200 line programs in which OO was worthwhile and 5000 line programs in which it was not. OO is not just for really, really large and complicated applications. OO is for any case where its brand of information hiding and abstraction works in your favour. Based on experience, I strongly suspect that you have encountered plenty of cases where OO would have been worthwhile. However without knowing what that tool could do for you, you blithely accepted the problems from not using it because you didn't realize that those problems were avoidable.

        I'd be more specific but I can't be since I lack the specifics of your circumstances. This is why it is important to learn the technique and conciously try to apply it. After you try, seek feedback, think about how it worked, and refine your approach.

        Remember the saying, There is a difference between 5 years of experience and 1 year of experience, repeated 5 times. If you're always using the same approach because it is what you know and you think that you don't have time to try anything else, then you're repeating your first year of experience. Furthermore you are shortchanging yourself on time - consistently taking the effort to expose yourself to new approaches saves you time.

        Don't believe me? Research has consistently documented 100-fold differences in productivity between programmers. Research on the top programmers demonstrates that they spend less time coding, and more time on design, analyzing what they did, learning about new things, etc. If you're always launching directly into coding because it is obvious to you how to proceed, then you're guaranteeing that you're on the 1 end of that productivity scale rather than the 100 end.

        Now some of that difference is ability - you are unlikely to personally be able to become 100 times more productive. But some of it is not. How much of your time is it worth if you could potentially double your productivity? No, I'm not saying that learning OO will double your productivity, on some kinds of projects it will do more than that, on some it is useless. However the attitude that leads you to try it out, when applied consistently to different things, certainly can achieve that doubling over time. And probably over less time than you'd suspect.

Re: Yet another "why CGI-Application" question
by dragonchild (Archbishop) on Nov 29, 2004 at 15:58 UTC
    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.

      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 CGI.pm 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.

•Try CGI::Prototype
by merlyn (Sage) on Nov 28, 2004 at 16:09 UTC
    I've been juggling about 5 "top priority" items in the past few weeks... and "publish CGI::Prototype" is one of them... I hope to do that within the week. I think if you look at that, it'll solve nearly all your problems in a nice scalable reusable manner. And yes, I had looked at CGI::Application, and liked some of what it did on the outside, but was thankfully pointed to its guts, which is a monolithic mess. I did not make that same mistake for CGI::Prototype.

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.


    update: This is now uploaded. Enjoy.
      Can you explain or post a working link?
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Yet another "why CGI-Application" question
by aufflick (Deacon) on Nov 29, 2004 at 02:32 UTC
    If you are serious about spending some time to get a good app model for your web app, take a look at the combo of mason and MasonX::WebApp

    Any way you look at it, mason is a winner for web sites and thanks to Ken and Dave, the O'Reilly Mason book is available online for free at masonbook.com

    Using mason by itself will help you with the structuring you want to do quite nicely, but you will have to use it's templating.

    Then if you want a nice OO-Application structure on top, MasonX::WebApp is the money.

    Just my 2c.

Re: Yet another "why CGI-Application" question
by knoebi (Friar) on Nov 28, 2004 at 21:56 UTC
    well, if you don't understand OO programming I will go with the others, you should read Damians book, you don't even have to read through the whole book if you're starting with oo. But he explains OO like nobody else in my opinon.
    There is a module called CGI::Builder, it's only a bit more than 200 lines of code, its portable but it's OO and it is in the CGI namespace...
    I use it in most of my projects nowdays, it's really great, far more hooks then CGI::Application, which is the key if you want to break up your CGI code.
      Please look at the source code of CGI::Builder before you consider using it for production code. Keep in mind that this code came from the person who thought nothing was wrong with doing essentially eval get "http://his.machine/secret.url" in his Makefile.PL, and had to be pressured into changing his code by P5P and the CPAN administrators.

      In my professional opinion, I could never recommend code from this individual.

      -- Randal L. Schwartz, Perl hacker
      Be sure to read my standard disclaimer if this is a reply.

Re: Yet another "why CGI-Application" question
by weierophinney (Pilgrim) on Nov 29, 2004 at 15:17 UTC

    First off, to really start using CGI::Application, you will need to understand at least some rudiments of perl OOP. The first time I looked at it, I didn't understand perl OOP at all, and I quickly dismissed it. However, once I'd grasped the fundamentals of perl OOP, I went back and looked at it and liked it much more. Contrary to what merlyn says, I don't find the guts of it to be monolithic; in fact, I was shocked to discover how lean it actually is, considering what all it lets you do.

    To address some of your specific issues:

    • One script for one run mode is asking for trouble when the run modes are all linked by a common theme. What CGI::App does is have you group run modes -- so that all operations relevant to a process are in one place. However, you don't necessarily need all these run modes in the same file; that's why namespaces are so much fun -- simply load up the appropriate file with the run mode you're requesting when you need it, or load them all up when the initial module is loaded.

      The beauty of doing it like this is that if you group the application code, you know where all of it is. Additionally, using the dispatch table provided by CGI::Application, and which you setup in your class, it becomes very easy to determine what method is invoked at each point in the application -- making debugging very easy. Look up the run mode, then find the method... you're there.
    • If you want to build reusable applications, CGI::App is your friend. Build an application module once, and use it many times. You can customize your application using metadata passed via the instance script (such as template location, DBI parameters, etc).
    • You don't have to use CGI.pm; you can override the query() method to have it pull in CGI::Simple or some other module that gives you access to CGI parameters. For that matter, you aren't tied to HTML::Template, either; you can use any templating system you want (or none at all), simply by overriding the appropriate method(s).

      This kind of flexibility is one of the key strengths of CGI::Application, in my opinion.
    • If you have multiple applications, but you want them to share some common parameters -- templates, session handling, etc. -- again, CGI::Application is your friend. Build a superclass for your site that performs these actions or sets up your environment, and have all the various application modules inherit from it.

    Again, you need to have a basic understanding of how OOP works in perl, but once you do, CGI::Application becomes a breeze to utilize, and an incredibly flexible tool. It's not the be-all, end-all of web programming, but it provides a nice, lightweight framework for developing web applications.

Re: Yet another "why CGI-Application" question
by Zed_Lopez (Chaplain) on Nov 29, 2004 at 18:53 UTC

    You don't really need to understand OOP to use CGI::App. You just need the basics of how to call methods on an object. That much is easy to pick up from CGI::App examples. (Of course, understanding OOP would only help, and would become essential if you wanted to extend CGI::App.)

    What weierophinney said: use CGI::Simple instead of CGI for all of the parameter processing goodness without the bulk of the HTML creation code. Note that CGI::Session doesn't subclass CGI, but it does do an if isa CGI test, so it would need tweaking to use it with CGI::Simple. But if you're trying to avoid external modules, realize that CGI::Session uses a bunch of 'em.