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

发布于 2024-09-08 04:22:41 字数 833 浏览 11 评论 0原文

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

chromatic's recent blog got me curious about the Moose subroutine has. I was looking at the Moose source code and noticed that inside the has subroutine, there is a $meta variable unpacked from @_. Where does $meta come from? I've started wading through the various Moose and Class::MOP modules. In many subroutines, it seems that $meta is commonly found as the first argument in @_, even though it is not specifically passed to it as an argument.

Edit: Here is the original source code for the has subroutine:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

昔日梦未散 2024-09-15 04:22:41

您正在寻找的特殊魔力就在 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

The particular magic you are looking for is in Moose::Exporter. You get the has method via Moose.pm from this code:

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

Note the "with_meta" option for setup_import_methods -- it imports those methods into the caller's namespace in a manner which ensures that the first argument passed will be the metaclass object.

The various MooseX modules that extend Moose use Moose::Exporter to import new symbols into the caller's namespace. You can read more about this process in the cookbook, starting at Moose::Cookbook::Extending::Recipe1.

孤独患者 2024-09-15 04:22:41

实际上导入到包中的不是命名的 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 是闭包本身;上面是其中引用的变量。

What's actually imported into into your package isn't the named has() subroutine but a closure that inserts the meta object. You can see exactly how this happens with:

$ 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 is the closure itself; above are the variables referenced in it.

青衫儰鉨ミ守葔 2024-09-15 04:22:41

ysth 答案中的分子评论

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

(希望如此!)一个简单的示例,说明如何实现这一点(但是我怀疑 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/

molecules comment in ysth answer:

I'm not sure how the has subroutine gets converted to this closure, but this definitely shows the curried nature of the imported has

Here is (hopefully!) a simple example of how this could be achieved (however I suspect Moose does it in a much more complex and better way!)

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' );

Now if you run meta_has.pl you will get:

# Given => name is rw isa Int 
# meta a => A
# meta b => B

Hope that helps.

/I3az/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文