Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Comment on

( #3333=superdoc: print w/ replies, xml ) Need Help??

I've been taking my first steps with merlyn's CGI::Prototype, which I find pretty nice overall, but I'm running into a conceptual block here. In summary: inheritance doesn't work as I expect. Not even Class::Prototyped-type inheritance. In fact, I can't figure out how inheritance works with CGI::Prototype!

The script and module below illustrate the problem.

I'll describe the script first. Most of it is assignments and definitions. Things don't get moving until the call to activate at the very end. Users of CGI::Prototype will find this pattern familiar. They will also recognize the methods defined in package Zero in the middle block as being among those called by CGI::Prototype::activate.

# cgi.pl use strict; use warnings; @One::ISA = @Two::ISA = 'Zero'; { package Zero; use base 'CGI::' . shift; sub dispatch { shift->param ? 'Two' : 'One'; } sub render { print my $self = shift, "\n"; print "$_: " . $self->param( $_ ) . "\n" for $self->param; } } @MY_Zero::ISA = 'Zero'; shift->activate; __END__

Passing the string 'Prototype' as the first argument to the script will cause Zero to be a subclass of CGI::Prototype. (The package I define below is called CGI::Classic, so 'Classic' is another meaningful value to pass as the first argument to the script.)

The dispatch method, which is called by the inherited activate method, returns 'One' if there are no CGI parameters, and 'Two' otherwise. Eventually this will result in the execution of either One->render or Two->render, as the case may be. The render method as defined in Zero prints the caller and the CGI params, if any.

In its next-to-last step the script defines another subclass of Zero, called 'MY_Zero'. Finally, it calls activate on the class passed as the script's second argument. Subsequent arguments, if any, should be in the form of 'key=value' strings.

With 'Prototype' as first argument, things work as expected if the second argument is 'Zero':

% perl cgi.pl Prototype Zero
One
% perl cgi.pl Prototype Zero foo=1 bar=2
Two
foo: 1
bar: 2

...but bomb if the second argument is 'MY_Zero':

% perl cgi.pl Prototype MY_Zero
One
Content-type: text/plain

ERROR: Two->initialize_CGI not called at /usr/lib/perl5/CGI/Prototype.pm line 173.

To show what I had expected to happen, I defined CGI::Classic. This module is, first, a drastically simplified toy version of CGI::Prototype. In particular, its (tiny) activate method doesn't even follow the original's logic (e.g. no testing of the result of the respond method, etc.). But the most fundamental difference between CGI::C and CGI::P is that CGI::C does not use Class::Prototype at all:

# CGI/Classic.pm use strict; use warnings; { package CGI::Classic; use CGI; my $CGI; sub activate { $CGI = CGI->new; shift->dispatch->render; } sub param { shift; $CGI->param( @_ ) }; sub dispatch { die 'subclass responsibility' } sub render { die 'subclass responsibility' } } 1; __END__

Now the script works with both 'Zero' and 'MY_Zero' as second argument.

% perl cgi.pl Classic MY_Zero
One
% perl cgi.pl Classic MY_Zero foo=1 bar=2
Two
foo: 1
bar: 2
% perl cgi.pl Classic Zero baz=3 frobozz=4
Two
baz: 3
frobozz: 4

I guessed that the problem resulted from the fact that Class::Prototyped uses a model of inheritance that is different from Perl's standard model, so I also tried a version of the script in which the last call to activate has the following form:

shift; # discard script's second argument Zero->new( 'parent*' => 'Zero' )->activate;

...but I get the same error as before.

Here's where I run out of steam. I find the innards of Class::Prototyped pretty difficult to understand, and I'm not even sure that that's were the problem is.

I ran into this problem when I tried to write a test script for some CGIP-based classes. I wanted to override some methods of the main class for testing, and that's when I created a class analogous to 'MY_Zero' above.

So I have a question and a comment. The question is: how do I do what I want to do (i.e. create a subclass of my main class for the purpose of overriding its methods for testing)? The comment is that the problem illustrated above strikes me as pretty serious, because it completely defeats reasonable expectations about how inheritance should work.

(Something tells me I'm going to learn a lot of Perl soon...)

the lowliest monk


In reply to Subclassing and CGI::Prototype by tlm

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • Outside of code tags, you may need to use entities for some characters:
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and the web crawler heard nothing...

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

      Is guessing a good strategy for surviving in the IT business?





      Results (104 votes), past polls