Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

CGI::Prototype leverages objects for web app control

by metaperl (Curate)
on Aug 07, 2009 at 10:54 UTC ( #786745=perlmeditation: print w/ replies, xml ) 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:

And basically all of them seem to miss what we see 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 controller

When you look at the sub below, just notice one line:
$next_page->render;
sub activate { my $self = shift; eval { $self->prototype_enter; $self->app_enter; my $this_page = $self->dispatch; $this_page->control_enter; $this_page->respond_enter; my $next_page = $this_page->respond; $this_page->respond_leave; if ($this_page ne $next_page) { $this_page->control_leave; $next_page->control_enter; } $next_page->render_enter; $next_page->render; $next_page->render_leave; $next_page->control_leave; $self->app_leave; $self->prototype_leave; }; $self->error($@) if $@; # failed something, go to safe mode }

the power of this method

Well, 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::Prototype

Let's just take a simple series of pages to process user logins.
  1. The user is presented with a login page
  2. The user's login information is submitted
  3. If it is valid, then present the main page of site. Else, present a page with login failure feedback (invalid fields, etc)

present a login page

We first present the login page. After the controller has run and decided to dispatch to the login page, we eventually get to
->render
which calls the engine method:
package Gimble::Page::Login::Base; use base qw(Gimble::Page::Base); sub engine { my ($self,$tree) = @_; $self->snip_validate($tree); $tree } sub template { require html::login; html::login->new } 1;
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 submitted

So after the information is submitted, a processing page will respond with a "validation error page" or "welcome page".

the validation error page

If 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-use

We 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 page

on 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.

Conclusion

This 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 subclassing
and 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 Story

we 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!

Comment on CGI::Prototype leverages objects for web app control
Select or Download Code
I think Catalyst can do it.
by metaperl (Curate) on Aug 07, 2009 at 12:10 UTC
    I think Catalyst chaining is an example of a modern way of doing what I described with CGI::Prototype.

    It's not as obvious as the CGI::Prototype from my perspective. But given the user support for Catalyst, there is always help getting started.

different classes dont imply different files
by metaperl (Curate) on Aug 07, 2009 at 14:35 UTC
    actually k0der could've messed up under CGIP as well.. he simply would have to put all his classes in the same file!
Re: CGI::Prototype leverages objects for web app control
by merlyn (Sage) on Aug 07, 2009 at 14:53 UTC
    Thanks for "getting it" about why I did CGIP.

    And yes, had I started today, I would have done it all with Moose... a lot of things that I had to lean pretty heavily on Class::Prototyped for, almost to the point of breaking it, would have been simpler in Moose. In fact, I remember asking for prototype-style inheritance in Moose, to be told "it's possible, but we haven't done it yet". But that was two years ago.

    If I wasn't so busy pushing Seaside for my newest web apps, I'd still be building on CGIP.

    -- Randal L. Schwartz, Perl hacker

    The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

      Wow, Seaside website is so messed up, they broke scrolling with javascript
      When can you release new version?
        I have forked CGI::Prototype and wrote a Moose version that I wanted merged back into the distro, but merlyn refuses to have a standard Moose-based CGI version of CGIP in the distro - he said it had to use prototype OO.

        As such, I have reserved the namespace CGI::Class but not released anything to CPAN.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://786745]
Approved by Old_Gray_Bear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (12)
As of 2014-09-02 21:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite cookbook is:










    Results (30 votes), past polls