English 中文(简体)
利用单一模块,获得Moose加若干MooseX延伸
原标题:Use a single module and get Moose plus several MooseX extensions
  • 时间:2012-04-18 19:10:00
  •  标签:
  • perl
  • moose

请允许我说,我有一个包含以下几个组成部分的代码基:Moose -based levels,我希望它们都使用一套共同的MooseX:*推广单元。 但我不想让每个摩西人阶层开始这样做:

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();
}

但是,这一办法并非由民间在“自由、独立、独立、自由、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主、民主 模块:* 例如,试图使用<代码>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: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 ...
最佳回答

如讨论的那样,你不应直接使用其他推广方法init_meta。 相反,你基本上应当inline。 这些展期init_meta 方法:将所有这些方法结合起来,纳入贵国的<代码>init_meta。 这一点很脆弱,因为现在你把你的模块与其他单元捆绑在一起,这些单元随时都会发生变化。

e.g. to combine MooseX::HasDefaults::IsRO, MooseX::StrictConstructor and MooseX::Aliases, you d do something like this (warning: untested) (now tested!):

package Mooseish;

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

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 ;
}
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,
);

1;

可以通过这一类测试:

package MyObject;
use Mooseish;

sub foo { 1 }

has this => (
    isa =>  Str ,
    alias =>  that ,
);

1;

use strict;
use warnings;
use MyObject;
use Test::More;
use Test::Fatal;

like(
    exception { MyObject->new(does_not_exist => 1) },
    qr/unknown attribute.*does_not_exist/,
     strict constructor behaviour is present ,
);

can_ok( MyObject , qw(alias this that has with foo));

my $obj = MyObject->new(this =>  thing );
is($obj->that,  thing ,  can access attribute by its aliased name );

like(
    exception { $obj->this( new value ) },
    qr/Cannot assign a value to a read-only accessor/,
     attribute defaults to read-only ,
);

done_testing;

印刷:

ok 1 - strict constructor behaviour is present
ok 2 - MyObject->can(...)
ok 3 - can access attribute by its aliased name
ok 4 - attribute defaults to read-only
1..4
问题回答

为此,我可以接手煤炭,但毫无疑问,谎言是:

package MyMoose;                                                                                                                                                               

use strict;
use warnings;
use Carp  confess ;

sub import {
    my $caller = caller;
    eval <<"END" or confess("Loading MyMoose failed: $@");
    package $caller;
    use Moose;
    use MooseX::StrictConstructor;
    use MooseX::FollowPBP;
    1;
END
}

1;

通过这样做,你将使用说明重新纳入一揽子呼吁。 换言之,你把他们使用的等级重新推给他们。

And here you declare your person:

package MyPerson;                                                                                                                                                              
use MyMoose;

has first_name => ( is =>  ro , required => 1 );
has last_name  => ( is =>  rw , required => 1 );

1;

测试!

use lib  lib ;                                                                                                                                                                 
use MyPerson;
use Test::Most;

throws_ok { MyPerson->new( first_name =>  Bob  ) }
qr/QAttribute (last_name) is required/,
   Required attributes should be required ;

throws_ok {
    MyPerson->new(
        first_name =>  Billy ,
        last_name  =>  Bob ,
        what       =>  ? ,
    );
}
qr/Qunknown attribute(s) init_arg passed to the constructor: what/,
   ... and unknown keys should throw an error ;

my $person;
lives_ok { $person = MyPerson->new( first_name =>  Billy , last_name =>  Bob  ) }
 Calling the constructor with valid arguments should succeed ;

isa_ok $person,  MyPerson ;
can_ok $person, qw/get_first_name get_last_name set_last_name/;
ok !$person->can("set_first_name"),
   ... but we should not be able to set the first name ;
done_testing;

And the test results:

ok 1 - Required attributes should be required
ok 2 - ... and unknown keys should throw an error
ok 3 - Calling the constructor with valid arguments should succeed
ok 4 - The object isa MyPerson
ok 5 - MyPerson->can(...)
ok 6 - ... but we should not be able to set the first name
1..6

让我们保持这种秘密吗?





相关问题
Why does my chdir to a filehandle not work in Perl?

When I try a "chdir" with a filehandle as argument, "chdir" returns 0 and a pwd returns still the same directory. Should that be so? I tried this, because in the documentation to chdir I found: "...

How do I use GetOptions to get the default argument?

I ve read the doc for GetOptions but I can t seem to find what I need... (maybe I am blind) What I want to do is to parse command line like this myperlscript.pl -mode [sth] [inputfile] I can use ...

Object-Oriented Perl constructor syntax and named parameters

I m a little confused about what is going on in Perl constructors. I found these two examples perldoc perlbot. package Foo; #In Perl, the constructor is just a subroutine called new. sub new { #I ...

Where can I find object-oriented Perl tutorials? [closed]

A Google search yields a number of results - but which ones are the best? The Perl site appears to contain two - perlboot and perltoot. I m reading these now, but what else is out there? Note: I ve ...

热门标签