Moose structured types
I'd like to create a structured type in Moose that can be used as the type for another Moose attribute. For example, I'd like to be able to create a name
attribute which has its own value
and error
attributes.
I would therefore like to know the best way of achieving this. I have created a working example by defining a simple Moose class to represent a generic Field
object. This has the value
and error
attributes. I have then created another Moose class for the Person
object. This has id
and name
attributes, both of which are of type Field
:
Define a generic field object:
package MyApp::Type::Field;
use Moose;
use namespace::autoclean;
has 'value' => ( is => 'rw' );
has 'error' => ( is => 'rw', isa => 'Str' );
__PACKAGE__->meta->make_immutable;
1;
Define a Person object which uses the field object:
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;
Do something with the Person object:
package MyApp::Test;
use Moose;
use namespace::autoclean;
use MyApp::Pers开发者_如何学运维on;
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;
This works, but in MyApp::Test
I would like to be able to directly access the value
and error
attributes of the person's name
and id
without first having to instantiate a new MyApp::Type::Field
object for each of the person's attributes.
Or, to put it another way, I'd prefer it if the user of the Person
class did not have to do this: $person->id( MyApp::Type::Field->new() );
before being able to use the id
attribute.
Is there a nice clean way that I can achieve this?
Couldn’t you simply supply a default
for the properties?
has 'id' => (
is => 'rw',
isa => 'MyApp::Type::Field',
default => sub { MyApp::Type::Field->new }
);
…or do the equivalent in BUILD
.
You also might try coercion:
coerce 'MyApp::Type::Field'
=> from 'Int'
=> via { MyApp::Type::Field->new( value => shift ) }
;
That way it's only:
$person->id( 1 );
to set it. Although setting the error would still need to go the way you have it.
I probably should have mentioned that you need to do the following:
- Add
Moose::Util::TypeConstraints
to the package where you put thecoerce
. Add the
coerce
flag to the field:has id => ( is => 'rw', isa => 'MyApp::Type::Field', coerce => 1 );
精彩评论