在运行时修改Moose :: Role的属性

时间:2013-12-19 22:10:06

标签: perl moose mop

我有一个Moose :: Role,它包含一个网络客户端作为属性:

package Widget;
use Moose::Role;

has 'network_thingy' => (
    isa => Maybe[ThingyClient],
);

当然,我有几个具体的Moose类使用这个角色:

package BlueWidget;
use Moose;
with 'Widget';

现在谈到Widgets的功能测试。我们有能力创建ThingyServer个对象,直接使用ThingyServer对象而不是旋转守护进程并让ThingyClient通过网络连接它会更快更好。因为ThingyClient& ThingyServer方便地使用完全相同的方法,这应该很容易实现。但是当然,当测试最终构建一个BlueWidget时,Moose要求我使用ThingyClient。

我做了一些研究,并且遇到了Moose :: Meta文档。似乎很完美!所以这是测试代码:

my $metarole = Moose::Meta::Role->initialize('Widget');

// first remove the old attribute
$metarole->remove_attribute('network_thingy');

我打算添加一个新属性,但我想我会检查角色的状态&先上课。现在,如果我抛弃$ metarole,它看起来很棒。再也没有network_thingy属性了。但是,如果我构建一个BlueWidget类,或者只是在元类内部达到峰值......

$metaclass = Moose::Meta::Class->initialize('BlueWidget');
diag Dumper ($metaclass);

......确定network_thingy仍然存在。这根本不是我的预期。如何在运行时修改/删除/替换Widget角色的属性?

1 个答案:

答案 0 :(得分:2)

当一个类使用角色时,属性会从角色复制到该类。如果您随后更改了角色中的属性,则该类中的副本不受影响。

因此,您需要遍历已经使用该角色的类,并更改每个类中的属性。 Moose :: Meta :: Role中有一个consumers方法可以帮助您获取已经消耗该角色的类列表,但是它只涵盖直接消耗该角色的类,而不是说,那些的子类。

如果类已经变为不可变(__PACKAGE__->meta->make_immutable),则在修改属性之前,您需要再次使它们可变。

总的来说,改变角色模块(即编辑文件)可能是个更好的主意;不要尝试在运行时调整属性。也许将isa设置为duck_type类型约束?