1

My question is the following: I want to use a subroutine to construct an array when I call the new() method to create an object using perl Moo. Please see the following example.

package Customer;
use DBI;
use 5.010;
use Data::Dumper;
use Moo;
use FindBin qw/$Bin/;
use lib "$Bin/../../../lib";
use lib '/home/fm/lib';
use TT::SQL;

has id => (
  is=>'ro',
  required=>1,
);

has type => (
  is=>'ro',
);

has emails => (
  is=>'rw',
  isa => sub {getEmails() },
);

sub getEmails
{
                #connecting into de DB
                my $self=shift;
                my $db2 = TT::SQL::get_handler("xxxxxx","xxxxx");
                my $fmuser=$self->id;
                my $type=$self->type;
                my $query;
                #checking the customer type to perform the query
                if ($type eq "xxxxxxxxxx")
                {
                $query=xxxxxxxxxxxxxx;
                }
                else
                {
                $query=xxxxxxxxxxxxxx;
                }
                my $ref = $db2->execute($query,$fmuser);
                my @emails;
                #retrieving emails
                while ( my $row = $ref->fetchrow_hashref  ) {
                       @emails=(@emails,"$row->{email}\n");
                  }
                return @emails;
}

1;

Basically, I'm trying to retrieve some emails from a database, and do not worry about the queries and the DB access because when I execute the following:

my $cc= Customer->new(id=>92,type=>'xxxxxxx');
@emails=$cc->getEmails();

The result in @emails is the expected. However, when I execute:

my $cc= Customer->new(id=>92,type=>'xxxxxxx');
@emails=$cc->emails;

I do not even have a result.

I'll be very grateful if I get an answer to this question. Thanks in advance guys.

the_ccalderon
  • 2,006
  • 2
  • 13
  • 24

1 Answers1

4

You want to use either a builder method or a default, isa is for enforcing type constraints:

default:

has emails => (
  is      => 'rw',
  default => sub { 
     my ($self) = @_;
     return $self->getEmails();
  },
);

builder:

has emails => (
  is      => 'rw',
  builder => '_build_emails',
);

sub build_emails {
    my ($self) = @_;

    return $self->getEmails();
}

If the getEmails() subroutine requires any addition startup time (e.g Getting a handle to the database) I would also suggest adding the lazy => 1 argument to your emails attribute. This will only initialize it if it called.

Hunter McMillen
  • 59,865
  • 24
  • 119
  • 170
  • `lazy` MUST be set to `1` in this case, because `getEmails` relies upon `type` (from the docs: *Note that if your default is fired during new() there is no guarantee that other attributes have been populated yet so you should not rely on their existence.*). Additionally, the `builder` parameter can be set directly to `getEmails` instead of using an intermediate. – polettix Apr 16 '16 at 00:04
  • You are the best guys! I have resolved my issue thanks to your answer. I appreciate it a lot. – the_ccalderon Apr 21 '16 at 03:04
  • @ccalderon911217 you can accept the answer by clicking the checkmark above, this will let future users who have this problem know this solved your issue – Hunter McMillen Apr 21 '16 at 13:07