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。それが理由です:

属性の型制約に強制がない限り、強制=>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);

残りのコードは、ご使用のバージョンと同じです。これは警告なしで機能し、多かれ少なかれ(再び、私は思うに)そうあるべきように動作します。

于 2012-09-18T22:23:00.857 に答える
3

Moose::Manual::Typeドキュメントから:

注文の問題を読み込む

Mooseタイプは実行時に定義されるため、ロード順序の問題が発生する可能性があります。特に、その型が定義される前に、クラスの型制約を使用したい場合があります。

この問題を改善するには、すべてのカスタムタイプを1つのモジュール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 に答える