5

我正在创建一个包含 IP 地址列表的类,作为 Net::IP 对象。

我已将 Net::IP 对象包装为子类型 (IPAddress),并定义了从字符串到 IPAddress 的强制转换。然后,我向名为 ip_list 的类添加了一个属性,类型为 ArrayRef[IPAddress],并委托给Array trait的push方法。

use MooseX::Declare;
use Moose::Util::TypeConstraints;

use Net::IP;

subtype 'IPAddress'
    => as 'Object'
    => where { $_->isa('Net::IP') };

coerce 'IPAddress'
    => from 'Str'
    => via { Net::IP->new( $_ ) };

class IPs {

    has 'ip_list' => ( traits  => ['Array'],
                       isa    => 'ArrayRef[IPAddress]',
                       is     => 'rw',
                       coerce => 1,
                       auto_deref => 1,
                       default => sub { [] },
                       handles => {
                           add_ip    => 'push'
                       }
                       );

}

但是,如果我尝试像这样调用委托方法:

my $o = IPs->new();
$o->add_ip( '192.168.0.1' );

我收到错误“值 SCALAR(0x8017e8) 未通过容器类型约束‘IPAddress’...”

所以显然 add_ip 的参数没有被强制。

是否可以做我正在尝试的事情,或者我应该手动完成所有这些?我已经浏览了 Moose 手册,但我没有看到任何可以表明这两种方式的东西,但我可能遗漏了一些东西。

4

1 回答 1

7

不幸的是 Moose 没有链式强制(在内部解析这些并以自动方式找出“正确的事情”是非常复杂的),因此您需要自己定义链:

use Net::IP;

class_type 'Net::IP';

coerce 'Net::IP'
    => from 'Str'
    => via { Net::IP->new( $_ ) };

subtype 'ArrayRefOfIPAddresses'
    => as 'ArrayRef[Net::IP]';

coerce 'ArrayRefOfIPAddresses'
    => from 'ArrayRef[Str]'
    => via { [ map { Net::IP->new($_) } @$_ ] };

coerce 'ArrayRefOfIPAddresses'
    => from 'Str'
    => via { [ Net::IP->new($_) ] };

coerce 'ArrayRefOfIPAddresses'
    => from 'Net::IP'
    => via { [ $_ ] };

class IPs {

    has 'ip_list' => ( traits  => ['Array'],
                       isa    => 'ArrayRefOfIPAddresses',
                       # ... rest of declaration as before
                     );

}

PS。由于您使用的是 Array 本机委托特征,因此我建议您避免auto_deref- 添加一个处理程序:

has ip_list => (
    is => 'bare',
    # ...
    handles => {
        # ...
        ip_list => 'elements',
    },
);
于 2010-10-20T16:27:41.950 回答