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.