Search code examples
perlclassooppackagemoose

Moose Attribute Not Defined in Builder Method


I'm using Moose to create an object but the builder method '_initialize_log' is not able to retrieve the value of the name attribute.

Is there a way for me to run the method only after that attribute has been set?

EFT.pm

package EFT;
use Moose;

# Attributes
has name => (
    is       => "ro",
    isa      => "Str",
    required => 1
);

has log => (
    is      => 'rw',
    isa     => 'Str',
    builder => '_initialize_log'
);

sub _initialize_log
{
    $self->{'log'} = "****\n";
    $self->{'log'} .= $self->{'name'} . "\n";
    $self->{'log'} .= `date`;
    $self->{'log'} .= "****\n";
}

test.pl

#!/usr/bin/perl

use strict;
use warnings;
use EFT;

# Constants
use constant NAME => 'Test Script';

# Create script object
my $script = EFT->new(name => NAME);

print $script->{'log'};

Output

Use of uninitialized value in concatenation (.) or string at EFT.pm line 46.
****

Thu Mar  3 12:54:31 EST 2016
****

Solution

  • The object is still being constructed! Delay your attribute's initialization until after it's constructed. The following delays its initialization until it's used:

    lazy => 1
    

    You could also use a BUILD method instead.

    sub BUILD {
       my $self = shift;
       $self->_initialize_log();
    }
    

    Note that you had three bugs in _initialize_log:

    sub _initialize_log
    {
        my $self = shift;              # <-- Won't even compile without this!
        my $log = "****\n";
        $log .= $self->name . "\n";    # <-- Removed reliance on Moose internals
        $log .= `date`;
        $log .= "****\n";
        return $log;                   # <-- The value is to be returned.
    }
    

    To call it from BUILD instead of as a builder, you'll need to change it as follows:

    sub _initialize_log
    {
        my $self = shift;
        my $log = "****\n";
        $log .= $self->name . "\n";
        $log .= `date`;
        $log .= "****\n";
        $self->log($log);              # <--
    }