【问题标题】:In Moose, how do I require one of multiple attributes?在 Moose 中,我如何需要多个属性之一?
【发布时间】:2020-06-07 02:42:54
【问题描述】:

我希望能够声明所需的一组相互依赖的属性中的一个。

让我们假设一个简单的例子,数字 'nr_two' 是 'nr_one' + 1,而 'nr_one' 是 'nr_two' -1,其中一个必须在初始化时传入。

到目前为止,我已经看到这个问题已经解决了,例如通过 BUILDARGS 检查和每个懒惰的构建器:

has 'nr_one' => (
    is => 'ro',
    isa => 'Num',
    lazy => 1,
    builder => '_build_nr_one',
);
sub _build_nr_one { shift->nr_two - 1; }

has 'nr_two' => (
    is => 'ro',
    isa => 'Num',
    lazy => 1,
    builder => '_build_nr_two',
);
sub _build_nr_two { shift->nr_one + 1; }

around 'BUILDARGS' => sub {
    my $orig = shift; 
    my $self = shift; 
    my $args = is_hashref($_[0])? $_[0] : { @_ }; 
    die "Either nr_one or nr_two is required!" unless defined $args{nr_one} || defined $args{nr_two};
    return $self->$orig($args);
};

或者,避开周围:

has 'nr_one' => (
    is => 'ro',
    isa => 'Num',
    predicate => 'has_nr_one',
    lazy => 1,
    builder => '_build_nr_one',
);
sub _build_nr_one { shift->nr_two - 1; }

has 'nr_two' => (
    is => 'ro',
    isa => 'Num',
    predicate => 'has_nr_two',
    lazy => 1,
    builder => '_build_nr_two',
);
sub _build_nr_two { shift->nr_one + 1; }

sub BUILD { 
    my $self = shift;
    die "Either nr_one or nr_two is required!" unless $self->has_nr_one || $self->has_nr_two;
}

但是,我正在寻找可以在属性上声明的东西, 例如某种分组,然后可以进行自省,例如在 BUILD 中触发。

理想情况下,我想将其发送到通用角色或 Meta 类中以使其可用 使用某种更好的语法,以避免检查 BUILD(ARGS) 检查 或者依靠 pod 准确地声明事物。

是否有 cpan 模块可以帮助解决这个问题,或者有人知道实现这一点的模式? 任何提示/部分解决方案表示赞赏,如果不是:)

我想象的一个例子是这样的:

has 'nr_one' => (
    is => 'ro',
    isa => 'Num',
    lazy => 1,
    builder => '_build_nr_one',
    required_grouping => 'NumberGroup',
);
sub _build_nr_one { shift->nr_two - 1; }

has 'nr_two' => (
    is => 'ro',
    isa => 'Num',
    lazy => 1,
    builder => '_build_nr_two',
    required_grouping => 'NumberGroup',

);
sub _build_nr_two { shift->nr_one + 1; }

# when initialized without any attributes, error thrown:
# "One of 'nr_one', 'nr_two' is required"
# or, probably easier: "NumberGroup required!"

【问题讨论】:

  • 请举例说明分组应该如何工作
  • 完成,@HåkonHægland 。我很高兴有其他可以满足我需求的建议,不过,不要被这个例子束缚。

标签: perl moose


【解决方案1】:

我没有找到一种方法来使自定义MooseX::Type 或attribute trait 自动将方法修饰符添加到BUILDARGS() 以验证属性。但是使用这样的Moose::Role 很简单:

#! /usr/bin/env perl
package NumberGroup;
use Moose::Role;
around 'BUILDARGS' => sub {
    my $orig = shift; 
    my $self = shift; 
    my $args = (ref $_[0]) eq "HASH" ? $_[0] : { @_ }; 
    die "Either nr_one or nr_two is required!" unless defined $args->{nr_one} || defined $args->{nr_two};
    return $self->$orig($args);
};


package Main;
use Moose;

with 'NumberGroup';

has 'nr_one' => (
    is       => 'ro',
    isa      => 'Num',
);

has 'nr_two' => (
    is       => 'ro',
    isa      => 'Num',
);

package main;
use strict;
use warnings;
Main->new();

输出:

Either nr_one or nr_two is required! at ./p.pl line 8.

【讨论】:

  • 这会将相同的逻辑传递到一个角色中——这可能会或可能不会被认为是一种改进(远距离行动),但无论哪种方式,都不能自省。背景是我正在尝试验证传递给对象的数据,这需要对属性进行自省。我可以把这个留给 Moose,但 Moose 的错误处理并不是我想要的全部。无论如何,使用参数化角色,我至少可以将此作为蓝图,使方法更加统一。
  • “内省”是什么意思?为什么角色中的BUILDARGS()不能自省?
  • 我的意思是普通的required 可以使用 Moose::Meta::Attribute 进行内省。这不能,因为它不在属性元信息中。就 Moose 而言,它并没有真正声明
  • 我仍然不清楚为什么除了我在回答中建议的角色之外还需要自省。你会从内省中获得什么额外的好处?
  • 您可以通过使用自定义特征标记您的属性来使其动态化,然后让around BUILDARGS 自动处理它。然后你可以把它作为 MooseX 东西发布。
猜你喜欢
  • 2010-10-20
  • 2021-11-26
  • 1970-01-01
  • 2017-08-09
  • 2021-09-10
  • 1970-01-01
  • 2015-10-28
  • 1970-01-01
相关资源
最近更新 更多