質問

これは私から続きます 前の質問 ムース構造タイプについて。質問の長さについてお詫びします。必要な詳細をすべて含めたことを確認したかったのです。

MyApp::Type::Field 構造化されたタイプを定義します。強制を使用して許可します value 私からより簡単に設定される属性 Person クラス(以下の例を参照)。フィールドタイプが単なる人の名前以上のものに使用される私の実際のアプリケーションでは、私もハッシュレフから強制されることに注意してください。

また、設定する必要があります MyApp::Type::Field sizerequired からの読み取り専用属性 MyApp::Person ビルド時に。ビルダー方式を使用してこれを行うことはできますが、これは、私の強制がビルダーメソッドを使用せずに新しいオブジェクトを直接作成するため、強制が使用される場合は呼び出されません。

Anを追加することでこれを回すことができます around メソッド修飾子へ MyApp::Person (以下の例を参照)が、これは乱雑に感じます。 around メソッド修飾子は頻繁に呼び出されますが、読み取り専用属性を1回設定する必要があります。

まだ強制を許可しながら、これを行うより良い方法はありますか? MyApp::Type::Field クラスは初期化できません sizerequired デフォルトまたはビルダーを介して、値がどうあるべきかを知る方法がないためです。

それは単に私が持っていないことを支持して強制を控えることができるかもしれません 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, 、そして、それが設定されないようにビルダーを変更します sizerequired:

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 実行されます、 sizerequired 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からそのサブタイプを強制する StrMyApp::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().

また、これは本当にムースオブジェクトの属性オブジェクトを構築しているように思われます。これは、既に属性オブジェクトを提供するメタオブジェクトシステムに基づいています。属性オブジェクトを自分で作るのではなく拡張してみませんか?

を参照してください ムースクックブックメタレシピ このアプローチの詳細については。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top