package SmartHash; use strict; use warnings; use Carp; use overload "%{}" => sub { my $caller = caller; croak "Illegal object access" unless $caller->isa(__PACKAGE__); return shift if $caller eq __PACKAGE__; return shift->{$caller} ||= {}; }, fallback => 1; 1; #### package Foo; use strict; use warnings; use base 'SmartHash'; sub new { my $class = shift; bless {}, $class; } sub foo { my $self = shift; $self->{foo} = shift if @_; return $self->{foo}; } 1; #### package Bar; use strict; use warnings; use base 'Foo'; sub bar { my $self = shift; $self->{bar} = shift if @_; return $self->{bar}; } sub foo { my $self = shift; $self->{foo} = shift if @_; return $self->{foo}; } 1; #### use strict; use warnings; use Test::More tests => 9; use Test::Exception; require_ok( 'Bar' ); require_ok( 'Foo' ); my $o = Bar->new; my $p = Foo->new; dies_ok { $o->{bar} = 42 } "dies on direct access"; $o->bar(13); is( $o->bar, 13, "Bar obj set/get bar" ); $o->foo(42); is( $o->foo, 42, "Bar obj set/get Bar's foo" ); $o->Foo::foo(23); is( $o->Foo::foo, 23, "Bar obj set/get Foo's foo" ); $p->foo(99); is( $p->foo, 99, "Foo obj set/get foo"); is( $o->foo, 42, "Bar obj's foo is still Bar obj's foo" ); is( $o->Foo::foo, 23, "Bar obj's Foo's foo is still Bar obj's Foo's foo" ); #### testfoo....1..9 ok 1 - require Bar; ok 2 - require Foo; ok 3 - dies on direct access ok 4 - Bar obj set/get bar ok 5 - Bar obj set/get Bar's foo ok 6 - Bar obj set/get Foo's foo ok 7 - Foo obj set/get foo ok 8 - Bar obj's foo is still Bar obj's foo ok 9 - Bar obj's Foo's foo is still Bar obj's Foo's foo ok All tests successful. Files=1, Tests=9, 0 wallclock secs ( 0.00 cusr + 0.00 csys = 0.00 CPU)