在Moose子程序中,$ meta如何进入@_?

Chr*_*oms 6 perl moose

chromatic最近的博客让我对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;
}
Run Code Online (Sandbox Code Playgroud)

Eth*_*her 12

您正在寻找的特殊魔法是在Moose :: Exporter中.您可以has通过以下代码通过Moose.pm 获取方法:

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,
    ],
);
Run Code Online (Sandbox Code Playgroud)

注意"with_meta"选项setup_import_methods- 它以确保传递的第一个参数将是元类对象的方式将这些方法导入调用者的命名空间.

扩展Moose的各种MooseX模块使用Moose :: Exporter将新符号导入调用者的命名空间.您可以在食谱中阅读有关此过程的更多信息,从Moose :: Cookbook :: Extending :: Recipe1开始.


yst*_*sth 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, @_);
     };
Run Code Online (Sandbox Code Playgroud)

$CODE1封闭本身; 以上是其中引用的变量.


dra*_*tun 2

molecules在ysth 答案中评论:

我不确定 has 子例程如何转换为这个闭包,但这绝对显示了导入的 has 的柯里化性质

这是(希望如此!)一个如何实现这一点的简单示例(但我怀疑Moose是以更复杂和更好的方式实现的!)

元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;
Run Code Online (Sandbox Code Playgroud)

导入.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;
Run Code Online (Sandbox Code Playgroud)

元_has.pl

use strict;
use warnings;
use Import;

has name => ( is => 'rw', isa => 'Int' );
Run Code Online (Sandbox Code Playgroud)

现在如果你运行meta_has.pl你会得到:

# Given => name is rw isa Int 
# meta a => A
# meta b => B
Run Code Online (Sandbox Code Playgroud)

希望有帮助。

/I3az/