Search code examples
perlmoosecarp

croaking from a Moose BUILD method


I want my class to blow up if the BUILD method fails. However, if I use croak to handle the error, the error gets reported from Class/MOP/Method.pm, rather than the caller's code. (That is to say, the caller who instantiates the object.) IOW, croak isn't barking far enough up the call tree.

Behold:

package Test;

use Moose;
use Carp 'croak';

sub BUILD {
    croak 'u r dum';
}

1;

Instantiating Test results in:

u r dum at /home/friedo/perl5/lib/perl5/x86_64-linux-gnu-thread-multi/Class/MOP/Method.pm line 125

Carp.pm is supposed to pay attention to a package variable called @CARP_NOT to know which packages to avoid, but it seems to only pay attention to one item on the list. For example, if I add this to my Test.pm:

our @CARP_NOT = ( 'Class::MOP::Method' );

Then the result is:

u r dum at /home/friedo/perl5/lib/perl5/x86_64-linux-gnu-thread-multi/Moose/Object.pm line 59

So I should just add that to the array as well, right?

our @CARP_NOT = ( 'Class::MOP::Method', 'Moose::Object'  );

Then the result is still:

u r dum at /home/friedo/perl5/lib/perl5/x86_64-linux-gnu-thread-multi/Moose/Object.pm line 59

Moose::Object seems unaffected.

I've been banging my head against this for a while now and can't seem to figure out what's messing it up.

Thanks.


Solution

  • make_immutable seems to fix that. Of course, I don't know what to do if you do need your classes to be mutable.

    Without make_immutable, Test->new invokes Moose::Object->new. If you look at the confess output, you'll note:

        Test::BUILD(...) called ...
        Class::MOP::Method::execute(...) called ...
        Moose::Object::BUILDALL(...) called ...
        Moose::Meta::Class::new_object(...) called ...
        Moose::Object::new('Test') called at ./t.pl line 17
    
    #!/usr/bin/env perl
    
    package Test;
    
    use Moose;
    use namespace::autoclean;
    
    use Carp 'croak';
    
    sub BUILD {
        croak 'u r dum';
    }
    
    __PACKAGE__->meta->make_immutable;
    
    package main;
    
    my $t = Test->new;
    

    Output:

    [sinan@archardy tmp]$ ./t.pl
    u r dum at constructor Test::new (defined at ./t.pl line 14) line 28

    From Moose::Cookbook::Basics::Recipe7:

    Second, you can no longer make changes via the metaclass API, such as adding attributes. In practice, this won't be a problem, as you rarely need to do this after first loading the class.

    We strongly recommend you make your classes immutable. It makes your code much faster, with a small compile-time cost. This will be especially noticeable when creating many objects.