roboticus has asked for the wisdom of the Perl Monks concerning the following question:
Hello, all:
I've decided to try Moose on my current project, and I've hit a stumbling block. I'm reviewing the documentation, but haven't found what I'm looking for, so I thought I'd see if anyone could offer any suggestions.
I'm building an ad-hoc parser for a file, and am hoping to leverage the BUILDARGS method to parse out the chunks at each level. I've stripped it down to a relatively short example, like so:
# Funky.pm use strict; use warnings; package Funky::Vertex; use Moose; use Data::Dump 'pp'; has 'X' => (is=>'rw', isa=>'Num'); has 'Y' => (is=>'rw', isa=>'Num'); around BUILDARGS => sub { my $orig = shift; my $class = shift; print "Vertex(<$_[0]>)\n"; if (@_==1 && !ref $_[0]) { # We were handed a single scalar, so we're expecting a string +like "##.##,##.##" # representing the X and Y coordinates my @list = split /,/, $_[0]; print "\tV: ", pp(\@list), "\n"; return $class->$orig( X=>$list[0], Y=>$list[1] ); } else { return $class->$orig(@_); } }; package Funky::Rectangle; use Moose; use Data::Dump 'pp'; has 'LL' => (is=>'rw', isa=>'Funky::Vertex'); has 'UR' => (is=>'rw', isa=>'Funky::Vertex'); around BUILDARGS => sub { my $orig = shift; my $class = shift; print "Rectangle(<$_[0]>)\n"; if (@_==1 && !ref $_[0]) { # We were handed a single scalar, so we're expecting a string +representing # a pair of vertices, like "##.##,##.##;##.##,##.##" # representing the LL and UR corners my @list = split /;/, $_[0]; print "\tRB: ", pp(\@list), "\n"; return $class->$orig( LL=>$list[0], UR=>$list[1] ); ### NOTE +BELOW ### } else { return $class->$orig(@_); } }; 1;
As you can see, a Funky::Vertex has an X and Y coordinate, but if you construct it with a scalar, it expects a string like "<num>,<num>" and will split that into a hash of { X=><num>, Y=><num> }. Similarly, a Funky::Rectangle has a lower left vertex (LL) and an upper-right vertex (UR). If called with a scalar like "<vertex>;<vertex>", it would split that into a hash of { LL=><vertex>, UR=><vertex> } to build itself.
The problem I'm having is that I was hoping that Moose would recurse through the BUILDARGS as needed to build all the items, but instead it seems to assume that all the arguments are fully built already:
#!env perl use strict; use warnings; use Test::More; use lib "."; BEGIN { use_ok( 'Funky' ); } require_ok( 'Funky' ); subtest 'Vertex' => sub { my $t = Funky::Vertex->new("15,22"); isa_ok($t, 'Funky::Vertex'); is($t->X(), 15, "x val"); is($t->Y, 22, "y val"); done_testing(); }; subtest 'Rectangle' => sub { my $t = Funky::Rectangle->new("10,10;15,25"); isa_ok($t, 'Funky::Rectangle'); is($t->LL->X, 10, "LL x val"); is($t->LL->Y, 10, "LL y val"); is($t->UR->X, 15, "UR x val"); is($t->UR->Y, 25, "UR y val"); done_testing(); };
When I run this example, I'm getting:
$ perl t_File.pl ok 1 - use Funky; ok 2 - require Funky; # Subtest: Vertex Vertex(<15,22>) V: [15, 22] ok 1 - An object of class 'Funky::Vertex' isa 'Funky::Vertex' ok 2 - x val ok 3 - y val 1..3 ok 3 - Vertex # Subtest: Rectangle Rectangle(<10,10;15,25>) RB: ["10,10", "15,25"] 1..0 # No tests run! not ok 4 - No tests run for subtest "Rectangle" # Failed test 'No tests run for subtest "Rectangle"' # at t_File.pl line 30. Attribute (UR) does not pass the type constraint because: Validation f +ailed for 'Funky::Vertex' with value 15,25 (not isa Funky::Vertex) at + /usr/local/lib/perl5/site_perl/5.26/x86_64-cygwin-threads/Moose/Obje +ct.pm line 24 Moose::Object::new('Funky::Rectangle', '10,10;15,25') called a +t t_File.pl line 23 main::__ANON__ at /usr/local/share/perl5/site_perl/5.26/Test/B +uilder.pm line 339 eval {...} at /usr/local/share/perl5/site_perl/5.26/Test/Build +er.pm line 339 Test::Builder::subtest('Test::Builder=HASH(0x60008c060)', 'Rec +tangle', 'CODE(0x6007d0e58)') called at /usr/local/share/perl5/site_p +erl/5.26/Test/More.pm line 807 Test::More::subtest('Rectangle', 'CODE(0x6007d0e58)') called a +t t_File.pl line 30 # Tests were run but no plan was declared and done_testing() was not s +een. # Looks like your test exited with 255 just after 4.
I can make the test pass by changing the line marked "### NOTE BELOW ###" to:
return $class->$orig( LL=>Funky::Vertex->new($list[0]), UR=>Funky::Vertex->new($list[1]) );However, I find that to be a bit of a problem because:
It feels like there should be a relatively simple way to get the behavior I want without having to repeat the same information (in has and in BUILDARGS). I've not come up with a solution, though.
Anyone have any suggestions?
...roboticus
When your only tool is a hammer, all problems look like your thumb.
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Using Moose and BUILDARGS, but having trouble
by tobyink (Canon) on Nov 30, 2018 at 00:07 UTC |