質問
これは私から続きます 前の質問 ムース構造タイプについて。質問の長さについてお詫びします。必要な詳細をすべて含めたことを確認したかったのです。
MyApp::Type::Field
構造化されたタイプを定義します。強制を使用して許可します value
私からより簡単に設定される属性 Person
クラス(以下の例を参照)。フィールドタイプが単なる人の名前以上のものに使用される私の実際のアプリケーションでは、私もハッシュレフから強制されることに注意してください。
また、設定する必要があります MyApp::Type::Field
size
と required
からの読み取り専用属性 MyApp::Person
ビルド時に。ビルダー方式を使用してこれを行うことはできますが、これは、私の強制がビルダーメソッドを使用せずに新しいオブジェクトを直接作成するため、強制が使用される場合は呼び出されません。
Anを追加することでこれを回すことができます around
メソッド修飾子へ MyApp::Person
(以下の例を参照)が、これは乱雑に感じます。 around
メソッド修飾子は頻繁に呼び出されますが、読み取り専用属性を1回設定する必要があります。
まだ強制を許可しながら、これを行うより良い方法はありますか? MyApp::Type::Field
クラスは初期化できません size
と required
デフォルトまたはビルダーを介して、値がどうあるべきかを知る方法がないためです。
それは単に私が持っていないことを支持して強制を控えることができるかもしれません around
修飾子。
MyApp::Type::Field
coerce 'MyApp::Type::Field'
=> from 'Str'
=> via { MyApp::Type::Field->new( value => $_ ) };
has 'value' => ( is => 'rw' );
has 'size' => ( is => 'ro', isa => 'Int', writer => '_set_size', predicate => 'has_size' );
has 'required' => ( is => 'ro', isa => 'Bool', writer => '_set_required', predicate => 'has_required' );
MyApp::Person
has name => ( is => 'rw', isa => 'MyApp::Type::Field', lazy => 1, builder => '_build_name', coerce => 1 );
sub _build_name {
print "Building name\n";
return MyApp::Type::Field->new( size => 255, required => 1 );
}
MyApp::Test
print "Create new person with coercion\n";
my $person = MyApp::Person->new();
print "Set name\n";
$person->name( 'Joe Bloggs' );
print "Name set\n";
printf ( "Name: %s [%d][%d]\n\n", $person->name->value, $person->name->size, $person->name->required );
print "Create new person without coercion\n";
$person = MyApp::Person->new();
print "Set name\n";
$person->name->value( 'Joe Bloggs' );
print "Name set\n";
printf ( "Name: %s [%d][%d]\n\n", $person->name->value, $person->name->size, $person->name->required );
印刷:
Create new person with coercion
Set name
Name set
Name: Joe Bloggs [0][0]
Create new person without coercion
Set name
Building name
Name set
Name: Joe Bloggs [255][2]
追加します around
メソッド修飾子へ MyApp::Person
, 、そして、それが設定されないようにビルダーを変更します size
と required
:
around 'name' => sub {
my $orig = shift;
my $self = shift;
print "Around name\n";
unless ( $self->$orig->has_size ) {
print "Setting size\n";
$self->$orig->_set_size( 255 );
};
unless ( $self->$orig->has_required ) {
print "Setting required\n";
$self->$orig->_set_required( 1 );
};
$self->$orig( @_ );
};
sub _build_name {
print "Building name\n";
return MyApp::Type::Field->new();
}
いつ MyApp::Test
実行されます、 size
と required
2回設定されています。
Create new person with coercion
Set name
Around name
Building name
Setting size
Setting required
Name set
Around name
Setting size
Setting required
Around name
Around name
Name: Joe Bloggs [255][3]
Create new person without coercion
Set name
Around name
Building name
Name set
Around name
Around name
Around name
Name: Joe Bloggs [255][4]
提案されたソリューション
daotoad's それぞれにサブタイプを作成するという提案 MyApp::Person
属性、およびaからそのサブタイプを強制する Str
に MyApp::Type::Field
非常にうまく機能します。ロット全体をforループで包むことで、複数のサブタイプ、強制、属性を作成することもできます。これは、同様のプロパティを持つ複数の属性を作成するのに非常に役立ちます。
以下の例では、私は使用して代表団を設定しました handles
, 、 となることによって $person->get_first_name
に翻訳されています $person->first_name->value
. 。ライターを追加すると、同等のセッターが提供され、クラスへのインターフェイスを非常にきれいにします。
package MyApp::Type::Field;
use Moose;
has 'value' => (
is => 'rw',
);
has 'size' => (
is => 'ro',
isa => 'Int',
writer => '_set_size',
);
has 'required' => (
is => 'ro',
isa => 'Bool',
writer => '_set_required',
);
__PACKAGE__->meta->make_immutable;
1;
package MyApp::Person;
use Moose;
use Moose::Util::TypeConstraints;
use namespace::autoclean;
{
my $attrs = {
title => { size => 5, required => 0 },
first_name => { size => 45, required => 1 },
last_name => { size => 45, required => 1 },
};
foreach my $attr ( keys %{$attrs} ) {
my $subtype = 'MyApp::Person::' . ucfirst $attr;
subtype $subtype => as 'MyApp::Type::Field';
coerce $subtype
=> from 'Str'
=> via { MyApp::Type::Field->new(
value => $_,
size => $attrs->{$attr}{'size'},
required => $attrs->{$attr}{'required'},
) };
has $attr => (
is => 'rw',
isa => $subtype,
coerce => 1,
writer => "set_$attr",
handles => { "get_$attr" => 'value' },
default => sub {
MyApp::Type::Field->new(
size => $attrs->{$attr}{'size'},
required => $attrs->{$attr}{'required'},
)
},
);
}
}
__PACKAGE__->meta->make_immutable;
1;
package MyApp::Test;
sub print_person {
my $person = shift;
printf "Title: %s [%d][%d]\n" .
"First name: %s [%d][%d]\n" .
"Last name: %s [%d][%d]\n",
$person->title->value || '[undef]',
$person->title->size,
$person->title->required,
$person->get_first_name || '[undef]',
$person->first_name->size,
$person->first_name->required,
$person->get_last_name || '[undef]',
$person->last_name->size,
$person->last_name->required;
}
my $person;
$person = MyApp::Person->new(
title => 'Mr',
first_name => 'Joe',
last_name => 'Bloggs',
);
print_person( $person );
$person = MyApp::Person->new();
$person->set_first_name( 'Joe' );
$person->set_last_name( 'Bloggs' );
print_person( $person );
1;
印刷:
Title: Mr [5][0]
First name: Joe [45][6]
Last name: Bloggs [45][7]
Title: [undef] [5][0]
First name: Joe [45][8]
Last name: Bloggs [45][9]
解決
すべての人が異なる要件を持つつもりです name
分野?これはありそうもないようです。
それぞれのパラメーターのセットがある可能性が高いようです Field
アプリケーションを承認します。したがって、タイプのPersonNameをフィールドのサブタイプとして定義します。あなたの強制は、文字列からPersonNameまでです。次に、強制コードと、呼び出したときに必要な値と長さに適切な値を適用できます Field->new()
.
また、これは本当にムースオブジェクトの属性オブジェクトを構築しているように思われます。これは、既に属性オブジェクトを提供するメタオブジェクトシステムに基づいています。属性オブジェクトを自分で作るのではなく拡張してみませんか?
を参照してください ムースクックブックメタレシピ このアプローチの詳細については。