http://www.perlmonks.org?node_id=847938

roho has asked for the wisdom of the Perl Monks concerning the following question:

I have a Moose class with an attribute that is not required. When I try to access this attribute in an object where it is not present, the warning message "Use of uninitialized value in concatenation (.) or string" is generated. How can I check for the presence of an optional attribute without generating the warning message?

The sample code below is a simple Moose class with 1 required attribute (name) and 1 optional attribute (age). The 'show_person' method generates the warning message because $self->age() is not present in the current object.

Just referencing $self->age() in 'show_person' to test it generates the warning, as in:
if ($self->age() ne '') {...};

#!/usr/bin/perl package Person; use Moose; has 'name' => ( is => 'ro', isa => 'Str', required => 1); has 'age' => ( is => 'ro', isa => 'Str'); sub show_person { my $self = shift; return $self->name() . " " . $self->age(); } __PACKAGE__->meta->make_immutable; no Moose; 1; my $person = Person->new(name => "Bob"); print "\n", $person->show_person(), "\n";

"Its not how hard you work, its how much you get done."

Replies are listed 'Best First'.
Re: Checking for Presence of Optional Attribute in Moose Object
by FalseVinylShrub (Chaplain) on Jul 04, 2010 at 04:41 UTC

    Hi

    I think your test should work without the warning if you use if (defined $self->age) { ... }.

    According to Moose::Manual::Attributes, you could also set up a predicate for that attribute:

    has 'age' => ( is => 'ro', isa => 'Str', predicate => 'has_age' ); if ($self->has_age) { ... }

    The predicate allows you to have undefined or false values and still know whether it's ever been set or not.

    On the other hand, if you just want to be able to interpolate an empty value into a string without warnings, how about putting a default of '':

    has 'age' => ( is => 'ro', isa => 'Str', default => '', );

    Assumes that '' is not a valid age... But that sounds like a reasonable assumption.

    FalseVinylShrub

    Disclaimer: Please review and test code, and use at your own risk... If I answer a question, I would like to hear if and how you solved your problem.

Re: Checking for Presence of Optional Attribute in Moose Object
by Your Mother (Archbishop) on Jul 04, 2010 at 04:47 UTC

    A way to do it is with a predicate.

    BEGIN { package Person; use Moose; use overload '""' => sub { +shift->show_person }, fallback => 1; has 'name' => is => 'ro', isa => 'Str', required => 1; has 'age' => is => 'ro', isa => 'Str', predicate => 'has_age'; sub show_person { my $self = shift; join(", ", $self->name, $self->has_age ? $self->age : "age unknown"); } __PACKAGE__->meta->make_immutable; no Moose; 1; } my $person = Person->new({ name => "Bob" }); print $person->show_person, "\n"; print Person->new({ name => "Your Uncle", age => 93 }), "\n";

    More here: Moose::Manual::Attributes.

    (Update: man! I waited for hours to see if anyone would bite on this and when I finally go to post, FalseVinylShrub hit create while I was typing.)

Re: Checking for Presence of Optional Attribute in Moose Object
by Khen1950fx (Canon) on Jul 04, 2010 at 04:23 UTC
    I'm not an expert in Moose, but looking at it makes me wonder why you are referencing it. I took the reference to age out of the sub.
    sub show_person { my $self = shift; return $self->name(); }
    If it's not required, then it wasn't necessary to return it, at least to my way of thinking. My logic could be off, which happens frequently, but here's the code that I ran. It returns "Bob" with no warnings.
    package Person; use Moose; has 'name' => ( is => 'ro', isa => 'Str', required => 1); has 'age' => ( is => 'ro', isa => 'Str'); sub show_person { my $self = shift; return $self->name(); } __PACKAGE__->meta->make_immutable; no Moose; 1; my $person = Person->new(name => "Bob"); print $person->show_person(), "\n";
      The sample code is only meant to describe the problem. I want to be able to add the optional attribute value to a display line if the attribute is present. The predicate suggestion by FalseVinylShrub and Your Mother solved the problem. Many thanks.

      "Its not how hard you work, its how much you get done."