Coercing ArrayRef[MyClass] from 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($_) };

但它死得很惨。

好吧,它并没有完全成功,当您尝试使用 coerce => 1 更新这些字段时,您应该感觉到它。这就是为什么:

不能传递强制 => 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);

其余代码与您的版本中相同。这在没有任何警告的情况下工作,并且更多的行为就像(再次,我认为)它应该是。

来自Moose::Manual::Type文档:

加载订单问题

由于 Moose 类型是在运行时定义的,因此您可能会遇到加载顺序问题。特别是,您可能希望在定义类的类型约束之前使用该类型。

为了改善此问题,我们建议在一个模块 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::课上。

最新更新