Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Make an inside-out object look hash-based using overload.

by kyle (Abbot)
on Apr 10, 2007 at 16:23 UTC ( [id://609197]=perlmeditation: print w/replies, xml ) Need Help??

Here's the scenario: You have a class implemented in the hash-based tradition. Code using it is welcome to access certain public attributes directly (e.g., $object->{foo} = 'new value of foo' is supported). Now you want to change that class to be an inside-out object, but you don't want to break existing code.

One answer is to use overload to make your new inside-out object appear to be a hash-based object. The code below is a proof of concept.

package Hash::Pretender; use overload '%{}' => \&get_hashref; use Class::Std; { my %attr1_of :ATTR( :get<attr1> :set<attr1> ); my %attr2_of :ATTR; sub get_hashref { my $self = shift; tie my %h, ref $self, $self; return \%h; } sub get_attr2 { return ++$attr2_of{ ident shift }; } sub TIEHASH { my ( $package, $self ) = @_; return $self; } sub STORE { my ($self, $key, $value) = @_; my $setter = "set_$key"; $self->$setter( $value ); } sub FETCH { my ($self, $key) = @_; my $getter = "get_$key"; return $self->$getter(); } } package main; use Test::More 'tests' => 11; my $o = Hash::Pretender->new(); isa_ok( $o, 'Hash::Pretender' ); $o->set_attr1( 'foo' ); is( $o->get_attr1(), 'foo', 'attr1 is "foo" via get_attr1' ); is( $o->{attr1}, 'foo', 'attr1 is "foo" via hash dereference' ); SKIP: { skip 'Not implemented', 1 unless ( $o->can('FIRSTKEY') && $o->can('NEXTKEY') ); %h = %{$o}; is( $h{attr1}, 'foo', 'attr1 is "foo" via hash copy' ); }; $o->{attr1} = 'bar'; is( $o->get_attr1(), 'bar', 'attr1 is "bar" via get_attr1' ); is( $o->{attr1}, 'bar', 'attr1 is "bar" via hash dereference' ); SKIP: { skip 'Not implemented', 1 unless ( $o->can('FIRSTKEY') && $o->can('NEXTKEY') ); %h = %{$o}; is( $h{attr1}, 'bar', 'attr1 is "bar" via hash copy' ); }; is( $o->get_attr2(), 1, 'attr2 is 1' ); is( $o->{attr2}, 2, 'attr2 is 2' ); ok( ! eval { $o->{attr2} = 3; 1 }, 'Set attr2 fails' ); my $err = $@; my $msg = q{Can't locate object method "set_attr2" via package "Hash::Pret +ender"}; is( substr( $err, 0, length $msg), $msg, 'Set attr2 fails for the expected reason' );

Now, code that used the old interface will continue to work. If you want to migrate it, you can throw out warnings in the methods that tie depends on to track down the offending code. You can also continue using the old interface and gain the advantage that the new code will throw an exception when you typo an attribute name.

Replies are listed 'Best First'.
Re: Make an inside-out object look hash-based using overload.
by Ovid (Cardinal) on Apr 11, 2007 at 09:14 UTC

    Nice to see tests along with this.

    I was halfway expecting folks to complain that you're violating the entire point of IOO (Inside-Out Objects), but I've worked with enough dodgy code that I know things like this are frequently necessary to evolve a code base into something useful.

    Cheers,
    Ovid

    New address of my CGI Course.

Re: Make an inside-out object look hash-based using overload.
by adrianh (Chancellor) on Apr 11, 2007 at 12:35 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://609197]
Approved by chromatic
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (4)
As of 2024-03-29 04:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found