How can I flexibly add data to Moose objects?

I am writing a module for a moose object. I would like the user using this object (or I ...) to add some fields on the fly as he wants to. I can’t define these fields a priori, because I just don’t know what they will be.

Currently, I just added one field called extra of type hashref that is set to rw , so users can just put stuff in this 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}; 

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

Note. I do not want to create another module that extends it, it is really simple for the on-the-fly that I would like to add.

+6
perl moose
source share
3 answers

I would probably do this with my own attributes:

 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 should use it as follows:

 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 such things is adding optional introspective data to exception and message classes.

+6
source share

If you have not made the class immutable (there is a performance penalty for not doing this, in addition to my concern about changing the definition class on the fly), you should do this by getting a 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 
+4
source share

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

eg.

 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, above, the baz method is only added to the $foo object, not to the Foo class.

Hmmm ... I wonder if I can implement MooseX :: SingletonAttribute?


Some previous SO answers using MooseX::SingletonMethod :

  • How to replace the Moose object method at runtime?
  • How to create a new Moose class and instantiate an object of this class at run time?

And also this blog post can be useful and / or interesting: Lightweight anonymous objects

/ I3az /

+3
source share

All Articles