观察Perl类中属性的更改

时间:2017-04-10 15:00:17

标签: perl oop watch mojolicious moo

任何人都可以提供一个代码示例,如何在类中设置变量更改的观察者?我尝试使用不同的功能(Scalar::Watchertrigger attribute of Moo)和OOP框架(Moo,Mojo :: Base)以及但都失败了。

以下是我失败的代码,可以更好地理解我的任务。在这个例子中,每当attr1改变时,我都需要更新attr2。

使用Mojo :: Base和Scalar :: Watcher:

package Cat;
use Mojo::Base -base;
use Scalar::Watcher qw(when_modified);
use feature 'say';

has 'attr1' => 1;
has 'attr2' => 2;

has 'test' => sub { # "fake" attribute for getting access to $self
  my $self = shift;
  when_modified $self->attr1, sub { $self->attr2(3); say "meow" };
};


package main;
use Data::Dumper;

my $me = Cat->new;
$me->attr1;
warn Dumper $me;
say $me->attr1(3)->attr2; # attr2 is still 2, but must be 3

使用Moo并触发:

package Cat;
use Moo;
use Scalar::Watcher qw(when_modified);
use feature 'say';

has 'attr1' => ( is => 'rw', default => 1, trigger => &update() ); 
has 'attr2' => ( is => 'rw', default => 1);

sub update {
  my $self = shift;
  when_modified $self->attr1, sub { $self->attr2(3); say "meow" }; # got error here: Can't call method "attr1" on an undefined value
};


package main;
use Data::Dumper;

my $me = Cat->new;
$me->attr1;
warn Dumper $me;
say $me->attr1(3)->attr2;

非常感谢任何建议。

1 个答案:

答案 0 :(得分:5)

Moo部分

  

此处出现错误:无法调用方法" attr1"在未定义的值

这是因为Moo希望代码引用为trigger的{​​{1}}。您正在将呼叫结果传递给hasupdate这里没有给你一个引用,而是告诉Perl忽略&函数的原型。你不想要那个。

相反,请使用update创建引用,并且不要添加括号\&foo。您不想调用该功能,您想引用它。

()

现在,一旦你完成了这项工作,你就不再需要Scalar :: Watcher了。触发器已经这样做了。每次has 'attr1' => ( is => 'rw', default => 1, trigger => \&update ); 更改时都会调用它。

attr1

如果你现在运行整个事情,它会工作一点,但崩溃时会出现这个错误:

  

找不到对象方法" attr2"通过包" 3" (也许你忘了加载" 3"?)

这是因为sub update { my $self = shift; $self->attr2(3); say "meow"; }; 会返回新值,而不会引用attr1。所有Moo / Moose存取器都是这样工作的。 $self不是对象,因此它没有方法3

attr2

相反,请将此作为两个电话。

#       this returns 1
#               |
#               V
say $me->attr1(3)->attr2;

这是一个完整的例子。

$me->attr1(3);
say $me->attr2;

输出:

package Cat;
use Moo;

use feature 'say';

has 'attr1' => ( is => 'rw', default => 1, trigger => \&update );
has 'attr2' => ( is => 'rw', default => 1 );

sub update {
    my $self = shift;
    $self->attr2(3);
    say "meow";
}

package main;
my $me = Cat->new;

say $me->attr2;
$me->attr1(3);
say $me->attr2;

为什么Scalar :: Watcher不能与Mojo一起使用

首先,Mojo :: Base不提供触发机制。但是你实现Scalar :: Watcher的方式无法工作,因为从未调用过1 meow 3 方法。我尝试在基于Mojo :: Base的类中test挂钩,在一个始终被调用的地方进行new调用。

这里的一切都只是推测。

我尝试了以下代码段,但它不起作用。我将在下面进一步解释原因。

when_modified

如您所见,现在这是package Cat; use Mojo::Base -base; use Scalar::Watcher qw(when_modified); use feature 'say'; has 'attr1' => '1'; has 'attr2' => 'original'; sub new { my $class = shift; my $self = $class->SUPER::new(@_); when_modified $self->{attr1}, sub { $self->attr2('updated'); say "meow" }; return $self; } 电话的一部分。代码确实被执行了。但它没有帮助。

观察者应该在那里的documentation of Scalar::Watcher states,直到变量超出范围。

  

如果在void上下文中调用when_modified,则观察者将是   活跃到$ variable的生命结束;否则,它会返回一个   引用取消器,取消该取消器时取消此观察者   垃圾收集。

但我们实际上并没有标量变量。如果我们尝试做

new

然后Perl在when_modified $self->foo 上对foo进行方法调用,$self将获得该调用的返回值。我也尝试进入上述对象的内部,但这也没有用。

我的XS不够强大,无法理解here发生了什么,但我认为在加入魔法时遇到了一些麻烦。它不能使用散列引用值。可能这就是为什么它被称为Scalar :: Watch。