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

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

I've had a good look through the docs for CGI::Application and I'm still left wondering - why?? Instead of having one great big script with if/then/else control branching this module merely seems to be pushing everything into a module which contains all the runmodes and subs for the runmodes. Surely this will result in a .pm file at least as big as the original .cgi ?? If so, what's the advantage? Where's the modularity in all this?

Replies are listed 'Best First'.
Re: CGI::Application - why?
by perrin (Chancellor) on May 15, 2006 at 00:58 UTC
    You might as well ask why bother making subroutines, or why bother using OO, since the answers are essentially the same. To give you a real world example, at my work we have built a base class that automates the common pattern of show the form, check for errors, show the errors or process the input, and show a confirmation page. We can add new forms with all of that functionality by filling in a couple of methods (the validation profile and the processing for successful input). We also got a bunch of this for free by using CGI::Application::Plugin::ValidateRM.

    Usually, OO and modular code are not about reducing code size. They are about isolating pieces of the code from each other and gaining reuse. Using a module for your web app will help a lot with that.

      I wanted to be able to factor my web applications into many moudules, not one which is just as big as the original CGI script. The CGI::Application examples seem to focus on getting everything into one big module so where's the modularity? I'd like to have my runmodes in distinct modules, not simply subs within one monolithic module/script.
        CGI::Application is intended to help you map HTTP requests to method calls. You can use that however you like. A common approach is to make a set of related actions into one application, and then factor out all the common code into separate modules, reducing the actual runmode code as much as possible.

        If you prefer, you can make every single action call a different module, but that would probably be massive overkill, especially since one runmode frequently calls another (e.g. if there are input errors you call the mode to show the form again, passing the errors along).

        So then do that. CGI::Application does not prevent it.
        Well, that's just what many CGI::Application users do.

        Since I started using CGI::Application a little over two years ago I find that I can get massive code re-use. Using things like Class::DBI and the many CGI::Application plug-ins now available I find that I can produce a complete web application in as little as 50 or 60 lines of code. Plus the HTML templates of course. By combining HTML::Template (I know, everybody tells me to use Template::Toolkit, but I am comfortable with HTML::Template), the session management and authenticaion plug-ins, CSS and a collection of common support modules, I have been known to produce complete 10 form applications inside a day.

        Maintenance time is drastically reduced as well. By factoring out all the common code into another set of modules, and having the runmodes as small as possible I find that a 20,000 line CGI::Application based product is vastly easier to maintain than its 20,000 line conventional CGI predecessor which I still maintain for one client.

        jdtoronto

        I found that CGI::Prototype did a good job of raising the abstraction that CGI::Application started towards. When you develop a web app under CGIP, you dispatch to modules not subroutines. And the dispatch mechanism is more general: it can be based on anything that you want: GET versus POST, any particular parameter, etc.
        Did you read the nodes in my reply to your original post? In them, I discuss breaking a large CGI application into related sub-applications.

        My criteria for good software:
        1. Does it work?
        2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: CGI::Application - why?
by dragonchild (Archbishop) on May 15, 2006 at 00:32 UTC
Re: CGI::Application - why?
by xdg (Monsignor) on May 15, 2006 at 02:08 UTC

    Is the real question why use modular code for writing CGI scripts instead of one big script? Or is your question specific to CGI::Application?

    In the general sense, modular code for CGI has the same benefits of reusability and abstraction as modular code for anything else. Most of the popular CGI builders and frameworks take that approach, just like CGI::Application, e.g. Catalyst, CGI::Prototype, Gantry, Jifty, Maypole, just to name a few (alphabetically), and there are many more.

    Specific to CGI::Application (and other things, too, really) -- why have the .cgi just a thin wrapper around a module? I think that's mostly because you need an executable of some sort and .pm files aren't usually structured to work that way. It's possible -- see How a script becomes a module -- but doesn't really save you much code.

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: CGI::Application - why?
by cees (Curate) on May 15, 2006 at 21:12 UTC

    There is lots of good info in the comments above, but I thought I would try to explain it with an example.

    Here is the old fashioned if/elsif/else style:

    use CGI qw(:standard); $page = param('page'); # do some header stuff here if ($page eq 'list_products') { # deal with listing products print "......" } elsif ($page eq 'view_product') { # deal with viewing products } elsif ($page eq 'edit_product') { # deal with editing products } elsif ($page eq 'save_product') { # deal with saving product changes } else { # display a default page } # do some footer stuff here

    One thing that is difficult to do in that construct is to redirect to a different page. Say a request comes in to save the changes to a product, but there is a validation error, so you want to show the edit_product page again with some error messages. With the if/else construct that is difficult, unless we put all the individual pages into subroutines:

    if ($page eq 'list_products') { list_products(); } elsif ($page eq 'view_product') { view_products(); } elsif ($page eq 'edit_product') { edit_product(); } elsif ($page eq 'save_product') { save_product(); } else { start(); } sub list_products { print '...'; }

    Now we can call edit_product() from our save_product subroutine to redirect to another page. Now if you ever see something like this in your code, you should really look at using a dispatch table. It removes all the if/elsif stuff, and converts it into a single if statement:

    %pages = ( list_products => \&list_products, view_product => \&view_product, edit_product => \&edit_product, save_product => \&save_product, default => \&start, ); if ($pages{$page}) { $pages{$page}->(); } else { $pages{default}->(); } sub list_products { print '...'; }

    The dispatch table is much easier to manage, and it doesn't have to test every single possible page in a conditional, since it just does a hash lookup.

    Now if you make it this far, then it is just a simple step to migrate to CGI::Application.

    use base qw(CGI::Application); sub setup { my $self = shift; $self->run_modes( list_products => 'list_products', view_product => 'view_product', edit_product => 'edit_product', save_product => 'save_product', start => 'start', ); } sub list_products { my $self = shift; return '...'; }

    Your functions become methods, and instead of printing to STDOUT, you return the page contents at the end of your runmode method. You still define your dispatch table, but now CGI::Application worries about dispatching to the right method for you. There is even a way to get rid of the dispatch table completely, if you use the CGI::Application::Plugin::AutoRunmode plugin:

    use base qw(CGI::Application); use CGI::Application::Plugin::AutoRunmode; sub list_products :RunMode { my $self = shift; return '...'; }

    Now we simply use an attribute to mark which methods are valid runmodes.

    There are other things that you can benefit from. There are a whole slew of plugins that make a lot of tedious tasks very easy. For example, the ValidateRM plugin which was already mentioned. Also, if you want Sessions, just add use CGI::Application::Plugin::Session to the top of your module, and then anywhere in your runmode methods you can call $self->session which will return you a CGI::Session object. Cookies are automatically sent for you, and sessions are created on demand (ie the first time you call $self->session so there is no wasted effort if sessions are not used on a request).

    There are countless other examples (search CPAN for CGI::Application and you will see lots of other plugins.

    The reason you can benefit from all this when using CGI::Application is because you are using a common way to organize your application. In other words, you can look at CGI::Applicaton as being an open standard on how to organize and build a web application in perl (there are lots of others as well like Catalyst, Mason, etc...). The important bit is that you are doing things the same was as many others are. That means that you can share ideas and code (ie plugins) with countless other people. If you go your own route, you have to build all that integration yourself, and you are unlikely to be able to easily share anything you build with other, since they probably aren't following your application structure.

    It is worth the effort to figure out why people are doing things this way. In the end you will benefit a great deal from the hard work of others that have run into the same things that you will undoubtedly run into.

    Updated: fixed a couple spelling and grammar mistakes.

Re: CGI::Application - why?
by skx (Parson) on May 15, 2006 at 08:56 UTC

    In addition to the fine replies pointing at earlier discussion on this subject, and the fact that you can choose how modular you make your code yourself.

    One advantage is that it becomes very simple to extend your application via hooks. Allowing things to be done very easily to modify your application.

    A great example of that is validating the HTML output of your CGI::Application - something that would be pretty difficult to do if you weren't using the structured C:A framework. (Where you're "forced" to return your text, rather than printing it out piecemeal.)

    Steve
    --
Re: CGI::Application - why?
by ronmaho249 (Scribe) on May 16, 2006 at 14:13 UTC
    Another point that often gets lost is that CGI::App promotes proper coding practices for using your CGI scripts as mod_perl Registry scripts. It discourages use of global variables, which we all know can be a problem under Registry. Also, by putting your code in a module, you don't get inner subroutine issues that can crop up under Registry when running the big .cgi script.