Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Moose from ArrayRef[HashRef] to ArrayRef[Object]

by saintex (Scribe)
on Feb 23, 2011 at 14:52 UTC ( [id://889800]=perlquestion: print w/replies, xml ) Need Help??

saintex has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

Following a previous conversation (for which I thank all the Monks):
href="http://www.perlmonks.org/?node_id=887227

I have started to use MooseX::SimpleConfig to have object constructions from config files.

But I have a problem related to types:
I would like to have an ArrayRef of objects, but I forced to have an ArrayRef of hashes.

How I can force a type to be another one?

Here there is some code, just to have an idea.

I inserted only the essential parts.

YAML FILE:
pages: - Page: URL: http://www.perlmonks.org/ - Page: URL: http://www.perl.org/

Page Class:
package Page; our $VERSION=0.01; use Moose; has 'URL' => (is => 'ro', isa => 'Str',required => 1); __PACKAGE__->meta->make_immutable; 1;


Objects container class (with ArrayRef):
package PagesGenerator; our $VERSION=0.01; use Moose; with 'MooseX::SimpleConfig'; use Page; has 'pages'=>( is=>'rw', isa=>'ArrayRef[Page]', default => sub { [ ] }, ); __PACKAGE__->meta->make_immutable; 1;


And finally on main script, the object construction:

use strict; use warnings; our $VERSION=0.01; use FindBin qw($Bin); my $p=PagesGenerator->new_with_config(configfile => "$Bin/pages-temp.y +aml");
But, while page is an ArrayRef[Page] I have this error:
Attribute (pages) does not pass the type constraint because: Validatio +n failed for 'ArrayRef[Page]' with value ARRAY(0x22422e8) at /usr/loc +al/share/perl/5.10.0/MooseX/ConfigFromFile.pm line 44 MooseX::ConfigFromFile::new_with_config('PagesGenerator', 'configf +ile', 'pages-temp.yaml') called at navigator.pl line 14

If I choose an ArrayRef[HashRef], all works fine.

The problem is that I need exaclty an ArrayRef[Page].

So I could coerce one type in another.
I readed something about coercion in Moose, but I don't understand how to use it in my case.

Any suggestions is welcome.

Thank you!

Replies are listed 'Best First'.
Re: Moose from ArrayRef[HashRef] to ArrayRef[Object]
by ikegami (Patriarch) on Feb 23, 2011 at 15:27 UTC
    You could create a coerce rule for creating ArrayRef[Page] from ArrayRef[HashRef]. Don't forget coerce=>1.
      yes...
      but... how?

      I have checked the samples on Moose manual and I am a little confused.
        Using BUILDARGS to avoid effects at a distance:
        { package Page; use strict; use warnings; use Moose; has 'url' => ( is => 'ro', isa => 'Str', required => 1, ); no Moose; __PACKAGE__->meta->make_immutable(); } { package Container; use strict; use warnings; use Moose; my $pages_constraint = Moose::Util::TypeConstraints::create_paramet +erized_type_constraint('ArrayRef[Page]'); my $aoh_constraint = Moose::Util::TypeConstraints::create_paramet +erized_type_constraint('ArrayRef[HashRef]'); around BUILDARGS => sub { my $orig = shift; my $class = shift; my %args = ( @_ == 1 ? %{ $_[0] } : @_ ); if (exists($args{pages})) { my $pages = $args{pages}; if (!$pages_constraint->check($pages)) { # Need to coerce? if ($aoh_constraint->check($pages)) { # Can we coerce? $args{pages} = [ map Page->new($_), @$pages ]; } } } return $class->$orig(%args); }; has 'pages' => ( is => 'rw', isa => 'ArrayRef[Page]', default => sub { [ ] }, ); no Moose; __PACKAGE__->meta->make_immutable(); } Container->new( pages => [ { url => "a" }, { url => "b" } ] );

        This is basically the same approach you tried, but correctly uses the more appropriate BUILDARGS instead of BUILD.

        Coded so it can take both ArrayRef[HashRef] and ArrayRef[Page].

        { package Page; use strict; use warnings; use Moose; has 'url' => ( is => 'ro', isa => 'Str', required => 1, ); use Moose::Util::TypeConstraints; subtype 'Pages' => as 'ArrayRef[Page]'; coerce 'Pages' => from 'ArrayRef[HashRef]' => via { [ map __PACKAGE__->new($_), @{ $_[0] } ] }; no Moose::Util::TypeConstraints; no Moose; __PACKAGE__->meta->make_immutable(); } { package Container; use strict; use warnings; use Moose; has 'pages' => ( is => 'rw', isa => 'Pages', coerce => 1, default => sub { [ ] }, ); no Moose; __PACKAGE__->meta->make_immutable(); } Container->new( pages => [ { url => "a" }, { url => "b" } ] );

        I don't really like the global-ness of the coercion. I'll be posting an alternative that uses BUILDARGS shortly.

        This is an horrible solution, because of duplicate attributes:

        YAML file:
        tmpPages: - Page: URL: http://www.perlmonks.org/ - Page: URL: http://www.perl.org/
        Objects container:
        package PagesGenerator; our $VERSION=0.01; use Moose; with 'MooseX::SimpleConfig'; use Page; has 'tmpPages'=>( is=>'rw', isa=>'ArrayRef[HashRef]', default => sub { [ ] }, ); has 'pages'=>( is=>'rw', isa=>'ArrayRef[Page]', default => sub { [ ] }, ); sub BUILD{ my $self=shift; @{$self->pages}= map {new Page(URL=>$$_{Page}{URL});} @{$self->tmp +Pages}; for ( @{ $self->pages } ) { print $_->URL; print "\n"; } }


        Any other idea?

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://889800]
Approved by moritz
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (3)
As of 2024-04-20 01:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found