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:

  1. As I edit the parser, I find more and more lines that need to be changed, and
  2. The information is already present in the has entries for which constructor to call for each thing.

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.


In reply to Using Moose and BUILDARGS, but having trouble by roboticus

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.