Заставить конструктор Moose игнорировать аргументы undef - PullRequest
4 голосов
/ 28 апреля 2011

Хеш-таблица является типичным инициализатором для ваших объектов Perl. Теперь ваш ввод ненадежен в том смысле, что вы не знаете, будет ли для какого-либо данного ключа определенное значение, и есть ли вообще ключ. Теперь вы хотите передать такой ненадежный ввод в объекты Moose, и, хотя отсутствующие ключи в порядке, вы хотите избавиться от неопределенных значений, чтобы не получить объект, полный неопределенных атрибутов.

Вы, безусловно, можете быть очень осторожны при создании экземпляров объектов и отфильтровывать неопределенные значения. Но допустим, вы хотите установить этот фильтр в своем конструкторе, потому что тогда он находится в одном месте. Вы хотите, чтобы конструктор игнорировал неопределенные значения, но не умирал при их обнаружении.

Для методов доступа вы можете использовать around, чтобы атрибут не был установлен на undef. Но эти модификаторы метода не вызываются для конструктора, только для методов доступа. Есть ли в Moose аналогичное средство для достижения того же эффекта для c'tor, то есть для предотвращения принятия любых атрибутов undef?

Обратите внимание, что тип Moose Any создает хеш-ключ в объекте, если атрибут undef. Я не хочу этого, потому что я хочу, чтобы %$self не содержал undef значений.

Вот несколько тестов, которые я провел:

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;

Ответы [ 4 ]

13 голосов
/ 29 апреля 2011

Это именно то, что делает MooseX :: UndefTolerant . Если вы сделаете свой класс неизменным, это будет намного быстрее, чем написание собственного метода BUILDARGS, так как код встроен в сгенерированный конструктор.

5 голосов
/ 28 апреля 2011

Просто предоставьте свою собственную BUILDARGS подпрограмму.

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);
};
2 голосов
/ 28 апреля 2011

Я понимаю, что это несколько дублирующее усилие, но вы можете подключить ctor с помощью 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
    );
};

Редактировать: Редактируется для поддержки даже ссылки, переданной в ctor.

0 голосов
/ 19 сентября 2014

Хотя в приведенном примере проясняется, что вопрос вызван желанием обрабатывать атрибуты undef, передаваемые конструктору, сам вопрос дополнительно подразумевает случай передачи конструктору только undef, с чем я столкнулся и хотелрешить.

Например, Class->new(undef).

Мне нравится ответ BIVR BUILDARGS .Он может быть расширен для обработки случая передачи значения undef вместо хэш-ссылки в качестве аргумента-одиночки для конструктора:

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 не поддерживает этот случай.

...