2010-10-22 4 views
6

J'écris un module pour un objet orignal. Je voudrais permettre à un utilisateur utilisant cet objet (ou moi-même ...) d'ajouter des champs à la volée comme il/elle le désire. Je ne peux pas définir ces domaines a priori puisque je ne sais tout simplement pas ce qu'ils seront.Comment ajouter de manière flexible des données aux objets Moose?

Je suis actuellement simplement ajouté un champ unique appelé supplémentaire de type hashref qui est mis à rw, afin que les utilisateurs peuvent tout simplement des choses mettre dans ce hachage:

# $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}; 

Cela fonctionne. Mais ... est-ce une pratique courante? D'autres idées (éventuellement plus élégantes)?

Remarque Je ne souhaite pas créer un autre module qui prolonge celui-ci, mais uniquement pour les choses à la volée que j'aimerais ajouter.

Répondre

6

je ne serais probablement le faire via des traits indigènes:

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 { {} } 

Sur un objet que vous souhaitez utiliser ce comme ce qui suit:

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 

Un cas d'utilisation commune pour des choses comme cela est l'ajout d'option données introspectables aux classes d'exception et de message.

+0

+1 C'est vraiment cool! –

+0

Puis-je faire crocher l'accesseur si j'essaie de lire (non défini) un champ inexistant? –

+0

Vous pouvez envelopper l'accesseur 'custom_field' avec un modificateur around, vérifier les arguments, et croasser si 'has_custom_field' renvoie false. – phaylon

4

Si vous n'avez pas rendu la classe immutable (il y a un performance penalty pour ne pas le faire, en plus de mes inquiétudes concernant la modification des définitions de classe à la volée), vous devriez pouvoir le faire en obtenant la méta-classe l'objet (en utilisant $meta = $object->meta) et en utilisant la méthode add_attribute dans 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"; 

Sortie:

42 
5
+0

+1 Intéressant. Merci. En général, je classe la classe sur immuable (en suivant les meilleures pratiques de Moose). –

3

Juste au cas où vous souhaitez ajouter une méthode à un objet et non pas à la classe puis jeter un oeil à quelque chose comme 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! 

Ainsi, dans la méthode ci-dessus baz est seulement ajouté à l'objet $foo et non à la classe Foo.

Hmmm ... Je me demande si je pourrais implémenter un MooseX :: SingletonAttribute?


Quelques réponse précédente SO en utilisant MooseX::SingletonMethod:

Et aussi ce billet de blog peut-être d'utilisation et/ou de l'intérêt: Easy Anonymous Objects

/I3az/

+0

+1 Merci, bon à savoir sur 'MooseX :: SingletonMethod'. –

Questions connexes