观察Moose属性的更改

时间:2018-11-09 00:42:02

标签: perl moose

当通过引用更改属性的内容而不是通过mutator设置其值时,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(作为属性值)可能是一种解决方案? 任何想法或建议都非常感谢。

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

2 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:2)

以下基于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

PROS:有效:)

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

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