2012-09-18 38 views
3

在试图回答How to instantiate Moose classes from a big hash时,我想我已经击中了另一个地方,我没有完全理解穆斯型强制。出于某种原因,下面的代码发出警告:从ArrayRef强制ArrayRef [MyClass] [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文档:

LOAD订单问题,

因为驼鹿类型是在运行时定义,你可能会碰到的加载顺序问题。特别是,您可能希望在定义类型之前使用类的类型约束。

为了改善这个问题,我们建议在一个模块中定义所有的自定义类型,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::类。

相关问题