use v5.12; use warnings; =head2 Target Classes =cut { package TargetSuper; use Carp; sub meth3 { my ( $self, $attr ) = @_ ; carp ("Unknown attr $attr") unless defined $self->{$attr}; return [ __PACKAGE__, 'meth3', $self->{$attr}, (caller)[0..2] ]; } } { package TargetClass; use Carp; use Data::Dump qw/pp/; our @ISA=("TargetSuper"); sub new { my ( $class, %attr ) = @_ ; return bless {%attr}, $class; } sub meth1 { my ( $self, $attr ) = @_ ; carp ("Unknown attr $attr") unless defined $self->{$attr}; return [__PACKAGE__, 'meth1', $self->{$attr}, (caller)[0..2]]; } sub meth2 { my ( $self, $attr ) = @_ ; carp ("Unknown attr $attr") unless defined $self->{$attr}; return [__PACKAGE__, 'meth2', $self->{$attr}, (caller)[0..2]]; } } =head2 Proxy Class Proxy object calls method transparently on target =cut { package Proxy; use Carp; use Data::Dump qw/pp dd/; sub new { my ( $class, $target ) = @_ ; return bless {target=>$target}, __PACKAGE__; } sub call { my $self = shift; my $meth = shift; my $target = $self->{target}; if ( my $c_meth = $target->can($meth) ) { unshift @_, $target; goto &$c_meth; } else { croak "unknown method $meth"; } } } =head2 Tests =cut { package Test; use Data::Dump qw/pp dd/; use Test::More; my %attributes = map { $_ => uc($_) } "a".."c"; my $target = TargetClass->new(%attributes); my $proxy = Proxy->new($target); my ($meth, $level, $attr); $meth = 'meth1'; $level = 'TargetClass'; $attr = 'a'; is_deeply( $proxy->call($meth, $attr), [$level, $meth, uc($attr), "Test",__FILE__,__LINE__], "${level}->$meth: Proxy->call skipped in caller" ); $meth = 'meth2'; $level = 'TargetClass'; $attr = 'b'; is_deeply( $proxy->call($meth, $attr), [$level, $meth, uc($attr), "Test",__FILE__,__LINE__], "${level}->$meth: Proxy->call skipped in caller" ); $meth = 'meth3'; $level = 'TargetSuper'; $attr = 'c'; is_deeply( $proxy->call($meth, $attr), [$level, $meth, uc($attr), "Test",__FILE__,__LINE__], "${level}->$meth: Proxy->call skipped in caller" ); done_testing; exit; }