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


in reply to CGI::Application - why?

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.