Ok, I think I found the cause of the problem, and a way to work around it, though it's a little ugly.
If you look in perlboot under SUPER, you find the following:
So, "SUPER::speak" means look in the current package's @ISA for "speak", invoking the first one found.
In other words, SUPER ignores the class of the object it's called upon, and always looks in the current package's @ISA to find the super class. So, when you create the anonymous sub to be the method, it's actually created in your CustomMethodMaker class, and so that's whose @ISA it uses, even though it's later installed into the FixedBug class. (Technically, it's not actually put into the FixedBug class. Rather, a reference to it is put into FixedBug's symbol table. The sub still resides in CustomMethodMaker.) Personally, I don't think is the correct thing to do (I think SUPER should look in the @ISA of the package that the object is blessed into) but it's documented, so it can't be a bug. ;)
So, to do what you want to do, you need to force the call to SUPER to be in the correct package. The way to do this is to note what package your installer function is called from, and manually set yourself back to that package just prior to calling the super-method. Of course, since you only know what package your installer function is called from at run-time, you have to set the package dynamically at run-time, via (string) eval.
I created a simple set of test classes. You should just need to use the eval and caller() lines in MyTest::MethodInstaller::install(). With your original example code, you'll have to get the caller two levels higher (ie, caller(2)). Once for the import, and once for something else internal to Class::MethodMaker. Or, you could use Class::MethodMaker's internal (undocumented) find_target_class() method, which searches up the call stack for the first namespace which is not a descendant of Class::MethodMaker. That would probably be better (more robust, somewhat less likely to change).
The code:
Well, that should get you working, though I think the eval trick is ugly and shouldn't be necessary in the first place. Oh well. C'est la vie.
bbfu
Black flowers blossom
Fearless on my breath
| [reply] [d/l] |
Done! For the record, I attached the final MethodMaker-derived code. Would you accept a small tip via PayPal (if so, what e-mail should I use)?
I'm going to see about submitting this for the MethodMaker maintainer.
####################################################################
## CustomMethodMaker
####################################################################
package CustomMethodMaker;
use base ( 'Class::MethodMaker' );
use Carp;
=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 = $_;
my $caller = $class->find_target_class();
$methods{$_} = sub {
my ( $self ) = @_;
my @parent_slots = ();
my $to_execute = "SUPER::$method_name";
#
# Without $caller and eval, the following line causes this err
+or:
#
# Can't locate auto/CustomMethodMaker/SUPER/[METHOD].al in @
+INC
#
@parent_slots = eval "package $caller; return \$self->SUPER::$
+method_name(\@_); 1" or die $@;
return ( @parent_slots, @slots );
};
}
$class->install_methods(%methods);
}
1;
-Dan | [reply] [d/l] |
Would you accept a small tip via PayPal
Nah, don't sweat it. I enjoyed helping. :) I appreciate the offer, tho.
Update: Re: your code; I just wanted to point out that you should probably either put the assignment to @parent_slots inside the eval, since it's conceivable that the super-method could correctly return an empty list which would cause your code to die, or make the test for dying an explicit check on $@ (in which case the 1; at the end of the eval is superfluous). IOW, it's best to do this:
@parent_slots = eval "package $class; \$self->SUPER::$method_name()";
die $@ if $@;
Also, if you're going to pass @_ in to the super-method, you need to shift $self off first, instead of setting it via list-context assignment. (Otherwise, the super-method gets two copies of $self.)
bbfu
Black flowers blossom
Fearless on my breath | [reply] [d/l] |
Woo hoo! Thanks, bbfu. I'll have to try this out in the morning. (FYI, don't do eval "package $self";, it causes a Segmentation Fault.)
-Dan
| [reply] [d/l] |
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.
I'll give that a shot (not sure if I can do it, but I'll try).
:-( I tried, but I can't figure it out. Here's hoping that we'll figure it out eventually.
-Dan
| [reply] |