0

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

package Widget;
use Moose::Role;

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

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

package BlueWidget;
use Moose;
with 'Widget';

现在涉及到小部件的功能测试。我们有能力创建ThingyServer对象,直接使用 ThingyServer 对象而不是启动守护程序并让 ThingyClient 通过网络连接到它会更快,整体上也很棒。由于 ThingyClient 和 ThingyServer 方便地具有完全相同的方法,这应该很容易实现。但当然,Moose 要求我在测试最终构建 BlueWidget 时使用 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 角色的属性?

4

1 回答 1

3

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

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

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

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

于 2013-12-19T22:32:40.567 回答