merlyn, after much ballyhoo, has released CGI::Prototype to CPAN. I decided to see what all the fuss was about, especially as he said this was an improvement over CGI::Application, which has a large following. If merlyn is going to reinvent the wheel, it's got to be something to write home about, right?

The first thing that hit me, upon reading the POD, was the version number. 0.90 for a first CPAN release? *shrugs* I've never put much stock in the big 1.00, but I know others do. Not a big deal.

The second thing was the complete lack of acknowledgement of prior art. Nowhere does he provide a rationale for a rounder wheel - he doesn't even acknowledge that there was a wheel before. This module may be the greatest thing since sliced bread, but you still have to tell me why it's a better mousetrap. Otherwise, I'm not going to give it a spin.

So, I reread the POD a little closer, and then read the source. merlyn, both in posts here and in the article referenced in the POD, mentions that he's tried C::A and found it lacking. What major improvements has he made? I think I found a bunch of differences ... it's up to you to determine if it's an improvement or not.

  1. C::A uses a subclass as a repository for runmodes. A runmode is the Controller for a given page. C::P seems to use a class as the Controller for a given page.
  2. C::A provides a number of hooks to take actions at various times. C::P also does the same thing. The names are different, but each has the same number of hooks.
  3. C::P requires the use of Template Toolkit. While TT is arguably the cadillac of templating technology, there are hundreds of reasons to stick with HTML::Template. More importantly, why can't I choose? C::A, while strongly recommending H::T, allows you to override load_tmpl(), and it's even documented how.
  4. C::A provides one loop for work, called run(). That loop calls one runmode, who is responsible for returning output. C::P provides one loop for work, called activate(). That loop calls methods in one class, then potentially calls methods in another class. The last class called is responsible for returning output from the display() method.

That last item threw me for a loop when I first read it. Why would C::P's engine loop potentially reference two classes? I realized it was for handling form input. From what I can guess, it looks like you're supposed to do something like:

  1. My::App::DisplayForm
  2. My::App::ReadForm which has a respond() of My::App::DisplayAcknowledgement

My first question was "Why?", as in "Why so complicated?". In C::A, if you want to have runmodeA use runmodeB for display, you just:

sub runmodeA { my $self = shift; # Do stuff here return $self->runmodeB(); }
Seems pretty simple, to me.

My second question immediately after was "What if my respond() wants to have a repond()\n"? In other words, why is the redispatch only one layer deep? Why wouldn't it be a loop of potential redispatch?

Lastly, I tried to redesign a medium-sized application I just finished work on a few weeks ago, taking it from C::A to C::P. And, I found I didn't want to. C::A allows me to organize runmodes into functional areas, corresponding to how the application is broken up. This means that all the pages in a certain section are in the same file. I don't have one file per page. That, to me, seems to be a huge step backwards.

And, it's not because I don't like bunches of files. It's because I cannot have standard pages across multiple applications. C::A allows me to define runmodes at any level in the class hierarchy. This means that I can have a runmode, such as a generic login form runmode, appear the same across every single application I write. This allows for easier co-branding of sites with less cost. I'm not sure how I'd do that with C::P.

Overall, I think C::P is a very well-written module by an experienced professional. But, I don't think it needed to be written. C::A already exists, it already has an excellent track record, and I personally know they have been very receptive to patches and suggestions. I would have much rather seen merlyn's experience and knowledge be used to improve C::A and take it to the next level rather than create a different color copy.

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.