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::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;

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?

Answers


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 );
    

Need Your Help

Installs to Root of C: Instead of \Program Files\

visual-studio-2010 windows-installer

I built an application and created an installer project for it using Visual Studio. Everywhere I've ever installed the application, it works perfectly, except on one particular 2008 R2 server.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.