tags:

views:

332

answers:

3

I am having difficulty with the BUILD method in MooseX::Declare. If I say:

#!/usr/bin/perl

use MooseX::Declare;

class Foo {
    has foo => (is => "rw", isa => "Str", default => "foo");

    method BUILD {
     print "I was called\n";
    }
}

Foo->new;

I get the following less than helpful error message:

Reference found where even-sized list expected at /Users/cowens/perl5/lib/perl5/MooseX/Method/Signatures/Meta/Method.pm line 335.
Validation failed for 'MooseX::Types::Structured::Tuple[MooseX::Types::Structured::Tuple[Object],MooseX::Types::Structured::Dict[]]' failed with value [ [ Foo=HASH(0x804b20) ], { HASH(0x8049e0) => undef } ], Internal Validation Error is: Validation failed for 'MooseX::Types::Structured::Dict[]' failed with value { HASH(0x8049e0) => undef } at /Users/cowens/perl5/lib/perl5/MooseX/Method/Signatures/Meta/Method.pm line 365
        MooseX::Method::Signatures::Meta::Method::validate('MooseX::Method::Signatures::Meta::Method=HASH(0xb8aab0)', 'ARRAY(0xb8ab30)') called at /Users/cowens/perl5/lib/perl5/MooseX/Method/Signatures/Meta/Method.pm line 139
        Foo::BUILD('Foo=HASH(0x804b20)', 'HASH(0x8049e0)') called at generated method (unknown origin) line 25
        Foo::new('Foo') called at test.pl line 13

But if I say:

#!/usr/bin/perl

use MooseX::Declare;

class Foo {
    has foo => (is => "rw", isa => "Str", default => "foo");

    sub BUILD {
     my $self = shift;
     print "I was called\n";
    }
}

Foo->new;

everything works just fine (but is ugly and out of place with the rest of the code).

A: 

Perl understands sub and so an entry into the current package's symbol table is made. &Foo::BUILD, after Devel::Declare and other magic has created a package scope from the class closure.

Moose specifically looks for the BUILD sub to allow you to manipulate constructor logic. My guess (although I haven't traced it all the way through) is that the MooseX modules stay out of the way of what Moose is trying to do. So that a native BUILD is forever passed to the Moose magic to determine constructor logic.

On the other hand the method keyword is more Devel::Declare magic to create methods in the meta class structure.

Axeman
+4  A: 

It's failing because BUILD requires a one-arg method signature. By default, MooseX::Declare creates a signature which is not compatible with the way BUILD is called. (The details are murky to me.) I know because I ran into a similar error once. I certainly agree the error message could be more enlightening; that's true with a lot of Moose stuff.

Anyway, I got it to work like this:

use MooseX::Declare;

class Foo {
    has foo => (is => "rw", isa => "Str", default => "foo");

    method BUILD(Item $href) {
        print "I was called\n";
    }
}

Foo->new;

Hope that helps.

You can fiddle with the signature and try more specific types; I think Moose sends a hashref of the as-yet-unblessed object as the parameter.

friedo
Wow, that is ugly, `$self` exists and holds the blessed object, but you also have this useless hashref full of the arguments passed into `new`. This is a major wart on what is otherwise a beautiful system.
Chas. Owens
This "wart" maybe something that will be removed. When you look at old docs (http://search.cpan.org/~drolsky/Moose-0.65/lib/Moose/Cookbook/Basics/Recipe4.pod) you will see that extra param hashref arg is needed. However in new docs (http://search.cpan.org/dist/Moose/lib/Moose/Cookbook/Basics/Recipe4.pod) you can now use $self direct to access the "params". So perhaps part of an evolutionary process where this extra param will be dropped off! Moose mailing list / IRC is best place to find out whether "params" arg is still needed and what the Darwinian plans are ;-)
draegtun
An argument to a method is a "major wart"? I would hate to hear what you think of the C++ or Java object system...
jrockway
BUILD is used to finish up Instance creation. It is passed a copy of the arguments sent to new() so that it can deal with any arguments in their original state (or at least the state they come out of BUILDARGS in). I don't see how an argument to enable the basic function of a code block is a "wart", and I'm fairly certain that the arguments won't change anytime soon.
perigrin
+5  A: 

BUILD takes an arg, if you don't need it, just say:

method BUILD($) { ... }
jrockway