В подпрограммах Moose, как $ meta попадает в @_? - PullRequest
6 голосов
/ 02 июля 2010

Недавний блог 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;
}

Ответы [ 3 ]

12 голосов
/ 02 июля 2010

Особая магия, которую вы ищете, находится в 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,
    ],
);

Обратите внимание на параметр «with_meta» для setup_import_methods - он импортирует эти методы в пространство имен вызывающего таким образом,первым переданным аргументом будет объект метакласса.

Различные модули MooseX, расширяющие Moose, используют Moose :: Exporter для импорта новых символов в пространство имен вызывающего.Вы можете прочитать больше об этом процессе в кулинарной книге, начиная с Moose :: Cookbook :: Extending :: Recipe1 .

6 голосов
/ 02 июля 2010

То, что на самом деле импортируется в ваш пакет, это не подпрограмма named (), а замыкание, которое вставляет метаобъект. Вы можете точно увидеть, как это происходит с:

$ 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 голосов
/ 03 июля 2010

molecules комментарий в YSTH ответ :

Я не уверен, как подпрограмма has преобразуется в это замыкание, но это определенно показывает каррированную природуУ import * есть

Вот (надеюсь!) простой пример того, как этого можно достичь (однако я подозреваю, что 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 /

...