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

Here is a bizarre and annoying error that cropped up for me in the past hour and which so far defies any logic I seem able to wrap my head around.

In this example, $self is 'My::New::Module::WWW', which absolutely has a ->_home() method, and was performing as intended until I added this new invocation to the constructor for the Admin module. Using CGI::TabPane, I have a method reading in relevant part:

sub init_admin { my $self = shift; my $id = shift || '3'; # print STDERR Dumper(\$self); # my @trace = caller; # print STDERR $trace[0] . ' ' . $trace[1] . ' ' . $trace[2]; my $admin = My::New::Module::Admin->new({ object => $self }); my($pane_1_tab_0_0) = $self->_home($id); my($pane_1_tab_0_1) = $self->_this_form(); my($pane_1_tab_0_1) = $self->_that_form(); . . . return [$pane]; }
And now I'm getting an error reading:

test-cgi-tabpane.cgi: Can't locate object method "_home" via package " +My::New::Module::Admin" at (the line reading: my($pane_1_tab_0_0) += $self->_home($id);).
But that very clearly is a call to $self->_home(), not to $admin->_home(). Both ::WWW and ::Admin include the line: use base 'My::New::Module'. My hope was that all of their various methods might be invoked as methods of the base class.

OK, I finally moved the constructor to the base package and renamed the constructors in the ::WWW and ::Admin modules and I'm still getting exactly the same error.

Now I'm really baffled and beffudled. Anyone out there who can help me unwrap this, please?

-- Hugh

UPDATE:

Thanks Chromatic! From the constructor, I now return $self as soon as I set it to $self->{'object'} which resolves the issue you identified, I think. And instead of creating an object to access the Admin->methods(), for the moment I'm content to give a fully qualified package name to those calls, so I can simply move on with this project. There are so many more pressing issues much closer to the critical path for delivery and deployment on this project, that I thought it best not to be distracted by this one too much longer.

CountZero: You are probably correct, that learning Moose (which has been on my list for a while) would be a worthwhile investment. At the moment I feel sufficiently invested in the project I'm currently working on that I don't want to get distracted by such a project at the moment. Perhaps that means re-inventing a few wheels along the way. But the path seems less of an uphill climb for the moment at any rate.

For the moment I need to stop adding new features and focus on deploying what I've already got. I have a few regression issues to deal with first. Hopefully by the end of the day. We shall see.

if( $lal && $lol ) { $life++; }

Replies are listed 'Best First'.
Re: Confused by use base, I think.
by chromatic (Archbishop) on Jun 15, 2008 at 06:27 UTC

    Can you show the code of My::New::Module::Admin::new?

Re: Confused by use base, I think.
by hesco (Deacon) on Jun 15, 2008 at 07:52 UTC
    Morning folks: Thanks for the additional sets of eyes on this.

    Ikegami: Your warn(ref($self)) gives me:

    test-cgi-tabpane.cgi: My::New::Module::Admin
    and uncommenting the debug code you mention yields:
    main /usr/lib/cgi-bin/test-cgi-tabpane.cgi 51
    Chromatic: I renamed my My::New::Module::Admin::new() method, as My::New::Module::Admin::_new, hoping that would force it to inherit My::New::Module::new instead. So I assume you want to see that, which reads:

    sub new { my $class = shift; my $defaults = shift; my $self = {}; if(defined($defaults->{'object'}) && (UNIVERSAL::isa($defaults->{'object'},'My::New::Module') || UNIVERSAL::isa($defaults->{'object'},'My::New::Module::WWW'))){ $self = $defaults->{'object'}; } elsif(defined($defaults->{'cfg'}) && UNIVERSAL::isa($defaults->{'cfg'},'Config::Simple')){ $self->{'cfg'} = $defaults->{'cfg'}; } elsif(defined($defaults->{'config_file'})){ my $config_file = $defaults->{'config_file'}; # print STDERR $config_file,"\n"; $self->{'cfg'} = Config::Simple::Inherit->inherit({ filename => $c +onfig_file }); } elsif(defined($defaults->{'config_files'})){ my $cfg; undef($cfg); foreach my $file (@{$defaults->{'config_files'}}){ # print STDERR "My::New::Module::WWW->new() # says \$file is $file \n"; $cfg = Config::Simple::Inherit->inherit({ base_config => $cfg, filename => $file }); } $self->{'cfg'} = $cfg; # print STDERR Dumper(\$cfg); } else { die "Constructor invoked with no Confirguration File." } $self->{'q'} = CGI->new(); $self->{'agent'} = WWW::Mechanize->new(); my $db1 = $self->{'cfg'}->get_block('db1_params'); my $db2 = $self->{'cfg'}->get_block('db2_params'); # print STDERR Dumper(\$db2),"\n",Dumper(\$db2); $self->{'dbh1'} = My::New::Module::DB->connect2($db1_params); $self->{'dbh2'} = My::New::Module::DB->connect2($db2_params); $self->{'s'} = CGI::Session->new( undef, $self->{'q'}, { Directory => '/tmp/sessions'} ) or die CGI::Session->errstr; $self->{'s'}->expire('+1h'); $self->{'s'}->flush(); my $auth_params = $self->{'cfg'}->get_block('auth'); $self->{'auth'} = CGI::Session::Auth::DBI->new({ CGI => $self->{'q'}, Session => $self->{'s'}, DBHandle => $self->{'dbh1'}, EncryptPW => $auth_params->{'EncryptPW'}, GroupField => $auth_params->{'GroupField'} }); bless $self, $class; return $self; }
    I'm not sure what additional clues that provides us, but am grateful others are looking at this with me. Thanks again.

    -- Hugh

    if( $lal && $lol ) { $life++; }

      You're reblessing your object into the wrong class. No wonder Perl can't find your method.

      $self = $defaults->{'object'}; ... bless $self, $class;

      I'm not sure what you intend to do with those lines, but they don't have the desired effect.

      Similarly:

      UNIVERSAL::isa($defaults->{'object'},'My::New::Module')

      ... is correct only in very simple circumstances. It's more correct to write:

      $defaults->{object}->isa( 'My::New::Module' )

      ... but then again, it's not clear what you intend this code to do.

Re: Confused by use base, I think.
by ikegami (Patriarch) on Jun 15, 2008 at 05:54 UTC

    What do you get when you do warn(ref($self)."\n"); right before the line that gives the error?

    And what's the output of the debugging code you have there but commented out?

Re: Confused by use base, I think.
by CountZero (Bishop) on Jun 15, 2008 at 20:04 UTC
    Rather than re-inventing wheels, would it not be more advisable to use existing OO-frameworks such as Moose? It implements inheritance, extensions, accessors, object construction and destruction, roles, class introspection, ...

    You are probably making all the errors the authors of these frameworks made and solved.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: Confused by use base, I think.
by Herkum (Parson) on Jun 15, 2008 at 03:33 UTC

    Use base is intended to include code from another another package into your current one. The way I am reading your code, it looks like you are trying to base an object and hope that it inherits its methods, and that is not right. use base should be used like this,

    package My::New::Module::Admin; use base 'My::New::Module::WWW';

    All the code in My::New::Module::WWW is now available in your child package My::New::Module::Admin. Does that help?

      You have it completely backwards.

      Use base is intended to include code from another another package into your current one

      Not at all. base is intended to load a module and setup an inheritance relationship in one step.

      In fact, since base doesn't even call the module's import function, it's intention are exactly the opposite of what you say they are.

        On a technical level, you are correct, but sometimes to wrap your head around an idea you present it in a way someone has to understand it. For someone who may not understand the difference you approach the idea in a way they can comprehend not the terminology they may or may not understand.