2010-07-02 10 views
6

chromatic's recent blog mi ha fatto incuriosire la subroutine di Moose has. Stavo guardando il codice sorgente di Moose e ho notato che all'interno della subroutine 10 è stata decompressa una variabile $meta da @_. Da dove proviene $meta? Ho iniziato a guado attraverso i vari moduli Moose e Class :: MOP. In molte subroutine, sembra che $meta si trovi comunemente come primo argomento in @_, anche se non è passato in modo specifico ad esso come argomento.

Edit: Ecco il codice sorgente originale per la 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; 
} 

risposta

2

molecules commento in ysth answer:

io non sono sicuro di come l'ha subroutine viene convertito in questa chiusura, ma questo dimostra sicuramente la natura del curry importato ha

Ecco (si spera!) un semplice esempio di come questo potrebbe essere raggiunto (tuttavia sospetto che lo Moose lo faccia in un modo molto più complesso e migliore!)

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

Ora, se si esegue meta_has.pl si otterrà:

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

Spero che questo aiuti.

/I3az/

+0

+1 Grande esempio. Grazie. –

12

La particolare magia che si sta cercando è in Moose::Exporter. Si ottiene il metodo has tramite Moose.pm da questo codice:

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

Nota l'opzione "with_meta" per setup_import_methods - importa i metodi nel namespace del chiamante in modo da assicurare che il primo argomento passato sarà l'oggetto metaclass.

I vari moduli MooseX che estendono Moose utilizzano Moose :: Exporter per importare nuovi simboli nello spazio dei nomi del chiamante. Puoi leggere ulteriori informazioni su questo processo nel ricettario, a partire da Moose::Cookbook::Extending::Recipe1.

+0

+1 Grazie. Vedo che ho bisogno di passare un po 'di tempo a studiare Moose :: Exporter prima che io possa davvero capirlo. Non mi è ancora chiaro come '$ meta' (o più esattamente, il riferimento a cui' $ meta' in seguito si riferisce) entri in '@ _', ma tu mi hai sicuramente indirizzato nella giusta direzione. –

+0

Si può in particolare voler esaminare il metodo privato '_make_wrapped_sub_with_meta' in' Moose :: Exporter'. Il valore restituito da questo è ciò che è installato come 'has' da Moose, credo. – perigrin

6

Ciò che viene effettivamente importato nel pacchetto non è la subroutine named() ma una chiusura che inserisce il meta oggetto. È possibile vedere esattamente come questo accade 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 è la chiusura in sé; sopra sono le variabili a cui si fa riferimento in esso.

+0

L'ho cambiato in 'perl -we'package X; utilizzare Data :: Dump :: Streamer; usare Moose; Dump (\ & has) ''per farlo funzionare sulla mia macchina Debian Lenny. –

+0

+1 Grazie. Non sono sicuro di come la subroutine 'has' venga convertita in questa chiusura, ma questo mostra sicuramente la natura alesata del' has' importato. –

+0

Per chiunque voglia vedere un output un po 'meno disordinato, prova a sostituire 'Dump (\ & has)' nella one-liner con 'Dump (\ e after)'. –