我想在Moose中创建一个结构化类型,可以用作另一个Moose属性的类型。例如,我希望能够创建一个name
属性,该属性具有自己的value
和error
属性。
因此,我想了解实现这一目标的最佳方法。我通过定义一个简单的Moose类来表示一个通用的Field
对象,从而创建了一个工作示例。这具有value
和error
属性。然后我为Person
对象创建了另一个Moose类。它具有id
和name
属性,两者都属于Field
类型:
定义通用字段对象:
package MyApp::Type::Field;
use Moose;
use namespace::autoclean;
has 'value' => ( is => 'rw' );
has 'error' => ( is => 'rw', isa => 'Str' );
__PACKAGE__->meta->make_immutable;
1;
定义使用字段对象的Person对象:
package MyApp::Person;
use Moose;
use namespace::autoclean;
use MyApp::Type::Field;
has 'id' => ( is => 'rw', isa => 'MyApp::Type::Field' );
has 'name' => ( is => 'rw', isa => 'MyApp::Type::Field' );
__PACKAGE__->meta->make_immutable;
1;
使用Person对象执行某些操作:
package MyApp::Test;
use Moose;
use namespace::autoclean;
use MyApp::Person;
my $person = MyApp::Person->new();
# This works.
$person->id( MyApp::Type::Field->new() );
$person->id->value( 1 );
$person->id->error( 'Not found' );
# This fails as the name object has not yet been defined.
$person->name->value( 'Dave' );
# Can't call method "value" on an undefined value at ...
__PACKAGE__->meta->make_immutable;
1;
这样做有效,但在MyApp::Test
我希望能够直接访问此人value
和error
的{{1}}和name
属性首先必须为每个人的属性实例化一个新的id
对象。
或者,换句话说,如果MyApp::Type::Field
类的用户不必执行此操作,我会更喜欢它:Person
,然后才能使用$person->id( MyApp::Type::Field->new() );
属性。
有没有一种很好的清洁方式可以达到这个目的?
答案 0 :(得分:1)
难道你不能简单地为属性提供default
吗?
has 'id' => (
is => 'rw',
isa => 'MyApp::Type::Field',
default => sub { MyApp::Type::Field->new }
);
...或在BUILD
中执行等效操作。
答案 1 :(得分:1)
你也可以尝试强制:
coerce 'MyApp::Type::Field'
=> from 'Int'
=> via { MyApp::Type::Field->new( value => shift ) }
;
这是唯一的:
$person->id( 1 );
设置它。虽然设置错误仍然需要按照你的方式进行。
我可能应该提到你需要做以下事情:
Moose::Util::TypeConstraints
添加到您放置coerce
的包中。 将coerce
标志添加到字段中:
has id => ( is => 'rw', isa => 'MyApp::Type::Field', coerce => 1 );