Следите за изменениями атрибута Moose - PullRequest
0 голосов
/ 09 ноября 2018

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

Давайте предположим следующий код:

has _changed  => ( is => 'rw' , isa=>'Bool' ) ;
has attribute => ( 
    is=>'rw', isa=>'Maybe[HashRef]', 
    default => sub { { a => 1 , b => 2 } },     
    trigger => sub { shift->_changed(1) } 
) ;

триггер работает как положено, устанавливая значение атрибута через мутатор:

$self->attribute({ a => 2 , b => 2 }) ; # OK

, но, устанавливая значение непосредственно с помощью клавиши, триггер не срабатывает (конечно):

$self->attribute->{a} = 3 ; # KO

Я отказался от идеи создания (и сравнения) дайджеста содержимого сериализованного атрибута, потому что это может быть очень большой хэш-адрес с несколькими уровнями вложенности, и создание дайджеста при каждом доступе к атрибуту может привести к проблеме производительности.

Может ли быть возможным решение связанного хеш-значения (в качестве значения атрибута)? Любая идея или предложение очень ценится.

ПРИМЕЧАНИЕ. Структура содержимого hashref неизвестна (я пишу класс ORM, поэтому структура может варьироваться в зависимости от документов, хранящихся на стороне базы данных NOSQL).

Ответы [ 2 ]

0 голосов
/ 10 ноября 2018

Следующий подход, основанный на модуле Tie :: Trace Perl, демонстрирует, как легко наблюдать за изменением атрибута Moose, даже если он был изменен посредством прямого доступа к содержащемуся хеш-файру вместо использования соответствующего установщика метод.

package Test::Document ;

use Mouse ;
use Tie::Trace qw<watch> ;

has _changed => ( is => 'rw', isa => 'Bool' ) ;
...
has value => (
    is      => 'rw', isa => 'HashRef',
    default => sub { { } },
    trigger => sub { shift->_changed( 1 )  }
) ;

sub BUILD {
    my ( $self ) = @_ ;
    $self->_changed( 0 ) ; # reset flag
    watch %{ $self->{ value } } , debug=> sub {
        $self->_changed(1)
    };
    return $self ;
}

package main ;

my $doc = Test::Document->new( value => { a => 1 , b => { c => 3 } } ) ;

my $x = $doc->value->{ b }->{ e } ; # not changed

$doc->value->{ b }->{ e } = 4 ; # changed

$doc->_changed(0);
delete $doc->value->{ b }->{ e } ; # changed

$doc->_changed(0);
$doc->value({ a => 1 }) ; # changed

ПРОФИ: Работает:)

CONS: Рекурсивно-привязанный подход к хэшам с большим количеством ключей и уровней вложенности может привести к проблемам с производительностью. Я должен сделать какой-то тест.

ПРИМЕЧАНИЕ. Я пробовал использовать магические переменные, но распространение скалярного контекста с использованием синтаксиса, подобного sub()->{a}->{b}, вызывает событие store, даже если нет (явного) назначения. Предложения приветствуются.

0 голосов
/ 09 ноября 2018

После того, как вы изменили хэш-ссылку напрямую, а не с помощью методов доступа, Moose больше не участвует. Если ваши атрибуты возвращают ссылку на связанный хеш, это будет единственной стратегией для внесения изменений в наблюдаемый хеш, но это не особенно привлекательное решение. Связанные переменные встречаются редко и могут вызывать ошибки в некотором коде. Их сравнительно сложно реализовать. И они подразумевают снижение производительности для каждого хеш-доступа.

Тщательно продумайте, можете ли вы изменить свой дизайн, чтобы избежать раскрытия внутреннего хэша. Например. иметь метод получения, который возвращает только (мелкую) копию хэша, и метод установки для отдельных элементов в хэше. Возможно, вы сможете автоматически сгенерировать некоторые из этих аксессоров, используя механизмы handles и traits, например, см. Moose :: Meta :: Attribute :: Native :: Trait :: Hash .

...