Search code examples
perlmoose

Moose: builder requires a value that sometimes is not set (non deterministic)


I'm new to MOOSE and Perl OOP, and I'm struggling understanding the execution order of the code.

I want to create a class reading files, so an attribute of the object should be the filehandle and another the filename to be read.

My problem is that the attribute 'filehandle' has a builder that requires $self->filename, but sometimes at runtime 'filename' is not (yet) available when the builder is called.

Thanks for you help

My ideal object creation:

my $file = FASTQ::Reader->new(
    filename => "$Bin/test.fastq",
);

Perl module:

has filename => (
    is => 'ro',     isa => 'Str',     required => 1,
);

has fh => (
  is => 'ro',   isa => 'FileHandle',   builder => '_build_file_handler',
);

sub _build_file_handler {
   my ($self) = @_;
   say Dumper $self;
   open(my $fh, "<", $self->filename) or die ("cant open " . $self->filename  . "\n");
   return $fh;
}

See: https://gist.github.com/telatin/a81a4097913af55c5b86f9e01a2d89ae


Solution

  • If a value of one attribute depends on another attribute, make it lazy.

    #!/usr/bin/perl
    use warnings;
    use strict;
    
    {   package My::Class;
        use Moose;
    
        has filename => (is => 'ro', isa => 'Str', required => 1);
        has fh => (is => 'rw', isa => 'FileHandle', lazy => 1, builder => '_build_fh');
        #                                           ~~~~~~~~~
    
        sub _build_fh {
            my ($self) = @_;
            open my $fh, '<', $self->filename or die $!;
            return $fh
        }
    }
    
    my $o = 'My::Class'->new(filename => __FILE__);
    print while readline $o->fh;
    

    See Laziness in Moose::Manual::Attributes:

    if the default value for this attribute depends on some other attributes, then the attribute must be lazy.