Используйте один модуль и получите Moose плюс несколько расширений MooseX

Допустим, у меня есть кодовая база с кучей классов на основе Moose -, и я хочу, чтобы все они использовали общий набор модулей расширения MooseX ::*. Но я не хочу, чтобы каждый класс, основанный на Moose -, начинался вот так :

package My::Class;

use Moose;
use MooseX::Aliases;
use MooseX::HasDefaults::RO;
use MooseX::StrictConstructor;
...

. Вместо этого я хочу, чтобы каждый класс начинался вот так:

package MyClass;

use My::Moose;

и был в точности эквивалентен приведенному выше.

Моя первая попытка реализовать это была основана на подходе, использованном Мэйсоном ::Мусом(источником):

package My::Moose;

use Moose;
use Moose::Exporter;
use MooseX::Aliases();
use MooseX::StrictConstructor();
use MooseX::HasDefaults::RO();
use Moose::Util::MetaRole;

Moose::Exporter->setup_import_methods(also => [ 'Moose' ]);

sub init_meta {
    my $class = shift;
    my %params = @_;

    my $for_class = $params{for_class};

    Moose->init_meta(@_);
    MooseX::Aliases->init_meta(@_);
    MooseX::StrictConstructor->init_meta(@_);
    MooseX::HasDefaults::RO->init_meta(@_);

    return $for_class->meta();
}

Но этот подход не рекомендуется людьми на #канале IRC moose в irc..perl.org, и это не всегда работает, в зависимости от набора модулей MooseX::*. Например,попытка использовать класс My::Mooseвыше, чтобы сделать My::Classтаким:

package My::Class;

use My::Moose;

has foo => (isa => 'Str');

Приводит к следующей ошибке при загрузке класса:

Attribute (foo) of class My::Class has no associated methods (did you mean to provide an "is" argument?)
 at /usr/local/lib/perl5/site_perl/5.12.1/darwin-2level/Moose/Meta/Attribute.pm line 1020.
    Moose::Meta::Attribute::_check_associated_methods('Moose::Meta::Class::__ANON__::SERIAL::2=HASH(0x100bd6f00)') called at /usr/local/lib/perl5/site_perl/5.12.1/darwin-2level/Moose/Meta/Class.pm line 573
    Moose::Meta::Class::add_attribute('Moose::Meta::Class::__ANON__::SERIAL::1=HASH(0x100be2f10)', 'foo', 'isa', 'Str', 'definition_context', 'HASH(0x100bd2eb8)') called at /usr/local/lib/perl5/site_perl/5.12.1/darwin-2level/Moose.pm line 79
    Moose::has('Moose::Meta::Class::__ANON__::SERIAL::1=HASH(0x100be2f10)', 'foo', 'isa', 'Str') called at /usr/local/lib/perl5/site_perl/5.12.1/darwin-2level/Moose/Exporter.pm line 370
    Moose::has('foo', 'isa', 'Str') called at lib/My/Class.pm line 5
    require My/Class.pm called at t.pl line 1
    main::BEGIN() called at lib/My/Class.pm line 0
    eval {...} called at lib/My/Class.pm line 0

Должен быть MooseX ::HasDefaults ::RO предотвращает эту ошибку, но, по-видимому, он не призван выполнять свою работу. Комментирование строки MooseX::Aliases->init_meta(@_);«решает» проблему, но a )— это один из модулей, которые я хочу использовать, а b )еще больше подчеркивает ошибочность этого решения. (В частности, init_meta()следует вызывать только один раз.)

Итак, я открыт для предложений, полностью игнорируя мою неудачную попытку реализовать это. Приветствуется любая стратегия, если она дает результаты, описанные в начале этого вопроса.


Основываясь на ответе @Ether, теперь у меня есть следующее (, которое также не работает):

package My::Moose;

use Moose();
use Moose::Exporter;
use MooseX::Aliases();
use MooseX::StrictConstructor();
use MooseX::HasDefaults::RO();

my %class_metaroles = (
    class => [
        'MooseX::StrictConstructor::Trait::Class',
    ],

    attribute => [
        'MooseX::Aliases::Meta::Trait::Attribute', 
        'MooseX::HasDefaults::Meta::IsRO',
     ],
);

my %role_metaroles = (
    role =>
        [ 'MooseX::Aliases::Meta::Trait::Role' ],
    application_to_class =>
        [ 'MooseX::Aliases::Meta::Trait::Role::ApplicationToClass' ],
    application_to_role =>
        [ 'MooseX::Aliases::Meta::Trait::Role::ApplicationToRole' ],
);

if (Moose->VERSION >= 1.9900) {
    push(@{$class_metaroles{class}},
        'MooseX::Aliases::Meta::Trait::Class');

    push(@{$role_metaroles{applied_attribute}}, 
        'MooseX::Aliases::Meta::Trait::Attribute',
        'MooseX::HasDefaults::Meta::IsRO');
}
else {
    push(@{$class_metaroles{constructor}},
        'MooseX::StrictConstructor::Trait::Method::Constructor',
        'MooseX::Aliases::Meta::Trait::Constructor');
}

*alias = \&MooseX::Aliases::alias;

Moose::Exporter->setup_import_methods(
    also => [ 'Moose' ],
    with_meta => ['alias'],
    class_metaroles => \%class_metaroles,
    role_metaroles => \%role_metaroles,
);

С таким классом-образцом:

package My::Class;

use My::Moose;

has foo => (isa => 'Str');

Я получаю эту ошибку:

Attribute (foo) of class My::Class has no associated methods (did you mean to provide an "is" argument?) at...

С таким классом-образцом:

package My::Class;

use My::Moose;

has foo => (isa => 'Str', alias => 'bar');

] Я получаю эту ошибку:

Found unknown argument(s) passed to 'foo' attribute constructor in 'Moose::Meta::Attribute': alias at...

5
задан John Siracusa 20 April 2012 в 01:41
поделиться