注意 Moose 属性的变化

Watch for changes of a Moose attribute

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

触发器按预期工作,通过 mutator 设置属性值:

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

但是通过它的键直接设置一个值然后触发器不会触发(当然):

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

我放弃了创建(和比较)序列化属性内容摘要的想法,因为它可能是一个非常巨大的具有多个嵌套级别的哈希引用,并且在每个属性访问时制作摘要会产生性能问题。

绑定的 hashref(作为属性值)可能是一种可能的解决方案? 非常感谢任何想法或建议。

注意:包含的 hashref 的结构未知(我正在编写一个 ORM class,因此该结构可能会因存储在 NOSQL 数据库端的文档而异)。

一旦您直接更改哈希引用而不是使用访问器方法,Moose 就不再涉及。让你的属性 return 引用绑定哈希将是更改哈希可观察值的唯一策略,但这并不是一个特别有吸引力的解决方案。绑定变量很少见,并且可能会触发某些代码中的错误。它们相对难以实施。它们意味着每次哈希访问都会产生性能开销。

认真考虑是否可以更改设计以避免暴露内部哈希。例如。有一个 getter 只有 return 散列的(浅)副本,以及散列中单个元素的 setter。您可以使用 handlestraits 机制自动生成其中一些访问器,例如参见 Moose::Meta::Attribute::Native::Trait::Hash

以下基于 Tie::Trace Perl 模块的方法演示了如何轻松观察 Moose 属性更改,即使是通过直接访问包含的 hashref 而不是使用适当的 setter 方法。

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

优点:有效:)

缺点:在具有大量键和嵌套级别的散列上,递归绑定方法可能会产生性能问题。我必须做一些基准测试。

注意:我尝试使用魔法变量,但使用 sub()->{a}->{b} 等语法的标量上下文传播会强制触发 store 事件,即使没有(显式)赋值也是如此。欢迎提出建议。