2010-12-01 32 views
2

Je voudrais créer un type structuré dans Moose qui peut être utilisé comme type pour un autre attribut Moose. Par exemple, j'aimerais pouvoir créer un attribut name qui possède ses propres attributs et error.Moose structured types

Je voudrais donc connaître le meilleur moyen d'y parvenir. J'ai créé un exemple de travail en définissant une classe Moose simple pour représenter un objet générique Field. Cela a les attributs value et error. J'ai ensuite créé une autre classe Moose pour l'objet Person. Cela a id et name attributs, qui sont tous deux de type Field:

Définir un objet champ générique:

package MyApp::Type::Field; 
use Moose; 
use namespace::autoclean; 

has 'value' => (is => 'rw'); 
has 'error' => (is => 'rw', isa => 'Str'); 

__PACKAGE__->meta->make_immutable; 
1; 

Définir un objet Personne qui utilise l'objet de champ:

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 quelque chose avec l'objet Personne:

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; 

Cela fonctionne, mais dans MyApp::Test je voudrais être en mesure d'accéder directement aux attributs et error de la personne name et id de la personne sans devoir d'instancier un nouvel objet MyApp::Type::Field pour chacun des attributs de la personne.

Ou, pour le dire autrement, je préférerais que l'utilisateur de la classe Person n'a pas eu à faire: $person->id(MyApp::Type::Field->new()); avant de pouvoir utiliser l'attribut id.

Existe-t-il une façon agréable et propre d'y parvenir?

Répondre

1

Pourriez-vous simplement fournir un default pour les propriétés?

has 'id' => (
    is => 'rw', 
    isa => 'MyApp::Type::Field', 
    default => sub { MyApp::Type::Field->new } 
); 

... ou de faire l'équivalent en BUILD.

+0

Ah oui, tout d'un coup, il semble si évident. :-) Merci de votre aide. – Mike

1

Vous pouvez également essayer la coercition:

coerce 'MyApp::Type::Field' 
    => from 'Int' 
    => via { MyApp::Type::Field->new(value => shift) } 
    ; 

De cette façon, il est seulement:

$person->id(1); 

pour le définir. Bien que la définition de l'erreur devrait toujours aller comme vous l'avez.


Je devrais probablement avoir mentionné que vous devez faire ce qui suit:

  • Ajouter Moose::Util::TypeConstraints au package où vous mettez le coerce.
  • Ajouter le drapeau coerce sur le terrain:

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

Merci pour ça. En fait, j'ai déjà ajouté une contrainte de Str et HashRef, ce qui m'a permis de créer une nouvelle personne comme ceci: '$ person = MyApp :: Person-> new (id => {value => 1, error => ' Pas trouvé '}, name => {value =>' Dave '}) ' – Mike