Make Moose constructor ignore undef arguments

A hash table is a typical initializer for your Perl objects. Now your input is unreliable because you do not know if there will be a certain value for any given key, or if there will be no key at all. Now you want to feed such an unreliable entry into Moose objects, and while the missing keys are fine, you want to get rid of undefined values ​​so that you don't end up with an object full of undefined attributes.

You could be very careful when creating objects and filtering undefined values. But let's say you want to install this filter in your constructor, because it is in one place. You want the constructor to ignore undefined values, but not die when it encounters them.

For access methods, you can use around to prevent the attribute from being set to undef . But those methods method modifiers are not called for the constructor, only for accessories. Is there a similar tool in Muse to achieve the same effect for c'tor, i.e. To prevent the adoption of any undef attributes?

Note that the Moose Any type will create a hash key in the object if the attribute is undef. I do not want this because I want %$self not contain any undef values.

Here are some tests I have done:

 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; 
+3
source share
4 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, since the code is embedded in the generated constructor.

+13
source

Just specify your own BUILDARGS routine.

 package Gurke; ... around 'BUILDARGS' => sub{ my($orig,$self,@params) = @_; my $params; if( @params == 1 ){ ($params) = @params; }else{ $params = { @params }; } for my $key ( keys %$params ){ delete $params->{$key} unless defined $params->{$key}; } $self->$orig($params); }; 
+5
source

I understand this is a somewhat duplicate effort, but you can connect ctor with BUILDARGS :

 around BUILDARGS => sub { my $orig = shift; my $class = shift; my %params = ref $_[0] ? %{$_[0]} : @_; return $class->$orig( map { $_ => $params{$_} } grep { defined $params{$_} } keys %params ); }; 

Edit: Edited to support even the link passed to ctor.

+1
source

The above example explains that the question is inspired by the desire to process undef attributes passed to the constructor, the question itself additionally implies the case of passing only undef to the constructor that I came across, and I wanted to solve it.

For example, Class->new(undef) .

I like bvr BUILDARGS answer . It can be extended to handle the case of passing undef instead of hashref as a single argument to the constructor:

 around BUILDARGS => sub { my $orig = shift; my $class = shift; my %params = defined $_[0] ? ref $_[0] ? %{$_[0]} : @_ : (); return $class->$orig( map { $_ => $params{$_} } grep { defined $params{$_} } keys %params ); }; 

MooseX :: UndefTolerant does not support this case.

0
source

All Articles