chromatic's recent blog让我对Moose子程序has
感到好奇。我查看了Moose源代码并注意到has
子例程中有一个$meta
变量从@_
解压缩。 $meta
来自哪里?我已经开始涉及各种Moose和Class :: MOP模块。在许多子例程中,似乎$meta
通常被认为是@_
中的第一个参数,即使它没有作为参数专门传递给它。
编辑:以下是has
子例程的原始源代码:
sub has {
my $meta = shift;
my $name = shift;
Moose->throw_error('Usage: has \'name\' => ( key => value, ... )')
if @_ % 2 == 1;
my %options = ( definition_context => Moose::Util::_caller_info(), @_ );
my $attrs = ( ref($name) eq 'ARRAY' ) ? $name : [ ($name) ];
$meta->add_attribute( $_, %options ) for @$attrs;
}
答案 0 :(得分:12)
你正在寻找的特殊魔法在Moose::Exporter。您可以通过以下代码通过Moose.pm获取has
方法:
Moose::Exporter->setup_import_methods(
with_meta => [
qw( extends with has before after around override augment )
],
as_is => [
qw( super inner ),
\&Carp::confess,
\&Scalar::Util::blessed,
],
);
注意setup_import_methods
的“with_meta”选项 - 它以确保传递的第一个参数将是元类对象的方式将这些方法导入调用者的命名空间。
扩展Moose的各种MooseX模块使用Moose :: Exporter将新符号导入调用者的命名空间。您可以从食谱中了解有关此过程的更多信息,从Moose::Cookbook::Extending::Recipe1开始。
答案 1 :(得分:6)
实际导入到包中的不是命名的has()子例程,而是插入元对象的闭包。你可以确切地看到这种情况:
$ perl -we'use Data::Dump::Streamer; use Moose; Dump(\&has)'
my ($extra,$sub,@ex_args);
$extra = sub {
package Moose::Exporter;
use warnings;
use strict 'refs';
Class::MOP::class_of(shift @_);
};
$sub = sub {
package Moose;
use warnings;
use strict 'refs';
my $meta = shift @_;
my $name = shift @_;
'Moose'->throw_error(q[Usage: has 'name' => ( key => value, ... )]) if @_ % 2 == 1;
my(%options) = ('definition_context', Moose::Util::_caller_info(), @_);
my $attrs = ref $name eq 'ARRAY' ? $name : [$name];
$meta->add_attribute($_, %options) foreach (@$attrs);
};
@ex_args = ( 'main' );
$CODE1 = sub {
package Moose::Exporter;
use warnings;
use strict 'refs';
my(@curry) = &$extra(@ex_args);
return &$sub(@curry, @_);
};
$CODE1
是封闭本身;以上是其中引用的变量。
答案 2 :(得分:2)
molecules
评论:
我不确定has子程序如何转换为此闭包,但这肯定表明导入的curry性质
这是(希望!)一个如何实现这一目标的简单例子(但我怀疑Moose
以更复杂和更好的方式做到了!)
<强> Meta.pm 强>
package Meta;
sub new {
my $class = shift;
bless { @_ }, $class;
}
sub has {
my $meta = shift;
print "Given => @_ \n";
print "meta $_ => ", $meta->{$_}, "\n" for keys %$meta;
}
1;
<强> Import.pm 强>
package Import;
use strict;
use warnings;
use Meta;
# some arbitrary meta info!
our $Meta = Meta->new( a => 'A', b => 'B' );
sub import {
my $caller = caller;
# import 'has' into caller namespace
no strict 'refs';
*{$caller . '::has'} = sub { $Meta->has(@_) };
}
1;
<强> meta_has.pl 强>
use strict;
use warnings;
use Import;
has name => ( is => 'rw', isa => 'Int' );
现在,如果您运行meta_has.pl
,您将获得:
# Given => name is rw isa Int
# meta a => A
# meta b => B
希望有所帮助。
/ I3az /