#lexical-methods-test.pl
use strict;
use warnings;
use 5.010;
use Foo;
my $obj = Foo->new;
# Look, we can call normal class methods:
$obj->foo;
# This will fail (which is why it's in an eval)
eval {
$obj->bar;
1;
} or do {
print "Unable to call 'bar' on Foo object in this lexical scope\n";
};
{
# Lexically allow method "bar" as a Foo method:
use Bar;
$obj->foo;
$obj->bar;
}
# Out here, normal method calls still work
$obj->foo;
# Kaboom!
$obj->bar;
####
# Bar.pm
use strict;
use warnings;
use 5.010;
package Bar;
# Installs a method called bar
sub import {
$^H{"Foo/bar"} = 'Bar/bar';
}
sub unimport {
delete $^H{"Foo/bar"};
}
sub bar {
print "Calling method bar from package Bar\n";
}
1;
####
# Foo.pm
use strict;
use warnings;
use 5.010;
package Foo;
use Carp 'croak';
sub new {
my $class = shift;
$class = ref($class) || $class;
return bless {}, $class;
}
sub foo {
print "Calling method foo from package Foo\n";
}
sub AUTOLOAD {
my $self = $_[0];
# Get the called method name and trim off the fully-qualified part
( my $method = our $AUTOLOAD ) =~ s{.*::}{};
# Get the hints hash for the calling lexical scope
my $hinthash = (caller(0))[10];
if (exists $hinthash->{"Foo/$method"}) {
my ($package, $package_method)
= split '/', $hinthash->{"Foo/$method"}, 2;
# Retrieve the subref and goto() it
my $subref = $package->can($package_method)
or croak("Lexically scoped Foo method $method points to a nonexistent method ${package}::$package_method");
goto &$subref;
}
elsif (my $super_method = $self->SUPER::can($method)) {
goto &$super_method;
}
elsif ($method eq 'DESTROY') {
# Do nothing if we come to this.
return;
}
else {
croak("Can't locate object method \"$method\" via package \"" . __PACKAGE__ . '"');
}
}
sub can {
my ($self, $method) = @_;
# Check if it's a lexical method
my $hinthash = (caller(0))[10];
if (exists $hinthash->{"Foo/$method"}) {
my ($package, $package_method)
= split /\//, $hinthash->{"Foo/$method"}, 2;
# Retrieve the subref and goto() it
my $subref = $package->can($package_method)
or croak("Lexically scoped Foo method $method points to a nonexistent method ${package}::$package_method");
return $subref;
}
elsif (my $super_method = $self->SUPER::can($method)) {
return $super_method;
}
else {
return undef;
}
}
1;