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

Hello Everyone:

I have recently been updating my CGI::Application program and it has grown enormous (for me at least), coming in at just shy of 3000 lines. Since it is just one file it is becoming a PITA to maintain. I am using around 20 perl modules (a lot of them are CGI::App::Plugins) and have a lot of various run modes.

I would like to break up the script into several parts so that I am not loading a lot of unnecssary modules and reading all those functions each time a webpage is loaded. However, I have not written a program this large that I need to break it up before. What my idea would be to have a main file with all the various run modes. Then in the run mode function it would load the modules that contains all that functionality (via require?). In pseudo code I want to do this:

package MyCGIApp; use base 'CGI::Application'; use CGI::Application::Plugin::AutoRunmode; sub setup { ... load database ... ... load session ... ... other setup stuff... } sub runmode1 : Runmode { my $html_output = ''; $html_output .= $q->start_html(-title=>'petnuch.com', -style=>{-src=>'/css/layout.css'}); $html_output .= $self->banner; $html_output .= $self->menu; require 'MyCGIApp::Runmode1'; # do_runmode1 is defined in MyCGIApp::Runmode1 $html_output .= $self->do_runmode1; $html_ouput .= $self->footer; return $html_output; } /* Common functions to every web page */ sub banner { ... } sub menu { ... } sub footer { ... } 1;

But what do I need to put in Runmode1.pm? Specifically what do need to do with ISA. Is it a CGI::Application or a MyCGIApp. Do I need to export do_runmode1? Do I need to have a 'use CGI::Application::Plugin::Session' in each runmode modules, or will it automatically be loaded? This is what I have in pseudocode (that does not work):

package MyCGIApp::Runmode1; use strict; use Runmode1specficmodules; sub do_runmode1 { my $self = shift; my $session = $self->session; my $q = $self->query(); my $id = $q->param('n'); unless( somecondition ) return $self->error; ...do stuff... return $html_output; } sub error { ...runmode1 error stuff... } 1;

All help will be greatly appreciated!

Replies are listed 'Best First'.
Re: Breaking up a CGI::Application program
by jdtoronto (Prior) on May 31, 2006 at 20:26 UTC
    CGI::Application has been discussed at length in the monastery - there are many useful nodes to be found.

    You should also consult the WIKI to be found here. There is also an excellent mailing list, I can't recall who hosts it but I am sure it is detailed on the WIKI.

    Richard Dice has made available his excellent extended framework which is based on CGI::Application, you can find it on CPAN at CGI::Application::Framework, it is well worth some study to see how an extensive framework can be built using CGI::A.

    jdtoronto

      The CAF looks interesting (but perhaps a lot of work). I am going to look into it. Thanks!
        Yes, it IS a lot of work. You don't need to slavishly copy what Richard has done, but it will give you some clues as to what CAN be done. I am not sure if it made it into the released framework, But Richard did a PM's talk here in Toronto a couple of years ago and the system upon which the framework is based had two totally separate CGI::App's sharing a common infrastructure - one app for the outside world, and a secure one (as I recall it) for the company to use internally. Very slick!

        jdtoronto

Re: Breaking up a CGI::Application program
by dragonchild (Archbishop) on May 31, 2006 at 20:06 UTC
    The links in Re: CGI::Application - why? should give you some ideas.

    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?

      I just checked out your node #321064. Your example there is exactly what I needed to see. Thanks so much!

Re: Breaking up a CGI::Application program
by samtregar (Abbot) on May 31, 2006 at 20:37 UTC
    A couple ideas:

    1. Move your HTML into templates. If you're working on an application that's complicated enough to need 4,000 lines of code then you're definitely ready for templates! HTML::Template is a simple way to do this, but of course there are tons of options.
    2. Move your database access into model classes. You could use something like Class::DBI or roll your own with a similar interface.

    When you're done with that I think you'll find your 4000 line app has shrunk considerably. If it's still too big then you can think about making several smaller apps that talk to each other. But don't do that till you've dealt with much more pressing concerns.

    -sam

      Hey thanks, but there is no html in the CGI::Application. Everything is already done using HTML::Template. I put the source on SVNWEB. You can check it out here if you care.

      Update: fixed the link

        This is, in my opinion, equivalent to putting HTML in your code:

        $html_output .= $q->start_html(-title=>'petnuch.com', -style=>{-src=>'/css/layout.css'} ); $html_output .= banner($self); $html_output .= menu($self); $html_output .= create_error($self); $html_output .= footer($self); $html_output .= $q->end_html();

        You can definitely shorten and simplify this code a great deal by using templates instead.

        -sam

        PS: While you're in the code - write some comments! No wonder you're finding this hard to maintain - it's 4,000 lines of code without a single hint as to what it's trying to do.

Re: Breaking up a CGI::Application program
by stonecolddevin (Parson) on May 31, 2006 at 23:30 UTC

    You know i was thinking of this exact same thing the other day.

    Some ideas I had were creating each modularized class to handle database, templates/formatting, email, etc., loading them at the init of the app, and building each page from there. This would eliminate a lot of non-abstracted SQL, HTML, and even text, as well as organize everything so as to make it much more scaleable.

    Instead of having to open up "WebApp.pm", search through 1000+ lines of code to change database connection settings, you could open up "WebApp::DBI.pm" and go right to the sub that handles database connections, wading through maybe 15 lines of code instead of the latter.

    Just my $0.02 :-)

    meh.
      One of the good reasons for using Class::DBI too! It keeps the database code all in one place, saves the agony of using placeholders and makes the whole thing a lot simpler.

      But for your particular situation dhoss there is a plugin from Mark Stosberg, CGI::Application::Plugin::DBH which puts the database initialisation in the cgiapp_init method. It even uses lazy loading so that if it is not required within a runmode the connection to the database is not made, thus saving time and bandwidth.

      samtregar also makes the very useful suggestion of using <tmpl_include ..> directives in templates, this is very useful. But don't forget to ponder the use of <tmpl_if ..> and <tmpl_unless ..> directives. Although coming dangerously close to incorporating code in the HTML, they do allow a very simple, and easily managed, method of controlling presentation of menues.

      Having said all of that, CGI::Application works best in a mod_perl environment when it gets to big applications. But that's the subject of another whole thread I think.

      jdtoronto

        CGI::Application::Plugin::DBH is quite nice, but i find myself doing a lot of SQL by hand that i shouldn't, especially since i'm not as versed in SQL as i should be for some of the queries i try and do.

        That's where Class::DBI or DBIx::Class comes in nice and handy, although I have trouble understanding how it handles relationships, but, that's a topic for another thread :-)

        meh.