Автоматическое преобразование пустой строки в undef в объектах Perl Moo

Для некоторых полей объекта Perl Moo я хочу заменить пустую строку, когда она назначена полю с undef,

То есть я хочу: $obj->x("") сделать поле x не определено.

Пожалуйста, помогите разработать расширение Moo, которое делает это.


Возможный способ сделать это:

sub make_field_undef {
  my ($class, $field_name) = @_;
  eval "package $class";
  around $field_name => sub {
    my $orig = shift;
    my $self = shift;
    my @args = @_;
    if(@args >= 1) {
      $args[0] = undef if defined $args[0] && $args[0] eq '';
    }
    $orig->($self, @args);
  };
}

Но есть ли "более структурированные" или "более декларативные" способы сделать это? Есть ли другие способы сделать это?


Ниже приведен полный пример с моей реализацией. Но его запуск приводит к ошибкам, которых я не понимаю:

package UndefOnEmpty;
use Moo;

sub auto_undef_fields { () }

sub make_fields_undef {
  my ($class) = @_;
  eval "package $class";
  around [$class->auto_undef_fields] => sub {
    my $orig = shift;
    my $self = shift;
    my @args = @_;
    if(@args >= 1) {
      $args[0] = undef if defined $args[0] && $args[0] eq '';
    }
    $orig->($self, @args);
  };
  around 'BUILD' => {
    my ($self, $args) = @_;
    foreach my $field_name ($class->auto_undef_fields) {
      $args->{$field_name} = undef if defined $args->{$field_name} && $args->{$field_name} eq "";
    }
  };
}

1;

Пример использования:

#!/usr/bin/perl

package X;
use Moo;
use lib '.';
extends 'UndefOnEmpty';
use Types::Standard qw(Str Int Maybe);
use Data::Dumper;

has 'x' => (is=>'rw', isa=>Maybe[Str]);
has 'y' => (is=>'rw', isa=>Maybe[Str]);

sub auto_undef_fields { qw(x y) }
__PACKAGE__->make_fields_undef;

my $obj = X->new(x=>"");
$obj->y("");
print Dumper $obj->x, $obj->y;

Вот ошибки:

$ ./test.pl 
"my" variable $class masks earlier declaration in same scope at UndefOnEmpty.pm line 20.
"my" variable $args masks earlier declaration in same statement at UndefOnEmpty.pm line 21.
"my" variable $field_name masks earlier declaration in same statement at UndefOnEmpty.pm line 21.
"my" variable $args masks earlier declaration in same statement at UndefOnEmpty.pm line 21.
"my" variable $field_name masks earlier declaration in same statement at UndefOnEmpty.pm line 21.
syntax error at UndefOnEmpty.pm line 20, near "foreach "
Compilation failed in require at /usr/share/perl5/Module/Runtime.pm line 317.

Пожалуйста, помогите понять, каковы причины ошибок.

1 ответ

Решение

Почему бы не использовать принуждение с coerce приписывать has? Это кажется самым простым и понятным способом.

package Foo;
use Moo;

has bar => (
    is     => 'rw',
    coerce => sub { $_[0] eq q{} ? undef : $_[0] },
);

Вот как выглядят объекты.

package main;
use Data::Printer;
p my $foo1 = Foo->new( bar => q{} );
p my $foo2 = Foo->new( bar => 123 );
p my $foo3 = Foo->new;

__END__

Foo  {
    Parents       Moo::Object
    public methods (2) : bar, new
    private methods (0)
    internals: {
        bar   undef
    }
}
Foo  {
    Parents       Moo::Object
    public methods (2) : bar, new
    private methods (0)
    internals: {
        bar   123
    }
}
Foo  {
    Parents       Moo::Object
    public methods (2) : bar, new
    private methods (0)
    internals: {}
}

В Moose вы также можете определить свой собственный тип, который происходит от вашего Str и имеет встроенное принуждение. Затем вы можете сделать все свои атрибуты этого типа и включить принуждение.

Чтобы заставить Му вести себя так, посмотрите документацию has и атрибут isa, который находится прямо над coerce (ссылка выше).

Другие вопросы по тегам