|There's more than one way to do things|
CGI::Prototype leverages objects for web app controlby metaperl (Curate)
|on Aug 07, 2009 at 10:54 UTC||Need Help??|
UPDATE - my ignorance of Catalyst dispatch chaining led to this post. Catalyst certainly has a way to do this, even if it is not obvious to me.
I've spent the last 2 days staring at as many CGI / webapp frameworks as I could:merlyn speaks of in his DESCRIPTION to CGI::Prototype -
being a fan of clean MVC designs, the classes become the controllers ... and CGI::Prototype a sort of "archetypal" controller.Now, from this point, we are going to look at the CGI::Prototype controller. Then I will discuss the power of such a controller theoretically. Then I will give a concrete example of why it is important that controllers be in a hierarchy of classes.
the CGI::Prototype controllerWhen you look at the sub below, just notice one line:
the power of this methodWell, what is obvious about this method is that because every controller is a class, you can refine the behavior of your class by building classes which override/augment/before/after/mixin any of the methods you see above!
I said to focus on $next_page->render because that is what I will demonstrate from a practical perspective. But as you can see any aspect of the HTTP response cycle can be handled in an object-oriented fashion.
I stared in utter confusion at every single framework above. I looked at Catalyst subroutines, Jifty actions, Mason components, and so on. And in each case, they seemed to miss the magick of merlyn's methodic approach to web application control.
With CGI::Prototype, it is very easy to create a "family" of actions and have them derive behavior from each other.
But enough talk. Let me provide a simple concrete example.
object-oriented page manipulation in CGI::PrototypeLet's just take a simple series of pages to process user logins.
present a login pageWe first present the login page. After the controller has run and decided to dispatch to the login page, we eventually get to
which calls the engine method:
And this page is quite simple. It finds the "template file" for logging in. hen it removes all the data validation HTML from it.
the users information is submittedSo after the information is submitted, a processing page will respond with a "validation error page" or "welcome page".
the validation error pageIf there was an issue with validating the user data, we can simply inherit the "template" method from Login::Base above. Then our engine renders the validation errors instead of snipping out the validation HTML.
the object-oriented re-useWe had a new rendering action but did not re-specify what template file to use. Because everything in CGI::Protototype is class-based, this was easy.
the login successful pageon a successful login, we still want to snip the validation data... so a $self->SUPER::engine call handles that. Then we do some other things to welcome the user.
ConclusionThis was a simple example of how rendering logic for a family of pages can be achieved in an Object-oriented fashion with CGI::Prototype. The closest thing to CGIP on CPAN is CGI::Application. Having a linear set of subroutines does not afford the same degree of re-use that I have shown here with classes.
Of course there are other logic tasks in a CGI application - dispatch, authentication, authorization, to name a few. But having had experience with many CGI / webapp frameworks, I think merlyn describes exactly how CGI::Prototype fits into the picture perfectly:
Create a CGI application by subclassingand I think it has the best setup for pluggable expansion into these other tasks as well.
Of course, it needs a Moose rewrite...
A Horror Storywe had a k0der in our IT department. Whats a k0der? It's a person who believes in writing code instead of downloading CPAN modules (he had mappings from 2-letter state names to full state names copy-pasted in 4 or 5 places instead of using Locale::US). It's a person who has not heard of MVC. It's a person who uses if-then instead of object-oriented dispatch for complexity management. It's person who doesnt know about database normalization and brushes you off with a huff when you try to talk to them about it.
Now what happened? I had written a few CGI apps using CGI::Application. Then they turned the k0der loose to do his own. And guess what? He stuck everything for a particular CGI action in each subroutine. Now we have a 300k .pm file.
Now, refactoring this is going to lead to merge conflicts. If he had been forced into the CGI::Prototype mode of "creating an application by subclassing" he would have had to partition his spaghetti brainfuck into separate modules and we could have a developer per module and no merge conflicts to sort out.
CGI::Application was a great step away from the HTML::Mason / Embperl approach to web application flow control. In my opinion, the next improved step is CGI::Prototype, having done my best to understand the merits of other CGI / web app frameworks, be they big or small, popular or unpopular!