【问题标题】:How to change attribute types? (Perl Moo)如何更改属性类型? (珀尔穆)
【发布时间】:2016-09-16 13:13:12
【问题描述】:

我们使用 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 怎么做?

【问题讨论】:

  • 和你的很多问题一样,我问自己:为什么
  • @simbabque 我们有一些(轻量级且功能不丰富)ORM(高于 DBI + SQL 的级别)。现在我被任命为“业务对象”(高于 ORM 的级别)。业务对象的某些类型(和has 语句)是从数据库列信息自动生成的。但我需要手动指定可以采用 undef 值(与 NULL 列不同)(Maybe 类型)的列列表,并以简单的方式完成。
  • 您不能编写代码生成器并在表格布局发生变化时像使用 DBIC 一样运行它吗?
  • @simbabque 我们已经有了这样的代码生成器。问题是 NULL 列和属性之间没有一对一的对应关系,这些属性可以采用undef 值。是的,这很愚蠢;但这是我们的现实
  • 然后在该代码生成器中保留错误列的列表,并在生成的代码中修复它们。或者,如果最糟糕的情况发生了,请拥有一个空的数据库结构副本,以及一个使所有错误列都可以为空的 sql 脚本,然后在该数据库上运行您的代码生成器。但尝试在实际生成的代码中修复它,而不是在代码运行后运行的东西中修复它。

标签: perl types attributes moo


【解决方案1】:

你不能。

Moo does not have a Meta Object Protocol。没有它,您将无法返回并更改内容。

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

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

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


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

【讨论】:

  • 但似乎我可以得到一个coderef c 并将其替换为Maybe[c]。问题是从哪里获取给定属性的类型 coderef。
  • @porton 这可能是一个新问题。或者去#moose问问。他们会试图说服你不要这样做,但他们会提供帮助。
【解决方案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 并传递一个空列表。

【讨论】:

    猜你喜欢
    • 2017-05-02
    • 1970-01-01
    • 1970-01-01
    • 2016-10-29
    • 2021-10-19
    • 2019-10-25
    • 1970-01-01
    • 2017-07-29
    • 2017-03-04
    相关资源
    最近更新 更多