Как скомпоновать существующую роль Moose в класс во время выполнения?

Скажем, я определяю абстрактный My::Object и конкретные реализации ролей My::Object::TypeA и My::Object::TypeB. Из соображений удобства обслуживания я бы не хотел иметь жестко закодированную таблицу, которая просматривает тип объекта и применяет роли. В качестве примера DWIMmy я ищу что-то в этом роде в My::Object:

has => 'id' (isa => 'Str', required => 1);

sub BUILD {
  my $self = shift;

  my $type = $self->lookup_type(); ## Returns 'TypeB'
  {"My::Object::$type"}->meta->apply($self);
}

Позвольте мне получить My::Object с примененной ролью My::Object::TypeB, выполнив следующие действия:

my $obj = My::Object(id = 'foo')

Будет ли это делать то, что я хочу, или я на совершенно неправильном пути?

Изменить: я слишком упростил это; Я не хочу знать тип при создании экземпляра объекта, я хочу, чтобы объект определял свой тип и соответствующим образом применял правильные методы роли. Я отредактировал свой вопрос, чтобы сделать это более ясным.


person Oesor    schedule 08.06.2010    source источник


Ответы (3)


Ты пробовал это?

$perl -Moose -E'
     sub BUILD { my ($self, $p) = @_; my $role = qq[Class::$$p{role}]; $role->meta->apply($self) };
     package Class::A; use Moose::Role; has a => (is => q[ro], default => 1);
     package main; say Class->new(role => q[A])->dump'

Урожайность:

$VAR1 = bless( {
             'a' => 1
           }, 'Class::MOP::Class::__ANON__::SERIAL::1' );

Кажется, это то, что вы хотите. Почистил код в вызове oose.pm:

package Class; 
use Moose;
sub BUILD { 
    my ($self, $p) = @_;
    my $role = qq[Class::$$p{role}];
    $role->meta->apply($self);
}

package Class::A;
use Moose::Role;

has a => ( is => 'ro', default => 1 );

package main;
Class->new(role => 'A');
person perigrin    schedule 08.06.2010
comment
Нет, не было. Это был скорее вопрос о передовой практике, чтобы убедиться, что я правильно понял -›мета-документацию и что я не пошел по неправильному пути. - person Oesor; 09.06.2010
comment
Moose и Perl в целом очень хороши в качестве экспериментальных языков. Если у вас есть вопросы, попробуйте и посмотрите, каковы результаты. Если вы не понимаете результаты, тогда спросите… вы сэкономите себе много времени, ожидая, когда кто-то еще подтвердит/опровергнет вашу гипотезу. - person perigrin; 10.06.2010

Используйте MooseX::Traits, и ваш роль в черту, которую можно применить во время выполнения, то вы просто вызываете:

   # Where Class is a class that has `use MooseX::Traits`;
   # And TypeB is a simple role
   Class->new_with_traits( traits => [qw/TypeB/] )

   # or even the new, and now preferred method.
   Class->with_traits('TypeB')->new();
person Evan Carroll    schedule 08.06.2010
comment
Я отредактировал свой вопрос, чтобы сделать его более ясным. Насколько я могу судить, MooxeX::Traits требует, чтобы тип был известен при создании экземпляра объекта. Я хочу, чтобы объект определял свой тип и применял требуемую роль, а не требовал, чтобы он был частью API. - person Oesor; 09.06.2010

После обновления вопроса я дам ему еще одну трещину: with() - это просто вызов функции.

package Class;
use Moose;

BEGIN {
  with ( map "MyObject::$_", qw/TypeA TypeB/ );
}

Кроме того, вы можете сослаться на текущий пакет с константой perl __PACKAGE__.

person Evan Carroll    schedule 08.06.2010