daliaessam
daliaessam

Reputation: 1666

Perl Moose add instance attribute not class attribute

I need to add attribute to Moose class instance. In the code below, when I create instance of the class Child and add attribute "app" to it, I find this attribute also added when I create next instances. What I am doing wrong, again I need the attribute per created instance.

#!C:\perl\bin\perl.exe
#!/usr/bin/perl

use v5.10;
use Moose;
use Data::Dumper;

{
    package Child;

    use Moose;
    use utf8;

    sub name {
        say "My name is Richard";
    }
}

sub add_attribute {
    my ($object, $attr) = @_;

    my $meta = $object->meta;

    if (!$object->can("app")) {
        $meta->add_attribute(app => (is => 'rw', default => sub{$attr}));
        $object->app($attr);
    }
    else {
        #$object->app($attr);
        say "attr $attr already exists: object=". ref($object) . ", attr=".($object->app);
    }
}

my $child = Child->new;
$child->name;
add_attribute($child, "First");
say "Child Attr: " . $child->app;
say "";
say Dumper($child);

my $child1 = Child->new;
$child1->name;
#add_attribute($child1, "Second");
say "Child1 Attr: " . $child1->app;
say Dumper($child1);
#say Dumper($child1->meta);

output:

My name is Richard
Child Attr: First

$VAR1 = bless( {
                 'app' => 'First'
               }, 'Child' );

My name is Richard
Child1 Attr: First
$VAR1 = bless( {
                 'app' => 'First'
               }, 'Child' );

Upvotes: 0

Views: 877

Answers (1)

tobyink
tobyink

Reputation: 13664

The trick is to create a new subclass of your original class, add the attribute to that, then rebless the instance into the new subclass. Here's an example:

use v5.14;

package Person {
  use Moose;
  has name => (is => 'ro');
}

sub add_attribute {
  my ($obj, $name, $value) = @_;
  my $new_class = Moose::Meta::Class->create_anon_class(
    superclasses => [ ref($obj) ],
  );
  $new_class->add_attribute($name, is => 'rw');
  $new_class->rebless_instance($obj, $name => $value);
}

my $alice  = Person->new(name => 'Alice');
my $bob    = Person->new(name => 'Bob');

add_attribute($alice, foot_size => 6);

say $alice->foot_size;

say $bob->foot_size;  # dies, no such method

Upvotes: 6

Related Questions