Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I flexibly add data to Moose objects?

Tags:

perl

moose

I'm writing a module for a moose object. I would like to allow a user using this object (or myself...) add some fields on the fly as he/she desires. I can't define these fields a priori since I simply don't know what they will be.

I currently simply added a single field called extra of type hashref which is is set to rw, so users can simply put stuff in that hash:

# $obj is a ref to my Moose object    
$obj->extra()->{new_thingie}="abc123"; # adds some arbitrary stuff to the object
say $obj->extra()->{new_thingie};

This works. But... is this a common practice? Any other (possibly more elegant) ideas?

Note I do not wish to create another module the extends this one, this really just for on-the-fly stuff I'd like to add.

like image 441
David B Avatar asked Oct 22 '10 10:10

David B


2 Answers

I would probably do this via native traits:

has custom_fields => (
    traits     => [qw( Hash )],
    isa        => 'HashRef',
    builder    => '_build_custom_fields',
    handles    => {
        custom_field         => 'accessor',
        has_custom_field     => 'exists',
        custom_fields        => 'keys',
        has_custom_fields    => 'count',
        delete_custom_field  => 'delete',
    },
);

sub _build_custom_fields { {} }

On an object you'd use this like the following:

my $val = $obj->custom_field('foo');           # get field value
$obj->custom_field('foo', 23);                 # set field to value

$obj->has_custom_field('foo');                 # does a specific field exist?
$obj->has_custom_fields;                       # are there any fields?

my @names = $obj->custom_fields;               # what fields are there?
my $value = $obj->delete_custom_field('foo');  # remove field value

A common use-case for stuff like this is adding optional introspectable data to exception and message classes.

like image 149
phaylon Avatar answered Oct 10 '22 21:10

phaylon


If you haven't made the class immutable (there is a performance penalty for not doing that, in addition to my concerns about changing class definitions on the fly), you should be able to do that by getting the meta class for the object (using $meta = $object->meta) and using the add_attribute method in Class::MOP::Class.

#!/usr/bin/perl

package My::Class;

use Moose;
use namespace::autoclean;

package main;

my $x = My::Class->new;
my $meta = $x->meta;
$meta->add_attribute(
    foo => (
        accessor => 'foo',
    )
);

$x->foo(42);

print $x->foo, "\n";

my $y = My::Class->new({ foo => 5 });
print $y->foo, "\n";

Output:

42
5
like image 33
Sinan Ünür Avatar answered Oct 10 '22 21:10

Sinan Ünür