これは私のprevious questionムース構造型について続きます。私は質問の長さについてお詫び申し上げます。私は、必要なすべての詳細が含まれていることを確認したかったのです。ムース強制とビルダー
MyApp::Type::Field
は、構造化タイプを定義します。私は強制的にそのvalue
属性をPerson
クラス(下記の例を参照)から設定できるようにしています。私の実際のアプリケーションでは、Field型が人の名前だけではなく、HashRefから強制的に強制されることに注意してください。
また、作成時にMyApp::Type::Field
size
とrequired
の読み取り専用属性をMyApp::Person
に設定する必要があります。ビルダーメソッドを使用してこれを行うことができますが、強制的に使用する場合は呼び出されません。強制的に新しいオブジェクトが作成されるため、ビルダーメソッドを使用しません。
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]
はMyApp::Person
にaround
メソッドモディファイアを追加し、それが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回設定する。各MyApp::Person
属性のサブタイプを作成し、非常によくMyApp::Type::Field
作品へStr
からそのサブタイプを強制するの
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]
提案するソリューション
提案。私は、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]
フィールドは、メタ属性を持つ属性よりもMooseX :: Types :: Structuredに似ています。使用の1つの例は、各フィールドが値、最大長(サイズ)、および必要なフラグを必要とするWebフォームです。モデル(この例ではPersonクラス)は、サイズと必須フラグを設定します。 'Field'はかなり一般的であることを意図していますが、' Person'クラスはより具体的です。これまではメタ属性を調べていましたが、アクセスするのは少し面倒です( '$ person-> meta-> get_attribute( 'name') - > size()')。サブタイプはオプションです。私はこれを見ていきます... – Mike
私はちょうどサブタイプを作成することで実験しました。そしてそれが素晴らしい解決策を提供するかもしれないと思います。私は明日のテストをもっとやるよ...ありがとう。 – Mike
サブタイプ提案を使用した提案されたソリューションで自分の答えを更新しました。アドバイスありがとうございます。 – Mike