5

I have come across an odd problem in one of my Perl scripts. I have a Perl object. Within a certain scope I want one of the objects attributes to be changed, but I want the attribute to be restored to it's old value after it leaves the scope.

Example:

my $object = Object->new('name' => 'Bob');
{
     # I know this doesn't work, but it is the best way
     # I can represent what I amd trying to do.
     local $object->name('Lenny');

     # Prints "Lenny"
     print $object->name();
}

# Prints "Bob"
print $object->name();

Is there a way to achieve something like this?

tjwrona1992
  • 8,614
  • 8
  • 35
  • 98
  • An easy if unimpressive hack would be to just store the original value in a new variable at the top of your scope, do your stuff, and restore the original value at the bottom. – swornabsent Sep 03 '15 at 20:19
  • if can you call different methods(with different implementation) inside block or outside block this will work. – Arunesh Singh Sep 03 '15 at 20:29

2 Answers2

6

This might not be as much encapsulation as you were asking for, but you can local-ize an attribute of a hash. This outputs "CarlLennyCarl"

sub Object::new { bless { _name => $_[1] }, $_[0] } }
sub Object::name { $_[0]->{_name} }

my $obj = Object->new("Carl");
print $obj->name;
{
    local $obj->{_name} = "Lenny";
    print $obj->name;
}
print $obj->name;

You could also local-ize the entire method. This also outputs "CarlLennyCarl":

sub Object::new { bless { _name => $_[1] }, $_[0] } }
sub Object::name { $_[0]->{_name} }

my $obj = Object->new("Carl");
print $obj->name;
{
    local *Object::name = sub { "Lenny" };
    print $obj->name;
}
print $obj->name;
mob
  • 117,087
  • 18
  • 149
  • 283
-1

I was completely misunderstanding what was occurring there. You cannot use local on subroutine calls, that is the issue you are having.

Lets use a code example from one that I know works and try to explain what eval is actually doing.

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use Cwd;

print getcwd() . "\n";

eval{
        local @INC = ('/tmp');
        require 'test.pl';

        print 'local: ' . Dumper(\@INC);
};
print Dumper(\@INC);

That works because I am modifying a variable, not calling on another subroutine to modify my variable.

In order for it to work as you are expecting, you would have to create a deep copy of the object to modify in local scope or something of the sort. (which I'm pretty sure is what is occurring in the first place)

local creates scope for the given brackets, eval, OR file (your problem there)

If you were able to access the elements directly without the method call (bad practice IMHO) you would likely be able to localize the scope of that element in the object.

Example:

name.pm:

package name;
use strict;
use warnings;

{

    sub new {
        my ($class,$name) = @_;
        my $self = bless {}, $class;
        $self->{'name'} = $name if defined $name;
        return $self;
    }

    sub name
    {
        my ($self,$name) = @_;
        $self->{'name'} = $name if defined $name;
        return $self->{'name'};
    }
}

index.pl:

#!/usr/bin/perl -w
use strict;
use warnings FATAL => 'all';

use name;

my $obj = name->new('test');

print $obj->{'name'} . "\n";

{
    local $obj->{'name'} = 'test2';

    print $obj->{'name'} . "\n";
}

print $obj->{'name'} . "\n";
steve
  • 290
  • 1
  • 11
  • You didn't get the question at all? – Arunesh Singh Sep 03 '15 at 20:51
  • no, misunderstood context. things like local, eval, and a lot of perl keywords do different things depending on context. It didn't click that the issue was local being called on a method call – steve Sep 03 '15 at 21:01
  • BUT, you are more than allowed to answer questions yourself instead of posting to a topic just to berate ;) – steve Sep 03 '15 at 21:02