I recently upgraded Moose to v1.15 and found that a set of modules I use no longer worked. The error I get is:
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
I can see several possible sources of error and would be grateful for advice on how to fix the problem.
The first bit of code for GOBO::Node looks like this:
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');
The roles used by this package also have attributes that are GOBO::Nodes, and the attribute 'source' mentioned in the error message is one of them.
part of the reason for having the coercion in GOBO::Node seems to be as a shortcut when creating a new node. Would it be better to use BUILDARGS rather than coerce?
where should I put the coercion if I want several packages to be able to use it? If I add the coercion to (e.g.) GOBO::Attributed, I get a warning that it already exists. Without the coercion, though, I get the warning above about not being able to coerce.
there is a separate package of subtypes; would it be better to create a subtype of GOBO::Node--e.g. GOBO::Node::ProtoNode--and a coercion, and use that for the attributes should be GOBO::Nodes?
Thank you for any help or advice on this problem!
The example code you pasted doesn't actually trigger the error. The source
attribute as written there won't try to coerce anything. I assume however that one of the roles you mention has an attribute with coerce => 1
defined.
In Moose types, and thus coercions, are global. When combined with the the fact that Moose builds a class dynamically you end up with the strange behavior you're seeing here. You'll need to move the definition of the coercion to someplace before the first use of the GOBO::Node
type. Typically this is done by creating a package of subtypes (which you note you already have) and including that as early as possible (via use
).
Simply moving the GOBO::Node
coercion definition into this package of subtypes, and making sure it's used everywhere the coercion is needed should fix your problem.
To answer your other questions:
In general I would recommend using coercion over BUILDARGS
because it is a much finer grained tool. The usage you're showing here is a text book example of proper coercion usage, so no real reason to change that.
As stated above, the typical answer is to build a Library package in a seperate namespace (MyApp::TypeLibrary
) and then include that package at the top of the classes you want your types available. Perl won't re-compile a package that it has already compiled, meaning the error you got about the coercion already existing won't be triggered in this case.
Based on the examples you've provided there is no need to create a new subtype, GOBO::Node should already work, and without a new subtype this is effectively the same answer as the last one. Yes use the subtype library.
I hope that helps.