2010-10-22 10 views
6

Estoy escribiendo un módulo para un objeto alce. Me gustaría permitir que un usuario que use este objeto (o yo mismo ...) agregue algunos campos sobre la marcha como lo desee. No puedo definir estos campos a priori ya que simplemente no sé lo que serán.¿Cómo puedo agregar datos de manera flexible a los objetos Moose?

que actualmente simplemente añade un solo campo llamado extra de tipo hashref que es está establecido en rw, por lo que los usuarios pueden simplemente poner cosas en ese resumen:

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

Esto funciona. Pero ... ¿es esta una práctica común? ¿Alguna otra idea (posiblemente más elegante)?

Nota: No deseo crear otro módulo que extienda este, esto realmente solo para cosas sobre la marcha que me gustaría agregar.

Respuesta

6

que probablemente hacerlo a través de los rasgos nativos:

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

sobre un objeto que tendría que utilizar esto como el siguiente:

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 caso de uso común para cosas como esta es la adición opcional datos introspectibles a clases de excepción y mensaje.

+0

+1 ¡Eso es realmente genial! –

+0

¿Puedo hacer que el descriptor de acceso no funcione correctamente si trato de leer (no configurar) un campo que no existe? –

+0

Puede ajustar el descriptor de acceso 'custom_field' con un modificador de alrededor, verificar los argumentos y croar si 'has_custom_field' devuelve falso. – phaylon

4

Si no ha hecho la clase inmutable (hay un performance penalty por no hacer eso, además de mis preocupaciones sobre el cambio de las definiciones de clase sobre la marcha), debería poder hacer eso al obtener la metaclase para el objeto (usando $meta = $object->meta) y usando el método add_attribute en 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"; 

Salida:

42 
5
+0

+1 Interesante. Gracias. Por lo general, configuro la clase como inmutable (siguiendo las mejores prácticas de Moose). –

3

Sólo en caso de que desee añadir un método a un objeto y no a toda la clase a continuación, echar un vistazo a algo así como 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! 

Así que en el método anterior baz sólo se agrega al objeto $foo y no a la clase Foo.

Hmmm ... Me pregunto si podría implementar un MooseX :: SingletonAttribute?


Algunos anterior para responder usando MooseX::SingletonMethod:

Y también esta entrada del blog tal vez del uso y/o interés: Easy Anonymous Objects

/I3az/

+0

+1 Gracias, es bueno aprender sobre 'MooseX :: SingletonMethod'. –

Cuestiones relacionadas