(You are Anonymous)

Organizing your App

This is collection of quotes from various people from C::A's mailing list.

In a nutshell, a lot of these techniques are accomplished by subclassing, inheritance, setting up multiple instance scripts (which can be eliminated by C::A::Dispatch).

Quote 1 on multiple instance scripts and separating runmodes

> I'm writing a medium-sized web-based financial application that will have up
> to 50 run modes between presenting empty forms, saving, editing, updating,
> and deleting from them. Run modes *could* be broken down into groups, e.g.,
> these 4 deal with managing users, these 6 deal with managing project
> specifications, etc.

50 is definitely too much. There isn't a hard rule to follow for what is too much, but I think there can never be too few. I usually split them up by functionality or user authentication level. And remember that base classes are your friend here. For instance, if I have admin and normal users and each can view reports. Some reports are the same, but some are different, I would split them up into the following structure:

  • MyApp::MyBase - base class for all my app classes that might have classes to deal with configuration, database, sessions, templates, etc that they all have in common.
  • MyApp::Report - base class for reports that contains those reports that everyone can see as well as common methods used to generate reports, graphs, etc
  • MyApp::Report::Admin - app class containing admin reports
  • MyApp::Report::User - normal user reports

Most of this is personal preference, but you really need to break it into structures.

Refer to this discussion: "Good practices: how many run modes in an app" http://www.mail-archive.com/cgiapp@lists.erlbaum.net/msg04707.html and http://www.mail-archive.com/cgiapp@lists.erlbaum.net/msg04725.html

Quote 2 on multiple instance scripts and separating runmodes

> Reading the thread on 'Good practices: how many run modes in an app' it 
> is obvious my current application under development is going way over 
> the 'recommended' upper limit of rm's. I know I need to break it into 
> smaller units based around functionality, but how?

A better question to start with is: "Why?"

Reading this thread, it appears that most suggestions are cosmetic fixes which will superficially move run mode methods out of your main module and into other modules. This is useful only if your main goal is to have fewer run modes in one physical file.

Years ago, when CGI-App was first released, the question of "how many run modes should my module have?" was posed to me. My answer was to give the number of run-modes my applications typically have. That doesn't really answer the "Why separate?" question.

And what is it you're supposed to be separating, anyway?

I separate instance scripts – not run-modes. (The "Instance Script" – aka, the .pl or .cgi file.) I typically create separate instance scripts based on a few different "soft" criteria:

1. Are the functions provided to the user substantially different?
2. Do different types of users use these scripts?
3. Might I want these scripts to be called from different places?
4. Do these scripts have different "start modes"?

If a preponderance of the answers to these questions are "yes", then I create a separate instance script, and a separate module (with separate run-mode methods). The "Why?" in this case was because I could use the file and directory orientation of the Apache web server to implement different security rules, and so that I could create logical, bookmark-able (email-able) URLs which provide entry points into the respective applications.

As you can see, these questions are highly subjective (possibly with the exception of the last one). As a result, you get very different answers depending on your point of view. For example, consider the following admin functions:

* Manage users
* Manage groups
* View logs
* Manage Widgets
* Manage Orders

In my way of thinking, these are five separate applications. However, if you think of them as functions of one big "admin control panel" you might come to a very different conclusion.

HTH, - Jesse -

Quote 3 on subclass and inheritance

Let's say I want to create a web app called MyApp. The directory structure looks like this.

/MyApp
       MyBase.pm
       User.pm
       Inventory.pm
       Other.pm

The base class would be

package MyApp::MyBase;
use base 'CGI::Application';

sub cgiapp_init {...};  # common init stuff for entire application
sub cgiapp_prerun {...};
sub _some_common_code {...};

1;

Then each of the other packages will contain the run modes for itself and can call methods from the base class as long as they are setup as follows:

package MyApp::User;
use base 'MyApp::MyBase';

sub setup {  
       my $self = shift;
       $self->start_mode( 'display_users' );
       $self->tmpl_path( set_template_path_here );
          # move this to cgiapp_init() if all templates are in the same directory
       $self->run_modes( qw/ display_users display_user_form edit_user /);        
}
sub display_users {...}
sub display_user_form {...}
sub edit_user {...}
1;

Instance script user.pl:

use MyApp::User;
my $webapp = MyApp::User->new();
$webapp->run();

More discussion can be found here http://www.mail-archive.com/cgiapp@lists.erlbaum.net/msg05213.html

The example above is a small application. A complex webapp consists of multiple small applications, connected as a bigger app by linking and other methods.

More discussion on thread "How to split runmodes into different modules" http://www.mail-archive.com/cgiapp@lists.erlbaum.net/msg05121.html

Quote 4 on separating the business logic into other objects

I would break each of them [run modes] up into functional areas. My rule of thumb is never more than 12, rarely more than 6, preferably 4 or less. Anything more and I'm trying to do too much in the controller and should be offloading into business objects.

Just regarding the last part, "business objects" – we were able to define a hard rule about what was appropriate in CGI-App modules, and what was not. That rule:

CGI::Application modules are prohibited from having or using DBI database handles

This rule was simple because no subjective judgment was needed. You either did or did not have a $dbh. As the primary side effect, this rule provided a natural separation between object/business modules and front-end CGI/UI modules. If you needed to access or modify data, you had to go through another module. This had the effect of orienting all the developers towards the philosophy of separation.

That is the rule I follow as well. The only time the CGI::App object sees a DBI handle is when it needs to pass one to a plugin (like the session plugin). It never ever uses it directly.

As a simple extension of this rule, if you use an O-R mapper like Class::DBI (or Rose::DB::Object which I have been looking at lately), you automatically enforce this separation. Of course it is not necesary to use an O-R mapper, but it can simplify a great deal of your code while providing that clean separation for you.

Above from http://www.mail-archive.com/cgiapp@lists.erlbaum.net/msg04719.html