Moose (Perl): конвертировать undef в пустую строку или 0 вместо die () - PullRequest
8 голосов
/ 23 июня 2011

Я получил много исключений из QA из-за неполных данных, передаваемых моим конструкторам Moose. Имя атрибута присутствует в аргументах конструктора, но значение равно undef.

Это факт жизни со многими приложениями сценариев, что вещи просто undef. И часто это прекрасно. Вы не хотите назойливое предупреждение от warnings прагмы (поэтому вы делаете no warnings 'uninitialized'), и вы, конечно, не хотите, чтобы ваш код умер, потому что одно маленькое значение, скажем, housenumber, равно undef.

Так что без лишних слов я хочу, чтобы мои конструкторы Moose вели себя как прямой Perl (т.е. без use warnings 'uninitialized'), то есть для преобразования undef в 0 или пустой строки, как требуется. Попытка, показанная в этом примере, не работает для случая, когда присутствует имя атрибута, но значение равно undef. Я мог бы подумать об использовании BUILDARGS для достижения того, чего я хочу. Но существует ли декларативный способ в обычном Moose без обращения к MooseX :: UndefTolerant (который, к сожалению, я не могу использовать, поскольку он не установлен)?

package AAA;
use Moose;
has 'hu', is => 'ro', isa => 'Str';
has 'ba', is => 'ro', isa => 'Int';
no Moose; __PACKAGE__->meta->make_immutable;

package BBB;
use Moose; extends 'AAA';
has '+hu', default => ''; # don't want to die on undef
has '+ba', default => 0;  # idem
no Moose; __PACKAGE__->meta->make_immutable;

package main;
use Test::More;
use Test::Exception;
# Those AAAs should die ...
throws_ok { AAA->new( hu => undef ) }
    qr/Validation failed for 'Str' with value undef/;
throws_ok { AAA->new( ba => undef ) }
    qr/Validation failed for 'Int' with value undef/;
# .. but these BBBs should live:
lives_ok  { BBB->new( hu => undef ) } 'hu supplied as undef';
lives_ok  { BBB->new( ba => undef ) } 'ba supplied as undef';
done_testing;

Ответы [ 3 ]

9 голосов
/ 23 июня 2011

In Moose :: Manual :: Types - документально подтвержденный способ решения именно такой проблемы.

Используйте тип Maybe[a].

package AAA;
use Moose;

has 'hu', is => 'ro', isa => 'Str';
has 'ba', is => 'ro', isa => 'Int';

no Moose; __PACKAGE__->meta->make_immutable;


package BBB;
use Moose; extends 'AAA';

has 'hu', is => 'rw', isa => 'Maybe[Str]', default => ''; # will not die on undef
has 'ba', is => 'rw', isa => 'Maybe[Int]', default => 0;  # idem

sub BUILD {
    my $self = shift;
    $self->hu('') unless defined $self->hu;
    $self->ba(0) unless defined $self->ba;
}

no Moose; __PACKAGE__->meta->make_immutable;


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

# Those AAAs should die ...
throws_ok { AAA->new( hu => undef ) }
    qr/Validation failed for 'Str' with value undef/;
throws_ok { AAA->new( ba => undef ) }
    qr/Validation failed for 'Int' with value undef/;

# .. but these BBBs should live:
lives_ok  { BBB->new( hu => undef ) } 'hu supplied as undef';
lives_ok  { BBB->new( ba => undef ) } 'ba supplied as undef';

my $bbb = BBB->new( hu => undef, ba => undef );

is $bbb->hu, '', "hu is ''";
is $bbb->ba, 0, 'ba is 0';

done_testing;
4 голосов
/ 23 июня 2011

Ваша жалоба на самом деле заключается в том, что Мус делает именно то, что должен делать. Если вы явно передаете undef в качестве значения, но это значение может быть только Int, тогда вы должны получить ошибку.

Так что вам нужно сделать выбор. Вы можете изменить тип (через объединение), чтобы разрешить undef в качестве допустимого значения, например:

    has 'hu', is => 'ro', isa => 'Str | Undef';
    has 'ba', is => 'ro', isa => 'Int | Undef';

Или вы можете просто не отправлять неопределенные значения:

    my %aa_params = ();
    $aa_params{hu} = $foo if defined $foo;

    $aa = AA->new( %aa_params );

Или, наконец, по какой-то неизвестной причине вы абсолютно не можете устоять перед отправкой недопустимых неопределенных значений для вещей, которые не следует явно устанавливать в неопределенные, просто напишите быстрый фильтр:

    sub filt_undef {
      my %hash = @_;
      return map { $_ => $hash{$_} } grep { defined $hash{$_} } keys %hash;
    }

    $aa = AA->new( filt_undef( hu => undef ) );

Но это кажется довольно неловким и ужасным.

2 голосов
/ 11 марта 2014

Или используйте принуждение на лету:

package BBB;
use Moose;
use MooseX::AttributeShortcuts;
extends 'AAA';
has '+hu',
  traits => [Shortcuts],
  coerce => [ Undef => sub { '' } ],
;
...