Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Importing CGI methods into a subclass as a class method

by Phaysis (Pilgrim)
on Feb 13, 2003 at 07:21 UTC ( [id://234909] : perlquestion . print w/replies, xml ) Need Help??

Phaysis has asked for the wisdom of the Perl Monks concerning the following question:

I have an odd, funky situation. I'm writing an aggregate object module, Request.pm, that contains, among others, a CGI object. My intent is to import some of CGI's methods (per CGI's docs) into my object and have access to them as though they were methods of Request. My problem is making the imported method stick beyond the scope of my _init() method. Here's a sample:
sub new { #object blessing stuff skipped here... $self->_init(@_); return $self; } sub _init { my $self = shift; #the following creates the CGI object, importing the #method param() into current scope, _init() $self->{'query'} = new CGI qw(param); #...etc... }
After having done this, the user only needs to do...
my ($comment) = $requestobj->param('comment');
...to access the value of the form variable "comment". I've tried different methods to make this work, but nothing has seemed to work short of creating a stub method in Request to forward any parameters and return context to the CGI::param() method.

Is there a way, while in the scope of _init(), to assign the imported method param() to the Request object? Would I have to use, perhaps, a sub reference? Would I have to rely on AUTOLOAD to form a forward call to any imported methods? I know that when _init() goes out of scope, the methods imported from CGI fail to exist; I want these methods to be sticky.

I am a-thoroughly confused. Thanks for any help.

-Shawn / (Ph) Phaysis
If idle hands are the tools of the devil, are idol tools the hands of god?

Replies are listed 'Best First'.
Re: Importing CGI methods into a subclass as a class method
by Corion (Patriarch) on Feb 13, 2003 at 08:08 UTC

    Whenever you have an aggregate object and want to export methods of the aggregated object(s), I think of Class::Delegation - this makes it (at least for one level of aggregation/calls) easy to delegate methods from your class to aggregated objects. I seem to have problem to derive a subclass from an Class::Delegation-using class, but that might be a problem of my code.

    perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ; # The $d = new HTTP::Daemon and fork and getprint $d->url and exit;#spider ($c = $d->accept())->get_request(); $c->send_response( new #in the HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web
Re: Importing CGI methods into a subclass as a class method
by blokhead (Monsignor) on Feb 13, 2003 at 07:44 UTC
    I think there is some confusion with the CGI.pm syntax. use CGI qw(param) will import the param sub to the caller's namespace, however new CGI qw(param) will initialize a new CGI object with a query string of "param". See:
    use CGI; my $q = new CGI qw(param); print "\$q->param($_) = " . $q->param($_) . "\n" for $q->param; print param('asdf'); # not imported, error __END__ prints: $ perl test.pl $q->param(keywords) = param Undefined subroutine &main::param called at - line 4.
    If the methods really were being imported into the namespace, they would be automatically usable as methods on your objects, as they are not bound by the lexical scope of your function call. However, I doubt it would work right, as the method calls would eventually translate to CGI::param($obj, 'foo') instead what you probably want: CGI::param($obj->{query}, 'foo'), (i.e., $obj->{query}->param('foo')).

    I don't see the problem with using AUTOLOAD to dispatch to the CGI object, even if you want to use it with more than just the param method:

    sub AUTOLOAD { my $func = $AUTOLOAD; $func =~ s/^.*:://; $self->{query}->$func(@_); }

    blokhead

Re: Importing CGI methods into a subclass as a class method
by dragonchild (Archbishop) on Feb 13, 2003 at 14:54 UTC
    Ideally, you would do this in some new() method, but _init() should work just fine, too. You want to use __PACKAGE__ because you want the methods defined at the lowest package possible. That way, code re-use is preserved.

    Update: broquaint made the good point that this could also be put in some BEGIN block.

    my @methods = qw( param foo bar ); sub _init { my $self = shift; { no strict 'refs'; unless (defined \&{__PACKAGE__."::$methods[0]"}) { foreach my $method (@methods) { *{__PACKAGE__."::$method"} = sub { my $self = shift; return undef unless UNIVERSAL::isa($self->query}, CGI); return $self->{query}->$method{@_); }; } } } # Rest of stuff here. }
    I personally don't like AUTOLOAD. It smacks too much of hidden black magic to me. :-)

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      It's good to test your code. At least with -c.

      Line 12: A reference is always defined. So the useful code will never be executed.
      Line 18: There's a missing { and CGI won't pass under strict subs. And does a return value of undef make most sense?
      Line 19: A { should be replaced with (.

      Another thought: Wouldn't it be better to check for definedness for every subroutine instead of just the first? What if he decides to define another param subroutine and forget to remove param from @methods? Then the other subroutine won't get defined either. Of course, it would be a lot better to put it in a BEGIN block, as noted. Then you don't have to check for definedness manually, since perl will warn about redefinitions.

      ihb
Re: Importing CGI methods into a subclass as a class method
by Phaysis (Pilgrim) on Feb 14, 2003 at 01:30 UTC
    OK. After reading your advice, and reading the most recent CGI documentation, I realized that importing any CGI methods into my Request namespace forces me into using CGI in the function-oriented fashion, and prevents me from having multiple Request objects with their own unique CGI objects. My confusion with the use CGI and $r = new CGI qw(param) importing situation has been cleared up. (Funny how re-reading, after listening to suggestions, can change things.)

    I've decided my best option, at this point, is to simply make stub methods to forward calls to the CGI object. Seems to me to be the best policy. I will, however, look into your suggestions as they appear to be potentially quite usable.

    Thank you folks for your quick (!!) advice. Wow.

    -Shawn / (Ph) Phaysis
    Among turkeys, I am the eagle. Among eagles, I am the turkey.
    If idle hands are the tools of the devil, are idol tools the hands of god?

      I realized that importing any CGI methods into my Request namespace forces me into using CGI in the function-oriented fashion

      Actually... no. :) The CGI subroutines are a dual in how they act. If they're used as functions, they act like functions. If they're used like methods, they act like methods. So you can still import CGI methods/functions and expect it to work.

      ihb