【问题标题】:Make the Moose constructor ignore undef arguments使 Moose 构造函数忽略 undef 参数
【发布时间】:2014-09-20 00:04:56
【问题描述】:

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

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

对于访问器方法,可以使用around左右来防止属性被设置为undef。但是那些method modifiers 不是为构造函数调用的,只是为访问器调用的。 Moose 中是否有类似的工具可以为 c'tor 实现相同的效果,即排除任何 undef 属性被接受?

请注意,如果属性为 undef,Moose Any 类型将在对象中创建哈希键。我不希望这样,因为我希望 %$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;

【问题讨论】:

标签: perl moose


【解决方案1】:

这正是MooseX::UndefTolerant 所做的。如果你让你的类不可变,这将比编写你自己的 BUILDARGS 方法快得多,因为代码被内联到生成的构造函数中。

【讨论】:

  • 哦,天哪,有这么多 Moose::WhatNot 和 MooseX::WhatElse 模块......而且它们完全符合我的要求! :-) 我想这需要更多的闲逛和使用玩具。感谢 Ether,这可以说比编写自己的 BUILDARGS 更好,所以我接受这是最好的答案。
  • @Michael: :) 如果你喜欢 irc,你可以在 irc.perl.org #moose 找到一个几乎 24/7 的优秀支持网络。通常,如果没有一个扩展程序可以满足您的需求,那么有人会认真地为您写一个,只是为了好玩! :D
  • 谢谢!实际上从未做过任何 IRC,但这听起来像是尝试一下的机会。我会和什么客户一起去?看起来tin 是一个经典的选择,可用于 Cygwin。还可以试试mIRC for Windows。
  • @Michael 当我使用 Windows 时,我使用了 XChat 的 Win32 端口。我现在通过远程机器上的 ssh+screen 会话使用 irssi。
【解决方案2】:

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

【讨论】:

  • 太好了,再次感谢布拉德!我的第一直觉是用each 替换keys,但是在迭代过程中调用delete 时当然​​不能这样做。非常感谢!
【解决方案3】:

我意识到这有点重复,但您可以将 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 的引用。

【讨论】:

  • 如果有人调用 Class->new( {key => value} )(即传递 hashref 而不是列表),这将不起作用。
【解决方案4】:

虽然给出的示例阐明了该问题的灵感来自于处理传递给构造函数的 undef 属性的愿望,但该问题本身还暗示了仅将 undef 传递给构造函数的情况,这是我遇到并想要的解决。

例如,Class->new(undef)

我喜欢bvr's BUILDARGS answer。它可以扩展为处理将 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::UndefTolerant 似乎不支持这种情况。

【讨论】:

    猜你喜欢
    • 2017-03-08
    • 2020-10-18
    • 1970-01-01
    • 2021-04-29
    • 2020-11-07
    • 1970-01-01
    • 1970-01-01
    • 2021-04-22
    • 1970-01-01
    相关资源
    最近更新 更多