Home > other >  Can I localize the hash reference in a Moose attribute via the accessor?
Can I localize the hash reference in a Moose attribute via the accessor?

Time:06-23

I have a Moose object with an attribute that contains a hash reference.

package Foo;

use Moose;

has bar => (
  is => 'ro',
  isa => 'HashRef',
  default => sub { {} },
};

In my code, I want to local the hash reference that is inside $foo->bar. I know I can do:

my $foo = Foo->new;

# ...

my %local_bar = ( asdf => 123 );
local $foo->{bar} = \%local_bar;   # THIS LINE

call_to_something_that_needs_bar($foo);

for (keys %local_bar) {
  ...
}

But I don't want to do that1. Is there syntax to localize that structure without going to the internals?


1) The reason I don't want to do that is that $foo is wrapped in an Object::Destroyer instance, so while $foo->bar resolves to Foo, $foo->{bar} actually ends up in the destroyer instance, and $foo->{object}->{bar} is where $foo->bar goes. The code with the local is in production code, but the $foo object is only an Object::Destroyer instance in a test.

CodePudding user response:

local makes a backup of a variable, then places a directive on the stack that restores that variable when it's popped off the stack. The following does something similar:

use Object::Destroyer qw( );

sub local_attribute {
   my $obj  = shift;
   my $attr = shift;
   my $val  = shift;  # Optional

   my $backup = $obj->$attr();
   my $guard = Object::Destroyer->new(
      sub {
         $obj->$attr( $backup )
      }
   );

   $obj->$attr( $val );

   return $guard;
}

my $guard = local_attribute( $foo, "bar", \%local_bar );

The above uses object destruction to provide a generic approach. You could also use case-specific exception handling.

  • Related