2012-09-18 2 views
3

При попытке ответить How to instantiate Moose classes from a big hash, я думаю, что попал в другое место, где я не совсем понимаю принуждения типа Moose. По некоторым причинам приведенный ниже код вызывает предупреждения:Принуждение ArrayRef [MyClass] от ArrayRef [HashRef]

You cannot coerce an attribute (departments) unless its type (ArrayRef[Company::Department]) has a coercion at ./test.pl line 12. 
You cannot coerce an attribute (employees) unless its type (ArrayRef[Company::Person]) has a coercion at ./test.pl line 23. 

, но затем это удается.

#!/usr/bin/env perl 

use warnings; 
use strict; 

package Company; 
use Moose; 
use Moose::Util::TypeConstraints; 

has 'id' => (is => 'ro', isa => 'Num'); 
has 'name' => (is => 'ro', isa => 'Str'); 
has 'departments' => (is => 'ro', isa => 'ArrayRef[Company::Department]', coerce => 1); 

coerce 'ArrayRef[Company::Department]', 
    from 'ArrayRef[HashRef]', 
    via { [ map { Company::Department->new($_) } @$_ ] }; 

package Company::Department; 
use Moose; 

has 'id' => (is => 'ro', isa => 'Num'); 
has 'name' => (is => 'ro', isa => 'Str'); 
has 'employees' => (is => 'ro', isa => 'ArrayRef[Company::Person]', coerce => 1); 

package Company::Person; 
use Moose; 
use Moose::Util::TypeConstraints; 

has 'id'   => (is => 'ro', isa => 'Num'); 
has 'name' => (is => 'ro', isa => 'Str'); 
has 'age'  => (is => 'ro', isa => 'Num'); 

coerce 'ArrayRef[Company::Person]', 
    from 'ArrayRef[HashRef]', 
    via { [ map { Company::Person->new($_) } @$_ ] }; 

package main; 

my %hash = (
    company => { 
     id => 1, 
     name => 'CorpInc', 
     departments => [ 
      { 
       id => 1, 
       name => 'Sales', 
       employees => [ 
        { 
         id => 1, 
         name => 'John Smith', 
         age => '30', 
        }, 
       ], 
      }, 
      { 
       id => 2, 
       name => 'IT', 
       employees => [ 
        { 
         id => 2, 
         name => 'Lucy Jones', 
         age => '28', 
        }, 
        { 
         id => 3, 
         name => 'Miguel Cerveza', 
         age => '25', 
        }, 
       ], 
      }, 
     ], 
    } 
); 

my $company = Company->new($hash{company}); 
use Data::Dumper; 
print Dumper $company; 

Как это сделать? Постскриптум Я пробовал просто делать

coerce 'Company::Department', 
    from 'HashRef', 
    via { Company::Department->new($_) }; 

но он умер ужасно.

ответ

3

Ну, это не удается полностью, и вы должны это почувствовать, когда попытаетесь обновить эти поля coerce => 1. Это why:

Вы не можете передать принуждать => 1, если тип ограничения атрибута не имеет с принуждением

Раньше это было принято, и он вроде работал, за исключением того, что если вы попытаетесь установите атрибут после создания объекта , вы получите ошибку времени выполнения. Теперь вы получите сообщение об ошибке при попытке определить атрибут.

Тем не менее, я думаю, что найти способ, чтобы исправить это, вводя подтипы, первый, и изменение порядка пакетов, второй:

package Company::Person; 
use Moose; 
use Moose::Util::TypeConstraints; 

subtype 'ArrayRefCompanyPersons', 
    as 'ArrayRef[Company::Person]'; 

coerce 'ArrayRefCompanyPersons', 
    from 'ArrayRef[HashRef]', 
    via { [ map { Company::Person->new($_) } @$_ ] }; 

has 'id'   => (is => 'ro', isa => 'Num'); 
has 'name' => (is => 'ro', isa => 'Str'); 
has 'age'  => (is => 'ro', isa => 'Num'); 

package Company::Department; 
use Moose; 

has 'id' => (is => 'ro', isa => 'Num'); 
has 'name' => (is => 'ro', isa => 'Str'); 
has 'employees' => (is => 'ro', isa => 'ArrayRefCompanyPersons', coerce => 1); 

package Company; 
use Moose; 
use Moose::Util::TypeConstraints; 

subtype 'ArrayRefCompanyDepartments', 
    as 'ArrayRef[Company::Department]'; 

coerce 'ArrayRefCompanyDepartments', 
    from 'ArrayRef[HashRef]', 
    via { [ map { Company::Department->new($_) } @$_ ] }; 

has 'id' => (is => 'ro', isa => 'Num'); 
has 'name' => (is => 'ro', isa => 'Str'); 
has 'departments' => (is => 'ro', isa => 'ArrayRefCompanyDepartments', coerce => 1); 

Остальная часть кода такая же, как в вашей версии. Это работает без каких-либо предупреждений, и более-менее-менее ведет себя как (опять же, я думаю), должно быть.

+0

Да, я должен был видеть, что это проблема с загрузкой. Я больше привык объявлять все в отдельных файлах, поэтому «use» (с его неявным блоком «BEGIN») исправляет для меня все. Наверное, я шел слишком быстро. Благодаря! –

2

Moose::Manual::Type От Docs:

НАГРУЗКИ ЗАКАЗ ВОПРОСЫ

Поскольку типы Муз определены во время выполнения, вы можете столкнуться с проблемами порядка нагрузки. В частности, вы можете использовать ограничение типа класса до того, как этот тип будет определен.

Чтобы улучшить эту проблему, мы рекомендуем определить все ваши пользовательские типы в одном модуле MyApp :: Types, а затем загрузить этот модуль во все ваши другие модули.


Так, чтобы добавить к raina77ow подтип & заказа пакета ответа (+1) Я рекомендую создать Company::Types модуль:

package Company::Types; 
use Moose; 
use Moose::Util::TypeConstraints; 

subtype 'CompanyDepartments' 
    => as 'ArrayRef[Company::Department]'; 

subtype 'CompanyPersons' 
    => as 'ArrayRef[Company::Person]'; 

coerce 'CompanyDepartments' 
    => from 'ArrayRef[HashRef]' 
    => via { 
     require Company::Department; 
     [ map { Company::Department->new($_) } @$_ ]; 
    }; 

coerce 'CompanyPersons' 
    => from 'ArrayRef[HashRef]' 
    => via { require Company::Person; [ map { Company::Person->new($_) } @$_ ] }; 

1; 

, а затем положить use Company::Types во всех ваших Company:: классов.

Смежные вопросы