Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make the Moose constructor ignore undef arguments

Tags:

perl

moose

A hashtable is the typical initializer for your Perl objects. Now your input is unreliable in that you don't know whether for any given key there'll be a defined value, nor whether the key is there at all. Now you want to feed such unreliable input into Moose objects, and while absent keys are perfectly okay you do want to get rid of the undefined values so you don't end up with an object full of undefined attributes.

You could certainly take great care when instantiating objects and filter out the undefined values. But let's say you want to install that filter in your constructor because then it is in one place. You want the constructor to ignore undefined values, but not to die on encountering them.

For accessor methods, you can use around around to prevent the attribute to be set to undef. But those method modifiers aren't called for the constructor, only for accessors. Is there a similar facility in Moose to achieve the same effect for the c'tor, i.e. to preclude any undef attributes from being accepted?

Note that the Moose Any type will create the hash key in the object if the attribute is undef. I don't want that because I want %$self not to contain any undef values.

Here's some testing I did:

package Gurke;
use Moose;
use Data::Dumper;

has color  => is => 'rw', isa => 'Str', default => 'green';
has length => is => 'rw', isa => 'Num';
has appeal => is => 'rw', isa => 'Any';

around color => sub {
    # print STDERR Dumper \@_;
    my $orig = shift;
    my $self = shift;
    return $self->$orig unless @_;
    return unless defined $_[0];
    return $self->$orig( @_ );
};

package main;
use Test::More;
use Test::Exception;

my $gu = Gurke->new;
isa_ok $gu, 'Gurke';
diag explain $gu;
ok ! exists $gu->{length}, 'attribute not passed, so not set';
diag q(attempt to set color to undef - we don't want it to succeed);
ok ! defined $gu->color( undef ), 'returns undef';
is $gu->color, 'green', 'value unchanged';
diag q(passing undef in the constructor will make it die);
dies_ok { Gurke->new( color => undef ) }
    'around does not work for the constructor!';
lives_ok { $gu = Gurke->new( appeal => undef ) } 'anything goes';
diag explain $gu;
diag q(... but creates the undef hash key, which is not what I want);
done_testing;
like image 594
Lumi Avatar asked Nov 30 '22 08:11

Lumi


1 Answers

This is exactly what MooseX::UndefTolerant does. If you make your class immutable, it will be much faster than writing your own BUILDARGS method, as the code is inlined into the generated constructor.

like image 64
Ether Avatar answered Dec 05 '22 05:12

Ether