我最近将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 ::节点?
感谢您对此问题的任何帮助或建议!
答案 0 :(得分:9)
您粘贴的示例代码实际上并未触发错误。写在那里的source
属性不会试图强制任何东西。但我假设你提到的一个角色有一个定义了coerce => 1
的属性。
在驼鹿类型中,因此强制是全球性的。当与Moose动态构建一个类的事实相结合时,你最终会遇到你在这里看到的奇怪行为。在首次使用GOBO::Node
类型之前,您需要将强制定义移动到某个地方。通常,这是通过创建一个子类型包(您已经注意到它已经拥有)并尽早包含(通过use
)来完成的。
只需将GOBO::Node
强制定义移动到这个子类型包中,并确保它在任何地方都需要强制使用才能解决您的问题。
回答您的其他问题:
一般情况下,我建议对BUILDARGS
使用强制,因为它是一个更精细的工具。您在此处显示的用法是正确强制使用的教科书示例,因此没有真正的理由来改变它。
如上所述,典型的答案是在单独的命名空间(MyApp::TypeLibrary
)中构建一个库包,然后将该包包含在您希望类型可用的类的顶部。 Perl不会重新编译它已经编译的包,这意味着在这种情况下不会触发你已经存在的强制错误。
根据您提供的示例,无需创建新的子类型,GOBO :: Node应该已经可以使用,并且没有新的子类型,这实际上与最后一个相同。是使用子类型库。
我希望有所帮助。
答案 1 :(得分: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
绝对是正确的做法;它更可重复使用。