Perl 使Moose构造函数忽略未定义的参数

Perl 使Moose构造函数忽略未定义的参数,perl,moose,Perl,Moose,哈希表是Perl对象的典型初始值设定项。现在,您的输入是不可靠的,因为您不知道任何给定的键是否有定义的值,也不知道该键是否存在。现在,您希望将这种不可靠的输入输入到Moose对象中,虽然缺少关键点是完全可以的,但您确实希望去掉未定义的值,这样您就不会得到一个充满未定义属性的对象 在实例化对象并过滤掉未定义的值时,您当然可以非常小心。但假设您希望在构造函数中安装该过滤器,因为它位于一个位置。您希望构造函数忽略未定义的值,但不要在遇到它们时死亡 对于访问器方法,可以使用aroundaround来防止

哈希表是Perl对象的典型初始值设定项。现在,您的输入是不可靠的,因为您不知道任何给定的键是否有定义的值,也不知道该键是否存在。现在,您希望将这种不可靠的输入输入到Moose对象中,虽然缺少关键点是完全可以的,但您确实希望去掉未定义的值,这样您就不会得到一个充满未定义属性的对象

在实例化对象并过滤掉未定义的值时,您当然可以非常小心。但假设您希望在构造函数中安装该过滤器,因为它位于一个位置。您希望构造函数忽略未定义的值,但不要在遇到它们时死亡

对于访问器方法,可以使用
around
around来防止将属性设置为
undef
。但是这些不是为构造函数调用的,只是为访问器调用的。Moose是否有类似的设施来实现与c'tor相同的效果,即阻止任何
unde
属性被接受

请注意,如果属性未定义,Moose
Any
类型将在对象中创建哈希键。我不希望这样,因为我希望
%$self
不包含任何
unde

以下是我做的一些测试:

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;

只需提供您自己的
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);
};

我意识到这有点重复,但您可以使用
BUILDARGS
钩住ctor:

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的引用

这正是我们要做的。如果使类不可变,则比编写自己的BUILDARGS方法快得多,因为代码是内联到生成的构造函数中的。

虽然给出的示例澄清了这个问题是受处理传递给构造函数的未定义属性的愿望的启发,这个问题本身还意味着只将undef传递给构造函数,这是我遇到并想要解决的问题

例如,
Class->new(undef)

我喜欢。它可以扩展为处理将undef值而不是hashref作为单独参数传递给构造函数的情况:

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::Undefortent似乎不支持这种情况。

Read。特别是。如果有人调用
Class->new({key=>value})
(即,传递一个hashref而不是一个列表),这将不起作用。太好了,再次感谢你,Brad!我的第一反应是将
替换为
每个
,但在迭代过程中调用
delete
时当然不能这样做。非常感谢!哦,天啊,有这么多的Moose::WhatNot和MooseX::WhatElse模块。。。他们做的正是我想要的!:-)我想这需要更多的闲逛和使用玩具。感谢以太,这可以说比编写自己的BUILDARGS更好,所以我接受这一点作为最佳答案。@Michael::)如果你喜欢irc,你可以在irc.perl.org#moose上找到一个几乎全天候的优秀支持网络。通常情况下,如果没有一个扩展可以满足您的需要,有人会全力以赴为您编写一个扩展,只是为了好玩汉克斯!实际上,我从未做过任何IRC,但这听起来像是一个尝试的机会。我会和哪个客户一起去?看起来
tin
是一个经典的选择,可用于Cygwin。也尝试一下Windows的mIRC。@Michael当我使用Windows时,我使用了XChat的Win32端口。我现在通过远程设备上的ssh+屏幕会话使用irssi。