[[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