Как в Moose изменить атрибут каждый раз, когда он установлен?

Если у вас есть атрибут, который нужно изменить каждый раз, когда он установлен, есть ли простой способ сделать это, за исключением написания аксессуара самостоятельно и непосредственной работы с содержимым $self, как это сделано в этом примере?

package Foo;
use Moose;

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

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

Я рассматривал trigger, но, похоже, требовался тот же подход.

Работает напрямую с хеш-ссылкой в ​​$self, что считается плохой практикой в ​​Moose , или я беспокоюсь о том, что не проблема?


person FMc    schedule 12.09.2009    source источник


Ответы (4)


Я не уверен, какая модификация вам нужна, но вы можете добиться того, что вам нужно, используя приведение типов:

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,
);

Если вы воспользуетесь этим подходом, не все значения будут изменены. Все, что проходит проверку как ModStr, будет использоваться напрямую:

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

Эта слабость может быть нормой или может сделать этот подход непригодным для использования. В правильных обстоятельствах это может быть даже преимуществом.

person daotoad    schedule 12.09.2009

Вы можете использовать модификатор метода «вокруг». Что-то вроде этого:

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

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

И да, работа напрямую с хеш-значениями считается плохой практикой.

Также не думайте, что представленный мной вариант обязательно правильный. Использование подтипов и принуждения будет правильным решением большую часть времени - если вы подумаете о своем параметре с точки зрения типа, который может быть повторно использован в вашем приложении, это приведет к гораздо лучшему дизайну, чем произвольные модификации, которые могут быть сделано с использованием слова «вокруг». См. Ответ от @daotoad.

person aaa90210    schedule 13.09.2009
comment
Чтобы это работало, вам нужно объявить писателя при определении атрибута. Например, имеет 'bar' = ›(is =› 'rw', isa = ›'Str', writer =› 'set_bar') - person draegtun; 13.09.2009
comment
re: hash values ​​плохая практика: это нормально в триггерах и примерах, которые я видел (в списке рассылки Moose), действительно это подтверждают. - person draegtun; 13.09.2009
comment
draegtun: если Moose не изменил его, писателем по умолчанию является имя атрибута. Вы можете добавить это, если не хотите указывать писателя. - person Jeremy Wall; 14.09.2009
comment
@ Джереми Уолл: Без объявления отдельного писателя / читателя это повлияет на получатель по умолчанию, а также на установщик атрибута по умолчанию, поэтому это вызовет проблемы (выдает здесь ошибку компиляции для меня). - person draegtun; 14.09.2009


Если работа с хешем напрямую вызывает у вас беспокойство, вы можете указать альтернативного писателя, а затем использовать его из своего собственного «общедоступного» писателя с соответствующим именем.

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);
}

Это или триггеры могут показаться мне хорошим подходом в зависимости от того, когда и как вам нужно манипулировать аргументами.

(отказ от ответственности: непроверенный код, написанный из памяти, просмотр SO на нетбуке с нестабильным доступом к краям)

person jsoverson    schedule 12.09.2009
comment
Это хорошее чистое решение, хотя оно не работает с конструкторами. - person mikegrb; 13.09.2009
comment
@mikegrb: Я не уверен, что именно вы хотите делать с конструкторами, но вы можете указать, где значение присваивается через конструктор, используя модификатор атрибута init_arg, и / или вы можете выполнить некоторые проверки в методе BUILDARGS. - person Ether; 27.10.2009