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

(A related, unanswered question: http://www.perlmonks.org/index.pl?node_id=234516).

I'm trying to utilize Class::MethodMaker for my open source shipping module (http://www.kavod.com/Business-Shipping), but currently, none of the MethodMaker-generated methods call SUPER. So, extended MethodMaker with a subclass that does, but that results in this error:

Can't locate auto/CustomMethodMaker/SUPER/required.al in @INC

(Where $method_name happened to be "required" at the time). My pet theory is that SUPER only works within the context of the correct package, and MethodMaker isn't it. I've tried to reduce all of the code down to the simplest example possible that still exhibits the problem, and pasted it below.

My questions are:

I would greatly appreciate any comments. Cheers,

-- Dan Browning, Kavod Technologies, <db@kavod.com> 360.843.4074x217 6700 NE 162nd Ave, Ste 611-210, Vancouver, WA. Random Fortune: Consultant, n.: An ordinary man a long way from home.
###################################################################### ## CustomMethodMaker ## (must be in it's own file, due to the fact that ## it has to be 'used'. ###################################################################### package CustomMethodMaker; use base ( 'Class::MethodMaker' ); =head2 grouped_fields_inherit Works like grouped_fields, except that it also calls the parent class. =cut sub grouped_fields_inherit { my ($class, %args) = @_; my %methods; foreach (keys %args) { my @slots = @{$args{$_}}; $class->get_set(@slots); my $method_name = $_; $methods{$_} = sub { my ( $self ) = @_; my @parent_slots = (); my $to_execute = "SUPER::$method_name"; # # The following line causes this error: # #Can't locate auto/CustomMethodMaker/SUPER/<METHOD>.al in @INC # @parent_slots = $self->$to_execute(); return ( @parent_slots, @slots ); }; } $class->install_methods(%methods); } 1; #!/usr/bin/perl use strict; use warnings; # See CustomMethodMaker.pm ###################################################################### ## Bug ###################################################################### package Bug; use Class::MethodMaker new_hash_init => 'new', grouped_fields => [ 'required' => [ 'id', 'type', 'description' ], 'optional' => [ 'severity' ], ]; ###################################################################### ## FixedBug ###################################################################### package FixedBug; use base ( 'Bug' ); use CustomMethodMaker new_hash_init => 'new', grouped_fields_inherit => [ 'required' => [ 'date_fixed', 'repairer' ], 'optional' => [ 'repair_notes', 'patch_file' ], ]; ###################################################################### ## Main ###################################################################### package Main; my $bug = Bug->new(); print join( ', ', $bug->required() ) . "\n"; # # Prints 'id', 'type', 'description' # my $fixed_bug = FixedBug->new(); print join( ', ', $fixed_bug->required() ) . "\n"; # # Prints 'date_fixed', 'repairer' -- but I want it to print: # 'id', 'type', 'description', 'date_fixed', 'repairer' #

Replies are listed 'Best First'.
Re: Calling SUPER in MethodMaker-generated methods
by shemp (Deacon) on Jul 07, 2003 at 22:59 UTC
    I've never used MethodMaker, but in general, you probably want to say:
    $class->SUPER::method([args]);
    With a generic class that i created, without the $class-> attached to the SUPER, i got an error like :

    Undefined subroutine &SUPER::method called at ...

    So the error is different, but i think the package scope problems are the same.

      Thanks for the reply. I am trying to do just that ( $class->SUPER::method() ):

      # [...] my $to_execute = "SUPER::$method_name"; @parent_slots = $self->$to_execute(); # [...]

      I tried changing it from $self to $class, to no avail (same error).

      -Dan

        $class vs. $self.
        Consider this most trivial class:
        package Trivial; sub new { my $class = shift @_; ... return bless $self, $class; } sub some_method { my $self = shift @_; ... } ... # in your program that uses Trivial: my $instance = Trivial->new(..); my $instance->some_method(..);
        In the constructor (new), @_ is shifted into $class. This is the name of the package, in this case Trivial.

        When calling some_method(), @_ is shifted into $self. Here, $self is a reference to the calling object, $instance, which is of class Trivial.

        Although i didn't specifically look, there is surely much more detailed documentation on perlmonks. In any case, a good way to remember what is being sent, look at what is to the left of the arrow "->" operator.

        But really, that is a quick and dirty answer that doesnt really tell whats going on. So check out the docs, or Damian Conway's Object Oriented Perl for some very detailed explanations.
Re: Calling SUPER in MethodMaker-generated methods
by bbfu (Curate) on Jul 08, 2003 at 19:24 UTC

    <update> Nevermind. It looks like the eval is just trapping the "Can't locate..." error. Strange that it prints the correct results, then. I'll look into it further when I get back from class tonight.</update>

    You'll notice that the error says perl is looking for the method in the CustomMethodMaker class, instead of Bug or FixedBug, like it's supposed to.

    The issue is that perl appears to be binding SUPER at the time the anonymous sub is created, to the package in which it was created. I'm really not sure why this is happening, but you can get around it by wrapping the call to the SUPER method in an eval.

    # In CustomMethodMaker.pm, line 31 # Was: @parent_slots = $self->$to_execute(); # Now: eval { @parent_slots = $self->$to_execute() };

    With this change, the test program outputs the following on my system:

    id, type, description date_fixed, repairer

    If someone more knowledgeable about the internals of Perl's OO could explain why this is happening, I'm rather curious.

    bbfu
    Black flowers blossom
    Fearless on my breath

      Thanks, I really appreciate you taking the time to look into this, and I think you are on to something. I tried the same change that you tried, and I got the same results. However,
      • The correct results are:
        id, type, description id, type, description, date_fixed, repairer
        see http://www.perlmonks.org/index.pl?node_id=272437 for an example that doesn't use Class::MethodMaker.
      • I don't think eval is trapping the error, because there isn't any @$ around after the eval. Therefore, I think that the eval is a valid statement, it just doesn't do what I want.
      • If you try commenting out the same statement, you should get the same results as eval'ing it.

      -Dan

        Actually, the eval is trapping the error. When I put a die $@ if $@; after the eval, it propagates the same error. I haven't tried using the string form of eval but I don't expect that to work any better than the block eval (though it might if it forces perl to bind SUPER at run time, and thus bind it to the base class instead of CustomMethodMaker, which is why I thought to try eval in the first place).

        The reason it works without Class::MethodMaker is (near as I can tell, anyway) that you're creating the sub in the same class for which you want to find the super class. In other words, it might be that if you wrote a third class that installs the method (in which SUPER is called) into the base class, it wouldn't work.

        It's also possible that the error is actually being generated somewhere else in the call chain when the required method is called. I didn't have time to do a full trace of the code earlier. Creating a simple test module that installs the SUPER-calling method into the example which doesn't use Class::MethodMaker would tell you if the issue is actually with using SUPER outside the base class, though, so that's maybe a good avenue to explore.

        Sorry I can't look into it any more right now. I'm at school and don't have access to Perl. :(

        HTH, anyway.

        bbfu
        Black flowers blossom
        Fearless on my breath