3

在尝试回答如何从大哈希中实例化 Moose 类时,我想我遇到了另一个我不完全理解 Moose 类型强制的地方。出于某种原因,以下代码会发出警告:

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;

这应该怎么做?PS我试着简单地做

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

但它死得很惨。

4

2 回答 2

4

好吧,它并没有完全成功,当您尝试使用coerce => 1. 这就是为什么

你不能通过 coerce => 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);

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

于 2012-09-18T22:23:00.857 回答
3

来自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::课都放进去。

于 2012-09-20T16:17:11.417 回答