version bump
[gitmo/Moose.git] / lib / Moose / Exporter.pm
index db6f935..717b732 100644 (file)
@@ -3,7 +3,8 @@ package Moose::Exporter;
 use strict;
 use warnings;
 
-our $VERSION   = '0.93';
+our $VERSION = '1.08';
+our $XS_VERSION = $VERSION;
 $VERSION = eval $VERSION;
 our $AUTHORITY = 'cpan:STEVAN';
 
@@ -13,6 +14,10 @@ use Moose::Util::MetaRole;
 use Sub::Exporter 0.980;
 use Sub::Name qw(subname);
 
+use XSLoader;
+
+XSLoader::load( 'Moose', $XS_VERSION );
+
 my %EXPORT_SPEC;
 
 sub setup_import_methods {
@@ -33,12 +38,15 @@ sub build_import_methods {
 
     $EXPORT_SPEC{$exporting_package} = \%args;
 
-    my @exports_from = $class->_follow_also( $exporting_package );
+    my @exports_from = $class->_follow_also($exporting_package);
 
     my $export_recorder = {};
+    my $is_reexport     = {};
 
     my $exports = $class->_make_sub_exporter_params(
-        [ @exports_from, $exporting_package ], $export_recorder,
+        [ @exports_from, $exporting_package ],
+        $export_recorder,
+        $is_reexport,
     );
 
     my $exporter = Sub::Exporter::build_exporter(
@@ -49,14 +57,24 @@ sub build_import_methods {
     );
 
     my %methods;
-    $methods{import} = $class->_make_import_sub( $exporting_package,
-        $exporter, \@exports_from );
+    $methods{import} = $class->_make_import_sub(
+        $exporting_package,
+        $exporter,
+        \@exports_from,
+        $is_reexport
+    );
 
-    $methods{unimport} = $class->_make_unimport_sub( $exporting_package,
-        $exports, $export_recorder );
+    $methods{unimport} = $class->_make_unimport_sub(
+        $exporting_package,
+        $exports,
+        $export_recorder,
+        $is_reexport
+    );
 
-    $methods{init_meta} = $class->_make_init_meta( $exporting_package,
-        \%args );
+    $methods{init_meta} = $class->_make_init_meta(
+        $exporting_package,
+        \%args
+    );
 
     my $package = Class::MOP::Package->initialize($exporting_package);
     for my $to_install ( @{ $args{install} || [] } ) {
@@ -67,7 +85,7 @@ sub build_import_methods {
         $package->add_package_symbol( $symbol, $methods{$to_install} );
     }
 
-    return ( $methods{import}, $methods{unimport}, $methods{init_meta} )
+    return ( $methods{import}, $methods{unimport}, $methods{init_meta} );
 }
 
 {
@@ -85,12 +103,12 @@ sub build_import_methods {
     sub _follow_also_real {
         my $exporting_package = shift;
 
-        if (!exists $EXPORT_SPEC{$exporting_package}) {
+        if ( !exists $EXPORT_SPEC{$exporting_package} ) {
             my $loaded = Class::MOP::is_class_loaded($exporting_package);
 
             die "Package in also ($exporting_package) does not seem to "
-              . "use Moose::Exporter"
-              . ($loaded ? "" : " (is it loaded?)");
+                . "use Moose::Exporter"
+                . ( $loaded ? "" : " (is it loaded?)" );
         }
 
         my $also = $EXPORT_SPEC{$exporting_package}{also};
@@ -99,9 +117,9 @@ sub build_import_methods {
 
         my @also = ref $also ? @{$also} : $also;
 
-        for my $package (@also)
-        {
-            die "Circular reference in 'also' parameter to Moose::Exporter between $exporting_package and $package"
+        for my $package (@also) {
+            die
+                "Circular reference in 'also' parameter to Moose::Exporter between $exporting_package and $package"
                 if $seen->{$package};
 
             $seen->{$package} = 1;
@@ -112,9 +130,10 @@ sub build_import_methods {
 }
 
 sub _make_sub_exporter_params {
-    my $class             = shift;
-    my $packages          = shift;
-    my $export_recorder   = shift;
+    my $class           = shift;
+    my $packages        = shift;
+    my $export_recorder = shift;
+    my $is_reexport  = shift;
 
     my %exports;
 
@@ -149,7 +168,7 @@ sub _make_sub_exporter_params {
         }
 
         for my $name ( @{ $args->{as_is} } ) {
-            my ($sub, $coderef_name);
+            my ( $sub, $coderef_name );
 
             if ( ref $name ) {
                 $sub = $name;
@@ -158,23 +177,8 @@ sub _make_sub_exporter_params {
                 ( $coderef_pkg, $coderef_name )
                     = Class::MOP::get_code_info($name);
 
-                # Moose re-exports things from Carp and Scalar::Util. Usually,
-                # we want to remove those again at unimport time. However, the
-                # importing package might have imported those symbols
-                # explicitly after using Moose ala
-                #
-                # use Moose;
-                # use Carp qw( confess );
-                #
-                # In this case, we don't want to remove 'confess' when
-                # unimporting. To do that, we wrap the exports from other
-                # packages in anonymous coderef. Then, at unimport time, we
-                # can figure out if the package symbol still contains the
-                # coderef we exported, or if the user overwrote it with
-                # something else we don't want to remove.
                 if ( $coderef_pkg ne $package ) {
-                    $sub = sub { goto &$name };
-                    &Scalar::Util::set_prototype( $sub, prototype $name );
+                    $is_reexport->{$coderef_name} = 1;
                 }
             }
             else {
@@ -186,7 +190,7 @@ sub _make_sub_exporter_params {
 
             $export_recorder->{$sub} = 1;
 
-            $exports{$coderef_name} = sub { $sub };
+            $exports{$coderef_name} = sub {$sub};
         }
     }
 
@@ -194,9 +198,9 @@ sub _make_sub_exporter_params {
 }
 
 sub _sub_from_package {
-    my $sclass = shift;
+    my $sclass  = shift;
     my $package = shift;
-    my $name = shift;
+    my $name    = shift;
 
     my $sub = do {
         no strict 'refs';
@@ -205,8 +209,7 @@ sub _sub_from_package {
 
     return $sub if defined &$sub;
 
-    Carp::cluck
-            "Trying to export undefined sub ${package}::${name}";
+    Carp::cluck "Trying to export undefined sub ${package}::${name}";
 
     return;
 }
@@ -228,9 +231,9 @@ sub _make_wrapped_sub {
     return sub {
         my $caller = $CALLER;
 
-        my $wrapper = $self->_curry_wrapper($sub, $fq_name, $caller);
+        my $wrapper = $self->_curry_wrapper( $sub, $fq_name, $caller );
 
-        my $sub = subname($fq_name => $wrapper);
+        my $sub = subname( $fq_name => $wrapper );
 
         $export_recorder->{$sub} = 1;
 
@@ -247,10 +250,12 @@ sub _make_wrapped_sub_with_meta {
     return sub {
         my $caller = $CALLER;
 
-        my $wrapper = $self->_late_curry_wrapper($sub, $fq_name,
-            sub { Class::MOP::class_of(shift) } => $caller);
+        my $wrapper = $self->_late_curry_wrapper(
+            $sub, $fq_name,
+            sub { Class::MOP::class_of(shift) } => $caller
+        );
 
-        my $sub = subname($fq_name => $wrapper);
+        my $sub = subname( $fq_name => $wrapper );
 
         $export_recorder->{$sub} = 1;
 
@@ -264,11 +269,12 @@ sub _curry_wrapper {
     my $fq_name = shift;
     my @extra   = @_;
 
-    my $wrapper = sub { $sub->(@extra, @_) };
-    if (my $proto = prototype $sub) {
+    my $wrapper = sub { $sub->( @extra, @_ ) };
+    if ( my $proto = prototype $sub ) {
+
         # XXX - Perl's prototype sucks. Use & to make set_prototype
         # ignore the fact that we're passing "private variables"
-        &Scalar::Util::set_prototype($wrapper, $proto);
+        &Scalar::Util::set_prototype( $wrapper, $proto );
     }
     return $wrapper;
 }
@@ -281,15 +287,17 @@ sub _late_curry_wrapper {
     my @ex_args = @_;
 
     my $wrapper = sub {
+
         # resolve curried arguments at runtime via this closure
-        my @curry = ( $extra->( @ex_args ) );
-        return $sub->(@curry, @_);
+        my @curry = ( $extra->(@ex_args) );
+        return $sub->( @curry, @_ );
     };
 
-    if (my $proto = prototype $sub) {
+    if ( my $proto = prototype $sub ) {
+
         # XXX - Perl's prototype sucks. Use & to make set_prototype
         # ignore the fact that we're passing "private variables"
-        &Scalar::Util::set_prototype($wrapper, $proto);
+        &Scalar::Util::set_prototype( $wrapper, $proto );
     }
     return $wrapper;
 }
@@ -299,6 +307,7 @@ sub _make_import_sub {
     my $exporting_package = shift;
     my $exporter          = shift;
     my $exports_from      = shift;
+    my $is_reexport    = shift;
 
     return sub {
 
@@ -315,9 +324,9 @@ sub _make_import_sub {
 
         my $metaclass;
         ( $metaclass, @_ ) = _strip_metaclass(@_);
-        $metaclass = Moose::Util::resolve_metaclass_alias(
-            'Class' => $metaclass
-        ) if defined $metaclass && length $metaclass;
+        $metaclass
+            = Moose::Util::resolve_metaclass_alias( 'Class' => $metaclass )
+            if defined $metaclass && length $metaclass;
 
         # Normally we could look at $_[0], but in some weird cases
         # (involving goto &Moose::import), $_[0] ends as something
@@ -337,6 +346,7 @@ sub _make_import_sub {
 
         my $did_init_meta;
         for my $c ( grep { $_->can('init_meta') } $class, @{$exports_from} ) {
+
             # init_meta can apply a role, which when loaded uses
             # Moose::Exporter, which in turn sets $CALLER, so we need
             # to protect against that.
@@ -346,6 +356,7 @@ sub _make_import_sub {
         }
 
         if ( $did_init_meta && @{$traits} ) {
+
             # The traits will use Moose::Role, which in turn uses
             # Moose::Exporter, which in turn sets $CALLER, so we need
             # to protect against that.
@@ -359,11 +370,25 @@ sub _make_import_sub {
             );
         }
 
-        goto $exporter;
+        my ( undef, @args ) = @_;
+        my $extra = shift @args if ref $args[0] eq 'HASH';
+
+        $extra ||= {};
+        if ( !$extra->{into} ) {
+            $extra->{into_level} ||= 0;
+            $extra->{into_level}++;
+        }
+
+        $class->$exporter( $extra, @args );
+
+        for my $name ( keys %{$is_reexport} ) {
+            no strict 'refs';
+            no warnings 'once';
+            _flag_as_reexport( \*{ join q{::}, $CALLER, $name } );
+        }
     };
 }
 
-
 sub _strip_traits {
     my $idx = first_index { $_ eq '-traits' } @_;
 
@@ -373,7 +398,7 @@ sub _strip_traits {
 
     splice @_, $idx, 2;
 
-    $traits = [ $traits ] unless ref $traits;
+    $traits = [$traits] unless ref $traits;
 
     return ( $traits, @_ );
 }
@@ -400,23 +425,30 @@ sub _apply_meta_traits {
     my $type = ( split /::/, ref $meta )[-1]
         or Moose->throw_error(
         'Cannot determine metaclass type for trait application . Meta isa '
-        . ref $meta );
+            . ref $meta );
 
-    my @resolved_traits
-        = map {
-            ref $_ ? $_ : Moose::Util::resolve_metatrait_alias( $type => $_ )
-        }
-        @$traits;
+    my @resolved_traits = map {
+        ref $_
+            ? $_
+            : Moose::Util::resolve_metatrait_alias( $type => $_ )
+    } @$traits;
 
     return unless @resolved_traits;
 
-    Moose::Util::MetaRole::apply_metaclass_roles(
-        for_class       => $class,
-        metaclass_roles => \@resolved_traits,
-    );
+    my %args = ( for => $class );
+
+    if ( $meta->isa('Moose::Meta::Role') ) {
+        $args{role_metaroles} = { role => \@resolved_traits };
+    }
+    else {
+        $args{class_metaroles} = { class => \@resolved_traits };
+    }
+
+    Moose::Util::MetaRole::apply_metaroles(%args);
 }
 
 sub _get_caller {
+
     # 1 extra level because it's called by import so there's a layer
     # of indirection
     my $offset = 1;
@@ -433,6 +465,7 @@ sub _make_unimport_sub {
     my $exporting_package = shift;
     my $exports           = shift;
     my $export_recorder   = shift;
+    my $is_reexport    = shift;
 
     return sub {
         my $caller = scalar caller();
@@ -440,7 +473,10 @@ sub _make_unimport_sub {
             $caller,
             [ keys %{$exports} ],
             $export_recorder,
+            $is_reexport,
         );
+        strict->unimport;
+        warnings->unimport;
     };
 }
 
@@ -449,16 +485,24 @@ sub _remove_keywords {
     my $package          = shift;
     my $keywords         = shift;
     my $recorded_exports = shift;
+    my $is_reexport   = shift;
 
     no strict 'refs';
 
-    foreach my $name ( @{ $keywords } ) {
+    foreach my $name ( @{$keywords} ) {
         if ( defined &{ $package . '::' . $name } ) {
             my $sub = \&{ $package . '::' . $name };
 
             # make sure it is from us
             next unless $recorded_exports->{$sub};
 
+            if ( $is_reexport->{$name} ) {
+                no strict 'refs';
+                next
+                    unless _export_is_flagged(
+                            \*{ join q{::} => $package, $name } );
+            }
+
             # and if it is from us, then undef the slot
             delete ${ $package . '::' }{$name};
         }
@@ -470,10 +514,11 @@ sub _make_init_meta {
     my $class = shift;
     my $args  = shift;
 
-    my %metaclass_roles;
+    my %old_style_roles;
     for my $role (
         map {"${_}_roles"}
-        qw(metaclass
+        qw(
+        metaclass
         attribute_metaclass
         method_metaclass
         wrapped_method_metaclass
@@ -481,18 +526,20 @@ sub _make_init_meta {
         constructor_class
         destructor_class
         error_class
-        application_to_class_class
-        application_to_role_class
-        application_to_instance_class)
+        )
         ) {
-        $metaclass_roles{$role} = $args->{$role} if exists $args->{$role};
+        $old_style_roles{$role} = $args->{$role}
+            if exists $args->{$role};
     }
 
     my %base_class_roles;
     %base_class_roles = ( roles => $args->{base_class_roles} )
         if exists $args->{base_class_roles};
 
-    return unless %metaclass_roles || %base_class_roles;
+    my %new_style_roles = map { $_ => $args->{$_} }
+        grep { exists $args->{$_} } qw( class_metaroles role_metaroles );
+
+    return unless %new_style_roles || %old_style_roles || %base_class_roles;
 
     return sub {
         shift;
@@ -500,9 +547,10 @@ sub _make_init_meta {
 
         return unless Class::MOP::class_of( $options{for_class} );
 
-        Moose::Util::MetaRole::apply_metaclass_roles(
-            for_class => $options{for_class},
-            %metaclass_roles,
+        Moose::Util::MetaRole::apply_metaroles(
+            for => $options{for_class},
+            %new_style_roles,
+            %old_style_roles,
         );
 
         Moose::Util::MetaRole::apply_base_class_roles(
@@ -521,6 +569,11 @@ sub import {
     warnings->import;
 }
 
+sub unimport {
+    strict->unimport;
+    warnings->unimport;
+}
+
 1;
 
 __END__
@@ -593,7 +646,9 @@ will export the functions you specify, and can also re-export functions
 exported by some other module (like C<Moose.pm>).
 
 The C<unimport> method cleans the caller's namespace of all the exported
-functions.
+functions. This includes any functions you re-export from other
+packages. However, if the consumer of your package also imports those
+functions from the original package, they will I<not> be cleaned.
 
 If you pass any parameters for L<Moose::Util::MetaRole>, this method will
 generate an C<init_meta> for you as well (see below for details). This
@@ -641,9 +696,9 @@ when C<unimport> is called.
 
 =back
 
-Any of the C<*_roles> options for
-C<Moose::Util::MetaRole::apply_metaclass_roles> and
-C<Moose::Util::MetaRole::base_class_roles> are also acceptable.
+You can also provide parameters for C<Moose::Util::MetaRole::apply_metaroles>
+and C<Moose::Util::MetaRole::base_class_roles>. Specifically, valid parameters
+are "class_metaroles", "role_metaroles", and "base_object_roles".
 
 =item B<< Moose::Exporter->build_import_methods(...) >>
 
@@ -723,6 +778,10 @@ These traits will be applied to the caller's metaclass
 instance. Providing traits for an exporting class that does not create
 a metaclass for the caller is an error.
 
+=head1 BUGS
+
+See L<Moose/BUGS> for details on reporting bugs.
+
 =head1 AUTHOR
 
 Dave Rolsky E<lt>autarch@urth.orgE<gt>