Just in has asked for the wisdom of the Perl Monks concerning the following question:

How do I prevent autovivification with Inside Out objects? I'm really after a fatal rather than a warning when the user accesses something they shouldnt.

I'm unable to use any modules that require installation :( and am using 5.8

I can control what goes into my module but not what the user writes to access it. All thoughts are welcome.

package InOutObj; use strict; use warnings; use Scalar::Util qw( refaddr ); { my %data; sub new { return bless \do{ my $dummy }, shift; } sub get { my $self = shift; return $data{ refaddr $self }; } sub set { my ( $self, $arg ) = @_; $data{ refaddr $self }{upper} = uc $arg; $data{ refaddr $self }{lower} = lc $arg; return; } sub DESTROY { my $self = shift; delete $data{ refaddr $self }; return; } } 1; package main; use strict; use warnings; my $obj = new InOutObj; $obj->set('i LiKe WiNe'); # User should not be able to make up stuff on their own! print $obj->get()->{uPper} . "\n"; print "Why am I here?\n";

Replies are listed 'Best First'.
Re: Preventing autovivification
by BrowserUk (Patriarch) on Feb 16, 2010 at 04:25 UTC

    You should not be returning a hashref from your get() method. It completely negates the purpose of using InsideOut objects.

    Try this:

    package InOutObj; use strict; use warnings; use Scalar::Util qw( refaddr ); { my( %upper, %lower ); sub new { return bless \do{ my $dummy }, shift; } sub getUpper { my $self = shift; return $upper{ refaddr $self }; } sub getLower { my $self = shift; return $lower{ refaddr $self }; } sub set { my ( $self, $arg ) = @_; $upper{ refaddr $self } = uc $arg; $lower{ refaddr $self } = lc $arg; return; } sub DESTROY { my $self = shift; delete $upper{ refaddr $self }; delete $lower{ refaddr $self }; return; } } 1; package main; use strict; use warnings; my $obj = new InOutObj; $obj->set('i LiKe WiNe'); # User should not be able to make up stuff on their own! print $obj->getUpper . "\n"; print $obj->getLower . "\n";

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      "You should not be returning a hashref from your get() method. It completely negates the purpose of using InsideOut objects"

      Thanks. That's a clear cut rule I can work with and remember. I went off at a tangent w.r.t. DC's PBP 1st ed. @ p345

      A reply falls below the community's threshold of quality. You may see it by logging in.

      In addition to this, you need to isolate the object (inside out) from the implementation (hash). In short, there is no way to stop an attribute from being added to a hash data structure. Auto-vivification is just part of the behavior of hashes.

      You can achieve like-hashes that don't auto-vivify like this with Variable::Magic or Tie::HashRef, but I believe this to be outside the scope of the question.

      With an inside hash, you would typically control autovivification with the set method.



      Evan Carroll
      The most respected person in the whole perl community.
      www.evancarroll.com
        In addition to this, you need to isolate the object (inside out) from the implementation (hash).

        Sorry, but I think you are compounding two different goals and coming up with a lemon.

        The original goals of IOOs were:

        • Proper encapsulation
        • Compile time checking of attribute use.
        • A technique not a module.
        • Don't depend on the implementation of the superclass.
        • Don't force a particular implementation on subclasses.
        • Simplicity.

        What you are suggesting is that the internal data implementation should be encapsulated (isolated) from the internal code. This was never a (stated) design goal, but can be achieved by the religious use of accessors, internally.

        In short, there is no way to stop an attribute from being added to a hash data structure. Auto-vivification is just part of the behavior of hashes.

        Actually, I think Hash::Util was specifically designed to address this.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Preventing autovivification
by ikegami (Patriarch) on Feb 16, 2010 at 05:00 UTC

    I think you might mean the prevention of addition of keys to a hash, but if you really mean autovivification, there's

    no autovivification qw( strict fetch exists delete store );

    Update: Oops, I missed "I'm unable to use any modules that require installation". I'm not sure how we can help if you can't install our code.

Re: Preventing autovivification
by JavaFan (Canon) on Feb 16, 2010 at 11:15 UTC
    Too bad you aren't using 5.10. It's so much simpler. Here's how you can do it in 5.10:
    use 5.010; use strict; use warnings; package InOutObj; use Hash::Util::FieldHash 'fieldhash'; fieldhash my %upper; fieldhash my %lower; sub new {bless \do {my $dummy}, shift} sub upper {$upper{$_[0]}} sub lower {$lower{$_[0]}} sub set { my ($self, $arg) = @_; $upper{$self} = uc $arg; $lower{$self} = lc $arg; $self; } package main; my $obj = InOutObj->new; $obj->set('i LiKe WiNe'); say $obj->upper; say $obj->lower; __END__ I LIKE WINE i like wine
Re: Preventing autovivification
by Corion (Patriarch) on Feb 16, 2010 at 08:56 UTC