4

もちろん、設定によって属性の初期引数の名前を変更できることは知っていますinit_arg(例)

package Test {
    use Moose;
    has attr => (
       is => 'ro',
       isa => 'Str',
       init_arg => 'attribute'
    );
}

それは私を可能にするでしょう

Test->new({ attribute => 'foo' });

だがしかし

Test->new({ attr => 'foo' });

同時に

MooseX::Aliasesは実際にはこの動作をしますが、エイリアスを作成するとアクセサーも作成されます。私は現在、そのモジュールのコードを理解しようとしており、それがどのように機能するかを判断できないかどうかを確認して、その機能を (私が理解している方法で) 複製できるようにしています。誰かがここでそれを行う方法を例で説明できれば、それは素晴らしいことです.

MX:: Aliasesは実際にコンストラクターに渡されたものを置き換えることによってこれを行っているようaround initialize_instance_slotですが、実際にどのように呼び出されるのかはまだわかりません。私のテスト コードでは、実際には実行されていないからです。

BUILDARGS私がやろうとしていることは、Meta Recipe3介して属性に追加しているラベルの名前を介してアクセサーの設定を許可するためです。あなたは私がやっていると言うかもしれません

has attr => (
   is => 'ro',
   isa => 'Str',
   alt_init_arg => 'attribute'
);

アップデート

これが、私がこれまでにやろうとしていることでなんとかうまくいったことです。

use 5.014;
use warnings;

package MooseX::Meta::Attribute::Trait::OtherName {
    use Moose::Role;
    use Carp;

    has other_name => (
        isa       => 'Str',
        predicate => 'has_other_name',
        required  => 1,
        is        => 'ro',
    );

    around initialize_instance_slot => sub {
        my $orig = shift;
        my $self = shift;

        my ( $meta_instance, $instance, $params ) = @_;

        confess 'actually calling this code';

        return $self->$orig(@_)
            unless $self->has_other_name && $self->has_init_arg;

        if ( $self->has_other_name ) {
            $params->{ $self->init_arg }
                = delete $params->{ $self->other_name };
        }
    };
}

package Moose::Meta::Attribute::Custom::Trait::OtherName {
    sub register_implementation { 'MooseX::Meta::Attribute::Trait::OtherName' }
}

package Message {
    use Moose;
#   use MooseX::StrictConstructor;

    has attr => (
        traits    => [ 'OtherName' ],
        is        => 'ro',
        isa       => 'Str',
        other_name => 'Attr',
    );

    __PACKAGE__->meta->make_immutable;
}

package Client {
    use Moose;

    sub serialize {
        my ( $self, $message ) = @_;

        confess 'no message' unless defined $message;

        my %h;
        foreach my $attr ( $message->meta->get_all_attributes ) {
            if (
                    $attr->does('MooseX::Meta::Attribute::Trait::OtherName')
                    && $attr->has_other_name
                ) {
                $h{$attr->other_name} = $attr->get_value( $message );
            }
        }
        return \%h;
    }
    __PACKAGE__->meta->make_immutable;
}

my $message = Message->new( Attr => 'foo' );

my $ua = Client->new;

my %h = %{ $ua->serialize( $message )};

use Data::Dumper::Concise;

say Dumper \%h

問題は、aroundブロックが実行されないことです。理由がわかりません。間違った場所などにラップしている可能性があります。

4

3 に答える 3

5

私は間違っているかもしれませんが、 BUILDARGS メソッドを使用して、あなたがやろうとしていることを達成できるかもしれないと思います。これにより、オブジェクトの作成に使用される前に、コンストラクターの引数を変更できます。

#!/usr/bin/env perl

use strict;
use warnings;

{
  package MyClass;

  use Moose;
  has attr => (
     is => 'ro',
     isa => 'Str',
     required => 1,
  );

  around BUILDARGS => sub {
    my $orig = shift;
    my $self = shift;
    my %args = ref $_[0] ? %{shift()} : @_;

    if (exists $args{attribute}) {
      $args{attr} = delete $args{attribute};
    }

    $self->$orig(%args);
  };
}

my $one = MyClass->new(attribute => "Hi");
my $two = MyClass->new(attr => "Bye");

print $one->attr, "\n";
print $two->attr, "\n";
于 2012-04-07T03:50:07.407 に答える
4

MooseX::Aliasesには、この機能を実現するための可動部分がいくつかあります。これは、動作を MOP のいくつかの異なる場所に適用する必要があるためです。ここのコードは、MooseX::Aliasesの Trait 属性のコードに非常によく似ています。

コードが呼び出されない理由は、特性を登録しようとしたときに何か問題が発生したためだと思われます。ここで使用している昔ながらの方法ではなく、使用しますMooseX::Aliases。セクションを役割内への呼び出しにMoose::Util::meta_attribute_alias置き換えてみてください。Moose::Meta::Attribute::Custom::Trait::OtherNameMoose::Util::meta_attribute_alias 'OtherName';

次に、ここにあるコードは不変クラスでは機能しません。不変性コードは属性のメタクラスではなく、クラスのメタクラスによって処理されるため、それらを処理するために 2 番目の特性を追加する必要があります。ロールの属性を処理するために、さらにいくつかの特性を追加する必要があると思います。次に、Moose::Exporter を接続して、すべてがコンパイルされたときにすべての特性が適切に適用されるようにする必要があります。

私はこれの単純なバージョンをイミュータブルで動作させました。このコードはgithubにもあります。

まず Attribute トレイト:

package MooseX::AltInitArg::Meta::Trait::Attribute;
use Moose::Role;
use namespace::autoclean;
Moose::Util::meta_attribute_alias 'AltInitArg';


has alt_init_arg => (
    is         => 'ro',
    isa        => 'Str',
    predicate  => 'has_alt_init_arg',
);


around initialize_instance_slot => sub {
    my $orig = shift;
    my $self = shift;
    my ($meta_instance, $instance, $params) = @_;

    return $self->$orig(@_)
        # don't run if we haven't set any alt_init_args
        # don't run if init_arg is explicitly undef
        unless $self->has_alt_init_arg && $self->has_init_arg;

    if (my @alternates = grep { exists $params->{$_} } ($self->alt_init_arg)) {
        if (exists $params->{ $self->init_arg }) {
            push @alternates, $self->init_arg;
        }

        $self->associated_class->throw_error(
            'Conflicting init_args: (' . join(', ', @alternates) . ')'
        ) if @alternates > 1;

        $params->{ $self->init_arg } = delete $params->{ $alternates[0] };
    }
    $self->$orig(@_);
};

1;
__END__

次に Class トレイト。

package MooseX::AltInitArg::Meta::Trait::Class;
use Moose::Role;
use namespace::autoclean;

around _inline_slot_initializer => sub {
    my $orig = shift;
    my $self = shift;
    my ($attr, $index) = @_;

    my @orig_source = $self->$orig(@_);
    return @orig_source
        # only run on aliased attributes
        unless $attr->meta->can('does_role')
            && $attr->meta->does_role('MooseX::AltInitArg::Meta::Trait::Attribute');
    return @orig_source
        # don't run if we haven't set any aliases
        # don't run if init_arg is explicitly undef
        unless $attr->has_alt_init_arg && $attr->has_init_arg;

    my $init_arg = $attr->init_arg;

    return (
        'if (my @aliases = grep { exists $params->{$_} } (qw('
          . $attr->alt_init_arg . '))) {',
            'if (exists $params->{' . $init_arg . '}) {',
                'push @aliases, \'' . $init_arg . '\';',
            '}',
            'if (@aliases > 1) {',
                $self->_inline_throw_error(
                    '"Conflicting init_args: (" . join(", ", @aliases) . ")"',
                ) . ';',
            '}',
            '$params->{' . $init_arg . '} = delete $params->{$aliases[0]};',
        '}',
        @orig_source,
    );
};
1;
__END__

最後にMoose::Exporter接着剤。

package MooseX::AltInitArg;
use Moose();

use Moose::Exporter;
use MooseX::AltInitArg::Meta::Trait::Attribute;

Moose::Exporter->setup_import_methods(
    class_metaroles => { class => ['MooseX::AltInitArg::Meta::Trait::Class'] }
);

1;
__END__

これがどのように使用されるかの例:

package MyApp;
use 5.10.1;
use Moose;
use MooseX::AltInitArg;

has foo => (
    is            => 'ro',
    traits        => ['AltInitArg'],
    alt_init_arg => 'bar',
);


my $obj = MyApp->new( bar => 'bar' );
say $obj->foo; # prints bar

Moose のメタプログラミングは信じられないほど強力ですが、多くの可動部分があるため (その多くはパフォーマンスの最大化のみに関係しています)、飛び込むと多くの作業が必要になります。

幸運を。

于 2012-04-11T06:37:41.623 に答える
0

だから私が聞いているのは次のことです:

  • 構築時に、属性はその init_arg および属性に定義された代替の init_args によって設定できる必要があります。
  • 属性は、インスタンスの構築時を除き、代替の init_args によって操作できないようにする必要があります。つまり、上記以外では、属性は「正常に」動作する必要があります。

それに基づくと、これはMooseX::MultiInitArg属性の特性によく一致するようです。はい?:)

于 2012-04-12T17:26:25.447 に答える