如何更改属性类型? (Perl Moo)

时间:2016-09-16 13:13:12

标签: perl types attributes moo

我们使用Perl Moo。

让我们定义一组属性:

package C;
use Moo;
use Types::Standard qw(Str Int Num Maybe);

has 'x' => (is=>'rw', isa=>Str);
has 'y' => (is=>'rw', isa=>Int);
has 'z' => (is=>'rw', isa=>Int);

# here to insert make_optional() described below

1;

我想编写一个例程,用一些属性替换T和Maybe [T]。例如:make_optional(qw(x y))应该包含x Maybe[Str]的类型和y Maybe[Int]的类型。

如何使用Moo?

2 个答案:

答案 0 :(得分:4)

你不能。

Moo does not have a Meta Object Protocol。没有它,你就不能回头改变一些东西。

  

没有元对象。如果你需要这种复杂程度,你需要Moose - Moo很小,因为它明确地没有提供元协议。

此外,类型只是代码引用。

  

没有内置类型系统。 isa通过coderef验证;如果你需要复杂的类型,Type :: Tiny可以提供类型,类型库,并且可以与Moo和Moose无缝协作。

你可以做的就是写一种类型,在其他地方访问某种单身,以决定它是否像Maybe[Str]Str,但这是一个很长的镜头,可能是丑陋和疯狂的你不应该这样做。

答案 1 :(得分:2)

[[请注意,Maybe类型实际上并不是使属性本身成为可选属性,而是不能容忍的。默认情况下,Moo属性已经是可选的。但是为了讨论起见,我将继续使用可选与必需的术语。]]

因为我不喜欢“你不能”的答案,所以这里有一些代码可以实现你想要的...

use strict;
use warnings;

BEGIN {
    package MooX::MakeOptional;
    use Types::Standard qw( Maybe Any );
    use Exporter::Shiny our @EXPORT = qw( make_optional has );
    use namespace::clean;
    
    sub _exporter_validate_opts {
        my $opts = pop;
        $opts->{orig_has} = do {
            no strict 'refs';
            \&{ $opts->{into} . '::has' };
        };
        $opts->{attributes} = [];
        'namespace::clean'->clean_subroutines( $opts->{into}, 'has' );
    }
    
    sub _generate_has {
        my $opts = pop;
        
        my $attributes = $opts->{attributes};
        
        return sub {
            my ( $name, %spec ) = @_;
            if ( ref($name) eq 'ARRAY' ) {
                push @$attributes, $_, { %spec } for @$name;
            }
            else {
                push @$attributes, $name, \%spec;
            }
        };
    }
    
    sub _generate_make_optional {
        my $opts = pop;
        
        my $attributes = $opts->{attributes};
        my $orig_has   = $opts->{orig_has};
        
        return sub {
            my %optional;
            $optional{$_} = 1 for @_;
            
            while ( @$attributes ) {
                my ( $name, $spec ) = splice( @$attributes, 0, 2 );
                if ( $optional{$name} ) {
                    $spec->{isa} = Maybe[ $spec->{isa} or Any ];
                }
                $orig_has->( $name, %$spec );
            }
        }
    }
}

{
    package C;
    use Moo;
    use MooX::MakeOptional;
    use Types::Standard qw( Str Int );

    has 'x' => ( is => 'rw', isa => Str );
    has 'y' => ( is => 'rw', isa => Int );
    has 'z' => ( is => 'rw', isa => Int );

    make_optional( qw(x y) );
}

这是用虚拟替换替换Moo的has关键字,除了将属性定义存储到数组之外,该虚拟替换什么也没做。

然后,当调用make_optional时,它将遍历数组,并将每个属性定义传递给Moo的原始has关键字,但是如果指定则更改为可选。

use MooX::MakeOptional 始终需要确保它们在类定义末尾调用make_optional函数的类,即使它们没有可选属性 。如果没有可选属性,则只需调用make_optional并将其传递为空列表即可。