2010-07-02 13 views
6

chromatic's recent blog tengo curiosidad acerca de la subrutina Moose has. Estaba mirando el código fuente de Moose y noté que dentro de la subrutina has, hay una variable $meta descomprimida desde @_. ¿De dónde viene $meta? Empecé a recorrer los distintos módulos Moose and Class :: MOP. En muchas subrutinas, parece que $meta se encuentra comúnmente como el primer argumento en @_, aunque no se le pasa específicamente como argumento.

Editar: Aquí está el código fuente original para el has subrutina:

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

Respuesta

2

molecules comentario en ysth answer:

No estoy seguro de cómo la tiene subrutina se convierte a este cierre, pero esto sin duda muestra la naturaleza del curry importado tiene

Aquí es (¡con suerte!) un ejemplo simple de cómo esto podría lograrse (sin embargo, sospecho que Moose lo hace de una manera mucho más compleja y mejor!)

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

Ahora si ejecuta meta_has.pl obtendrá:

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

Espero que ayude.

/I3az/

+0

+1 Gran ejemplo. Gracias. –

12

La magia especial que busca está en Moose::Exporter. Se obtiene mediante el método has Moose.pm de este código:

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

cuenta que la opción "with_meta" para setup_import_methods - importa esos métodos en espacio de nombres de la persona que llama de una manera que asegura que el primer argumento pasado será el objeto metaclase.

Los diversos módulos de MooseX que extienden Moose usan Moose :: Exporter para importar nuevos símbolos en el espacio de nombres de la persona que llama. Puede leer más sobre este proceso en el libro de cocina, comenzando en Moose::Cookbook::Extending::Recipe1.

+0

+1 Thanks. Veo que necesito pasar un poco de tiempo estudiando Moose :: Exporter antes de poder entenderlo realmente. Todavía no me queda claro cómo '$ meta' (o más exactamente, la referencia a la que se refiere' $ meta' más tarde) se mete en '@ _', pero definitivamente me has apuntado en la dirección correcta. –

+0

Puede desear específicamente mirar el método privado '_make_wrapped_sub_with_meta' dentro de' Moose :: Exporter'. El valor de retorno de esto es lo que está instalado como 'has' por Moose, creo. – perigrin

6

Lo que realmente se importa en el paquete no es la subrutina named() sino un cierre que inserta el metaobjeto. Usted puede ver exactamente cómo sucede esto con:

$ 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 es el cierre en sí; arriba se encuentran las variables a las que se hace referencia.

+0

Lo cambié a 'perl -we'package X; usa Data :: Dump :: Streamer; usa Moose; Dump (\ & has) '' para que funcione en mi máquina Debian Lenny. –

+0

+1 Gracias. No estoy seguro de cómo se convierte la subrutina 'has' a este cierre, pero esto definitivamente muestra la naturaleza al curry del' has' importado. –

+0

Para cualquiera que quiera ver una salida menos desordenada, intente reemplazar 'Dump (\ & has)' en el one-liner con 'Dump (\ & after)'. –

Cuestiones relacionadas