tags:

views:

68

answers:

3

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.

+3  A: 

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
Sinan Ünür
+1 Interesting. Thanks. I usually do set the class to immutable (following Moose best practices).
David B
+2  A: 

Just in case you want to add a method to an object and not to the whole class then have a look at something like MooseX::SingletonMethod.

E.g.

use 5.012;
use warnings;

{
    package Foo;
    use MooseX::SingletonMethod;
    sub bar { 'bar' }     # method available to all objects
}

my $foo = Foo->new;

$foo->add_singleton_method( baz => sub { 'baz!' } );

$foo->baz;     # => baz!

So in above the method baz is only added to the object $foo and not to class Foo.

Hmmm... I wonder if I could implement a MooseX::SingletonAttribute?


Some previous SO answer using MooseX::SingletonMethod:

And also this blog post maybe of use and/or interest: Easy Anonymous Objects

/I3az/

draegtun
+1 Thanks, good to learn about `MooseX::SingletonMethod`.
David B
+1  A: 

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.

phaylon
+1 That's really cool!
David B
Can I make the accessor croak if I try to read (not set) a non-existing field?
David B
You can wrap the 'custom_field' accessor with an around modifier, check the args, and croak if 'has_custom_field' returns false.
phaylon