首页 文章

从ArrayRef [HashRef]强制执行ArrayRef [MyClass]

提问于
浏览
3

在试图回答How to instantiate Moose classes from a big hash时,我想我已经到了另一个我不完全理解穆斯式强制的地方 . 出于某种原因,以下代码发出警告:

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($_) };

但它死得很可怕 .

2 回答

  • 2

    好吧,它没有't succeed completely, and you should feel it when you'尝试用 coerce => 1 更新这些字段 . 那是why

    除非属性的类型约束具有强制,否则无法传递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);
    

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

  • 3

    来自Moose::Manual::Type docs:

    LOAD ORDER ISSUES因为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:: 课程中 .

相关问题