How to instantiate Moose classes from a big hash

别来无恙 提交于 2019-12-06 01:30:57

You could have a BUILDARGS handler which converts unblessed references in those slots to objects. Coercions is probably the best, but it takes more doing. (Unless this is all coming from a RDBMS, in which case use DBIx::Class).

#!/usr/bin/env perl

use warnings;
use strict;

package Company;
use Moose;

has 'id'   => (is => 'ro', isa => 'Num');
has 'name' => (is => 'ro', isa => 'Str');
has 'departments' => (is => 'ro', isa => 'ArrayRef[Company::Department]');

sub BUILDARGS {
  my $self = shift;
  my $args = $self->SUPER::BUILDARGS(@_);
  @{ $args->{departments} } = 
    map { eval{ $_->isa('Company::Department') } ? $_ : Company::Department->new($_) }
    @{ $args->{departments} };

  return $args;
};

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]');

sub BUILDARGS {
  my $self = shift;
  my $args = $self->SUPER::BUILDARGS(@_);
  @{ $args->{employees} } = 
    map { eval{ $_->isa('Company::Person') } ? $_ : Company::Person->new($_) }
    @{ $args->{employees} };

  return $args;
};

package Company::Person;
use Moose;

has 'id'         => (is => 'ro', isa => 'Num');
has 'name'       => (is => 'ro', isa => 'Str');
has 'age'        => (is => 'ro', isa => 'Num');

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;

I've used your option 2 several times and it worked fine for me. Last instance was inflating JIRA REST API results into real objects. Note that with coercions you can also lookup an existing instance by id and create only if it does not exist.

Edit: Here is some code to demonstrate those coercions:

package Company::Types;
use Moose::Util::TypeConstraints;

subtype 'Company::Departments', as 'ArrayRef[Company::Department]';
coerce  'Company::Departments', from 'ArrayRef', via {
    require Company::Department;
    [ map { Company::Department->new($_) } @$_ ]
};

subtype 'Company::Persons', as 'ArrayRef[Company::Person]';
coerce  'Company::Persons', from 'ArrayRef', via {
    require Company::Person;
    [ map { Company::Person->new($_) } @$_ ]
};

no Moose::Util::TypeConstraints;

and in those classes:

use Company::Types;

has 'departments' => (is => 'ro', isa => 'Company::Departments', coerce => 1);
has 'employees'   => (is => 'ro', isa => 'Company::Persons', coerce => 1);

then you can pass whole structure into Company constructor and all gets inflated properly.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!