2

我最近将 Moose 升级到 v1.15,发现我使用的一组模块不再起作用。我得到的错误是:

You cannot coerce an attribute (source) unless its type (GOBO::Node) has a coercion at
/opt/local/lib/perl5/site_perl/5.12.0/darwin-multi-2level/Moose/Meta/Role/Application/ToClass.pm line 142

我可以看到几个可能的错误来源,并感谢有关如何解决问题的建议。

GOBO::Node 的第一段代码如下所示:

package GOBO::Node;
[...]
extends 'GOBO::Base';
with 'GOBO::Labeled';
with 'GOBO::Attributed';

coerce 'GOBO::Node'
  => from 'Str'
  => via { new GOBO::Node(id=>$_) };

has 'source' => (is => 'rw', isa => 'GOBO::Node');

该包使用的角色也具有 GOBO::Nodes 属性,错误消息中提到的属性 'source' 就是其中之一。

  • 在 GOBO::Node 中进行强制的部分原因似乎是创建新节点时的捷径。使用 BUILDARGS 而不是强制使用会更好吗?

  • 如果我想要几个包能够使用它,我应该把强制放在哪里?如果我将强制添加到(例如)GOBO::Attributed,我会收到它已经存在的警告。但是,如果没有强制,我会收到上面关于无法强制的警告。

  • 有一个单独的子类型包;创建 GOBO::Node 的子类型会更好吗?例如 GOBO::Node::ProtoNode 和强制转换,并将其用于属性应该是 GOBO::Nodes?

感谢您对此问题的任何帮助或建议!

4

2 回答 2

9

您粘贴的示例代码实际上不会触发错误。那里写的source属性不会试图强制任何东西。但是,我假设您提到的角色之一具有已coerce => 1定义的属性。

在 Moose 中,类型和强制是全局性的。当结合 Moose 动态构建类的事实时,您最终会遇到您在此处看到的奇怪行为。在第一次使用该类型之前,您需要将强制的定义移动到某个地方。GOBO::Node通常这是通过创建一个子类型包(您注意到您已经拥有)并尽早包含它(通过use)来完成的。

只需将GOBO::Node强制定义移动到这个子类型包中,并确保在需要强制的任何地方都使用它,就可以解决您的问题。

要回答您的其他问题:

  • 一般来说,我会推荐使用强制转换,BUILDARGS因为它是一个更细粒度的工具。您在此处显示的用法是正确强制使用的教科书示例,因此没有真正的理由要更改它。

  • 如上所述,典型的答案是在单独的命名空间 ( MyApp::TypeLibrary) 中构建一个库包,然后将该包包含在您希望类型可用的类的顶部。Perl 不会重新编译它已经编译的包,这意味着在这种情况下不会触发你得到的关于已经存在的强制的错误。

  • 根据您提供的示例,无需创建新的子类型, GOBO::Node 应该已经可以工作,并且没有新的子类型,这实际上与上一个答案相同。是的,使用子类型库。

我希望这会有所帮助。

于 2010-10-15T20:09:57.733 回答
6

常见的解决方案是有一个单独的文件来声明类型约束及其强制。如果您需要确保加载特定的类——例如强制它——在你的强制函数中需要它。

所以,像:

package GOBO::Types;
use Moose::Util::TypeConstraints;
class_type 'GOBO::Node';
coerce 'GOBO::Node',
    from 'Str',
    via { require GOBO::Node; GOBO::Node->new(id => $_) };

您可以在任何地方使用此类型库而不必担心加载顺序,并且您可以确保所有类型都已定义 - 如果您有未定义的类型,则尤其重要class_type,因为 Moose 如何尝试解析类型约束名称它还没有被定义。

在这里使用强制而不是BUILDARGS绝对是正确的做法;它更可重用。

于 2010-10-15T20:15:25.130 回答