2009-09-12 10 views
18

Si tiene un atributo que necesita modificarse cada vez que se configura, ¿hay alguna forma hábil de hacer esto sin escribir el programa de acceso usted mismo y rebuscar directamente con el contenido de $self, como se hace en este ejemplo?En Moose, ¿cómo modifico un atributo cada vez que se configura?

package Foo; 
use Moose; 

has 'bar' => (
    isa => 'Str', 
    reader => 'get_bar', 
); 

sub set_bar { 
    my ($self, $bar) = @_; 
    $self->{bar} = "modified: $bar"; 
} 

que considera trigger, pero parecía requerir el mismo enfoque.

Está trabajando directamente con la referencia de hash en $self considerada mala práctica en Moose, o estoy preocupado por un problema?

Respuesta

8

No estoy seguro de qué tipo de modificación que necesita, pero que podría ser capaz de lograr lo que se necesita por utilizando el tipo de coacción:

package Foo; 
use Moose; 

use Moose::Util::TypeConstraints; 

subtype 'ModStr' 
    => as 'Str' 
    => where { /^modified: /}; 

coerce 'ModStr' 
    => from 'Str' 
    => via { "modified: $_" }; 

has 'bar' => ( 
    isa => 'ModStr', 
    is => 'rw', 
    coerce => 1, 
); 

Si utiliza este método, no se modificarán todos los valores. Cualquier cosa que pasa la validación como ModStr se puede utilizar directamente:

my $f = Foo->new(); 
$f->bar('modified: bar'); # Set without modification 

Esta debilidad puede estar bien o puede que este enfoque sea inutilizable. En las circunstancias correctas, incluso podría ser una ventaja.

6

creo que el uso de la referencia a un hash está bien dentro de un trigger así:

package Foo; 
use Moose; 

has 'bar' => ( 
    isa => 'Str', 
    is => 'rw', 
    trigger => sub { $_[0]->{bar} = "modified: $_[1]" }, 
); 

El gatillo también se activa cuando la barra arg pasó con el constructor. Esto no sucederá si defines tu propio método set_bar o con un modificador de método.

re: referencia de hash - En general, creo que es mejor seguir con el atributo setters/getters a menos que (al igual que con el trigger anterior) no haya una alternativa fácil.

Por cierto, usted puede encontrar este recent post about triggers por nothingmuch interesante.

+0

Mira la Moose :: Manual :: Atributos de disparadores - http://search.cpan.org/~drolsky/Moose-0.88/lib/Moose/Manual/Attributes.pod#Triggers –

3

Si le preocupa el uso del hash directamente, puede especificar un escritor alternativo y luego usarlo desde su propio escritor "público" apropiadamente nombrado.

package Foo; 
use Moose; 

has 'bar' => (
    isa => 'Str', 
    reader => 'get_bar', 
    writer => '_set_bar', 
); 

sub set_bar { 
    my $self = shift; 
    my @args = @_; 
    # play with args; 
    return $self->_set_bar(@args); 
} 

esto, o disparadores, me golpearía como un buen enfoque dependiendo de cuándo y cómo tiene que ser la manipulación de los argumentos.

(exención de responsabilidad: Código no probado escrito de la memoria, la navegación SO en un netbook con acceso borde escamosa)

+0

Este es un bonito, limpio solución, aunque no funcionará con los constructores. – mikegrb

+0

@mikegrb: No estoy seguro de qué es exactamente lo que quiere hacer con los constructores, pero puede especificar dónde se asigna un valor mediante un constructor utilizando el modificador de atributo 'init_arg', y/o puede hacer algunas comprobaciones en' BUILDARGS 'método. – Ether

9

Puede usar el modificador de método 'alrededor'. Algo como esto:

has 'bar' => (
    isa => 'Str', 
    reader => 'get_bar', 
    writer => 'set_bar' 
); 

around 'set_bar' => sub { 
    my ($next, $self, $bar) = @_; 
    $self->$next("Modified: $bar"); 
}; 

Y sí, trabajar directamente con los valores hash se considera una mala práctica.

Además, no asuma que la opción que presenté es necesariamente la correcta. El uso de subtipos y coerción va a ser la solución correcta la mayor parte del tiempo; si piensas en tu parámetro en términos de un tipo que posiblemente se pueda reutilizar en toda la aplicación, el diseño será mucho mejor que el tipo de modificaciones arbitrarias que se pueden realizar. hecho usando 'alrededor'. Ver respuesta de @daotoad.

+1

Para que esto funcione, debe declarar un escritor al definir el atributo. Por ejemplo, tiene 'bar' => (es => 'rw', isa => 'Str', escritor => 'set_bar') – draegtun

+0

re: hash values ​​"mala práctica": está bien en desencadenantes y ejemplos que he visto (en la lista de correo de Moose) reafirman esto. – draegtun

+0

draegtun: a menos que Moose lo haya cambiado, el escritor predeterminado es el nombre del atributo. Puedes ponerlo sobre eso si no quieres especificar el escritor. –

Cuestiones relacionadas