开发者

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 the coerce.
  • Add the coerce flag to the field:

    has id => ( is => 'rw', isa => 'MyApp::Type::Field', coerce => 1 );
    
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜