Можно ли автоматически привести параметры, передаваемые делегированным методам (из признака Array), используя Moose / MooseX :: Declare для Perl? - PullRequest
5 голосов
/ 20 октября 2010

Я создаю класс, который будет содержать список IP-адресов в виде объектов Net :: IP.

Я обернул объект Net :: IP подтипом (IPAddress) и определилприведение строки к IP-адресу.Затем я добавил атрибут в класс ip_list с типом ArrayRef [IPAddress] и делегировал методу push признака Array .

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, но не видел ничего, что указывало бы в любом случае, но я, вероятно, что-то упустил.

1 Ответ

7 голосов
/ 20 октября 2010

К сожалению, 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',
    },
);
...