2010-07-02 4 views
6

chromatic's recent blog Mam ciekawy temat podprocedury Moose has. Patrzyłem na kod źródłowy Moose i zauważyłem, że wewnątrz podprogramu jest zmienna $meta rozpakowana z . Skąd pochodzi $meta? Zacząłem brnąć przez różne moduły Moose i Class :: MOP. W wielu podprogramach wydaje się, że $meta jest powszechnie uznawany za pierwszy argument w @_, mimo że nie jest on specjalnie przekazywany do niego jako argumentu.W podprogramach Moose, jak meta dostaje się do @_?

Edycja: Oto oryginalny kod źródłowy dla has podprogramu:

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

Odpowiedz

2

molecules komentarz w ysth answer:

nie jestem pewien, w jaki sposób ma podprogram zostanie przekonwertowany do tego zamknięcia, ale to na pewno pokazuje curry charakter importowany ma

Oto (mam nadzieję!) prosty przykład, jak można to osiągnąć (jednak podejrzewam, że jest to znacznie bardziej skomplikowane i lepsze!)

Me ta.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'); 

Teraz po uruchomieniu meta_has.pl dostaniesz:

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

Nadzieję, że pomaga.

/I3az/

+0

+1 Świetny przykład. Dzięki. –

12

Szczególna magia szukasz jest w Moose::Exporter. Masz metodę has poprzez Moose.pm z tego kodu:

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

Uwaga Opcja „with_meta” dla setup_import_methods - importuje te metody do nazw rozmówcy w sposób zapewniający, że pierwszy argument przekazany będzie obiekt metaclass.

Różne moduły MooseX, które rozszerzają Moose, używają Moose :: Exporter do importowania nowych symboli do przestrzeni nazw rozmówcy. Więcej informacji na temat tego procesu można znaleźć w książce kucharskiej, poczynając od numeru Moose::Cookbook::Extending::Recipe1.

+0

+1 Dzięki. Widzę, że muszę spędzić trochę czasu na studiowaniu Moose :: Exporter, zanim naprawdę go zrozumiem. Nie jest dla mnie jasne, w jaki sposób '$ meta' (lub dokładniej, odniesienie, do którego odnosi się' $ meta'), przechodzi w '@ _', ale zdecydowanie wskazałeś mi właściwy kierunek. –

+0

Możesz szczególnie chcieć spojrzeć na prywatną metodę '_make_wrapped_sub_with_meta' wewnątrz' Moose :: Exporter'. Wartość zwracana z tego jest zainstalowana jako "ma" według Moose'a. – perigrin

6

To, co faktycznie zostało zaimportowane do twojego pakietu, nie jest podprogramem has(), ale zamknięciem wstawiającym obiekt meta. Widać dokładnie, jak to się dzieje z:

$ 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 to samo zamknięcie; powyżej są wymienione w nim zmienne.

+0

Zmieniłem go na 'perl -we'package X; użyj danych :: Dump :: Streamer; użyj łosia; Dump (\ & has) '', aby uruchomić go na moim komputerze Debian Lenny. –

+0

+1 Dzięki. Nie jestem pewien, w jaki sposób podprogram 'ma' jest konwertowany do tego zamknięcia, ale to zdecydowanie pokazuje curried charakter zaimportowanego' ma'. –

+0

Dla każdego, kto chce zobaczyć wyjście, które jest nieco mniej niechlujne, spróbuj zastąpić 'Dump (\ & has)' w jedno-liniowej z 'Dump (\ & after)'. –

Powiązane problemy