bump version to 0.79
[gitmo/Moose.git] / lib / Moose / Exporter.pm
index 74be72d..de058f1 100644 (file)
@@ -3,11 +3,15 @@ package Moose::Exporter;
 use strict;
 use warnings;
 
+our $VERSION   = '0.79';
+$VERSION = eval $VERSION;
+our $AUTHORITY = 'cpan:STEVAN';
+
 use Class::MOP;
 use List::MoreUtils qw( first_index uniq );
 use Moose::Util::MetaRole;
 use Sub::Exporter;
-
+use Sub::Name qw(subname);
 
 my %EXPORT_SPEC;
 
@@ -36,7 +40,7 @@ sub build_import_methods {
 
     my ( $exports, $is_removable )
         = $class->_make_sub_exporter_params(
-        [ $exporting_package, @exports_from ], $export_recorder );
+        [ @exports_from, $exporting_package ], $export_recorder );
 
     my $exporter = Sub::Exporter::build_exporter(
         {
@@ -161,103 +165,123 @@ sub _make_sub_exporter_params {
     return ( \%exports, \%is_removable );
 }
 
-{
-    # This variable gets closed over in each export _generator_. Then
-    # in the generator we grab the value and close over it _again_ in
-    # the real export, so it gets captured each time the generator
-    # runs.
-    #
-    # In the meantime, we arrange for the import method we generate to
-    # set this variable to the caller each time it is called.
-    #
-    # This is all a bit confusing, but it works.
-    my $CALLER;
-
-    sub _make_wrapped_sub {
-        shift;
-        my $fq_name         = shift;
-        my $sub             = shift;
-        my $export_recorder = shift;
-
-
-        # We need to set the package at import time, so that when
-        # package Foo imports has(), we capture "Foo" as the
-        # package. This lets other packages call Foo::has() and get
-        # the right package. This is done for backwards compatibility
-        # with existing production code, not because this is a good
-        # idea ;)
-        return sub {
-            my $caller = $CALLER;
-
-            my $sub = Class::MOP::subname( $fq_name => sub { $sub->( $caller, @_ ) } );
+our $CALLER;
 
-            $export_recorder->{$sub} = 1;
+sub _make_wrapped_sub {
+    my $self            = shift;
+    my $fq_name         = shift;
+    my $sub             = shift;
+    my $export_recorder = shift;
 
-            return $sub;
-        };
-    }
+    # We need to set the package at import time, so that when
+    # package Foo imports has(), we capture "Foo" as the
+    # package. This lets other packages call Foo::has() and get
+    # the right package. This is done for backwards compatibility
+    # with existing production code, not because this is a good
+    # idea ;)
+    return sub {
+        my $caller = $CALLER;
 
-    sub _make_import_sub {
-        shift;
-        my $exporting_package = shift;
-        my $exporter          = shift;
-        my $exports_from      = shift;
-        my $export_to_main    = shift;
-
-        return sub {
-            # I think we could use Sub::Exporter's collector feature
-            # to do this, but that would be rather gross, since that
-            # feature isn't really designed to return a value to the
-            # caller of the exporter sub.
-            #
-            # Also, this makes sure we preserve backwards compat for
-            # _get_caller, so it always sees the arguments in the
-            # expected order.
-            my $traits;
-            ($traits, @_) = Moose::Exporter::_strip_traits(@_);
-
-            # Normally we could look at $_[0], but in some weird cases
-            # (involving goto &Moose::import), $_[0] ends as something
-            # else (like Squirrel).
-            my $class = $exporting_package;
-
-            $CALLER = Moose::Exporter::_get_caller(@_);
-
-            # this works because both pragmas set $^H (see perldoc
-            # perlvar) which affects the current compilation -
-            # i.e. the file who use'd us - which is why we don't need
-            # to do anything special to make it affect that file
-            # rather than this one (which is already compiled)
-
-            strict->import;
-            warnings->import;
-
-            # we should never export to main
-            if ( $CALLER eq 'main' && ! $export_to_main ) {
-                warn
-                    qq{$class does not export its sugar to the 'main' package.\n};
-                return;
-            }
+        my $wrapper = $self->_make_wrapper($caller, $sub, $fq_name);
 
-            my $did_init_meta;
-            for my $c ( grep { $_->can('init_meta') } $class, @{$exports_from} ) {
+        my $sub = subname($fq_name => $wrapper);
 
-                $c->init_meta( for_class => $CALLER );
-                $did_init_meta = 1;
-            }
+        $export_recorder->{$sub} = 1;
 
-            if ( $did_init_meta && @{$traits} ) {
-                _apply_meta_traits( $CALLER, $traits );
-            }
-            elsif ( @{$traits} ) {
-                Moose->throw_error("Cannot provide traits when $class does not have an init_meta() method");
-            }
+        return $sub;
+    };
+}
 
-            goto $exporter;
-        };
+sub _make_wrapper {
+    my $class   = shift;
+    my $caller  = shift;
+    my $sub     = shift;
+    my $fq_name = shift;
+
+    my $wrapper = sub { $sub->($caller, @_) };
+    if (my $proto = prototype $sub) {
+        # XXX - Perl's prototype sucks. Use & to make set_prototype
+        # ignore the fact that we're passing a "provate variable"
+        &Scalar::Util::set_prototype($wrapper, $proto);
     }
+    return $wrapper;
+}
+
+sub _make_import_sub {
+    shift;
+    my $exporting_package = shift;
+    my $exporter          = shift;
+    my $exports_from      = shift;
+    my $export_to_main    = shift;
+
+    return sub {
+
+        # I think we could use Sub::Exporter's collector feature
+        # to do this, but that would be rather gross, since that
+        # feature isn't really designed to return a value to the
+        # caller of the exporter sub.
+        #
+        # Also, this makes sure we preserve backwards compat for
+        # _get_caller, so it always sees the arguments in the
+        # expected order.
+        my $traits;
+        ( $traits, @_ ) = _strip_traits(@_);
+
+        my $metaclass;
+        ( $metaclass, @_ ) = _strip_metaclass(@_);
+
+        # Normally we could look at $_[0], but in some weird cases
+        # (involving goto &Moose::import), $_[0] ends as something
+        # else (like Squirrel).
+        my $class = $exporting_package;
+
+        $CALLER = _get_caller(@_);
+
+        # this works because both pragmas set $^H (see perldoc
+        # perlvar) which affects the current compilation -
+        # i.e. the file who use'd us - which is why we don't need
+        # to do anything special to make it affect that file
+        # rather than this one (which is already compiled)
+
+        strict->import;
+        warnings->import;
+
+        # we should never export to main
+        if ( $CALLER eq 'main' && !$export_to_main ) {
+            warn
+                qq{$class does not export its sugar to the 'main' package.\n};
+            return;
+        }
+
+        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.
+            local $CALLER = $CALLER;
+            $c->init_meta( for_class => $CALLER, metaclass => $metaclass );
+            $did_init_meta = 1;
+        }
+
+        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.
+            local $CALLER = $CALLER;
+            _apply_meta_traits( $CALLER, $traits );
+        }
+        elsif ( @{$traits} ) {
+            require Moose;
+            Moose->throw_error(
+                "Cannot provide traits when $class does not have an init_meta() method"
+            );
+        }
+
+        goto $exporter;
+    };
 }
 
+
 sub _strip_traits {
     my $idx = first_index { $_ eq '-traits' } @_;
 
@@ -272,12 +296,24 @@ sub _strip_traits {
     return ( $traits, @_ );
 }
 
+sub _strip_metaclass {
+    my $idx = first_index { $_ eq '-metaclass' } @_;
+
+    return ( undef, @_ ) unless $idx >= 0 && $#_ >= $idx + 1;
+
+    my $metaclass = $_[ $idx + 1 ];
+
+    splice @_, $idx, 2;
+
+    return ( $metaclass, @_ );
+}
+
 sub _apply_meta_traits {
     my ( $class, $traits ) = @_;
 
     return unless @{$traits};
 
-    my $meta = $class->meta();
+    my $meta = Class::MOP::class_of($class);
 
     my $type = ( split /::/, ref $meta )[-1]
         or Moose->throw_error(
@@ -350,6 +386,11 @@ sub _remove_keywords {
     }
 }
 
+sub import {
+    strict->import;
+    warnings->import;
+}
+
 1;
 
 __END__
@@ -362,44 +403,55 @@ Moose::Exporter - make an import() and unimport() just like Moose.pm
 
   package MyApp::Moose;
 
-  use strict;
-  use warnings;
-
   use Moose ();
   use Moose::Exporter;
 
   Moose::Exporter->setup_import_methods(
-      with_caller => [ 'sugar1', 'sugar2' ],
+      with_caller => [ 'has_rw', 'sugar2' ],
       as_is       => [ 'sugar3', \&Some::Random::thing ],
       also        => 'Moose',
   );
 
+  sub has_rw {
+      my ($caller, $name, %options) = @_;
+      Class::MOP::Class->initialize($caller)->add_attribute($name,
+          is => 'rw',
+          %options,
+      );
+  }
+
   # then later ...
   package MyApp::User;
 
   use MyApp::Moose;
 
   has 'name';
-  sugar1 'do your thing';
+  has_rw 'size';
   thing;
 
   no MyApp::Moose;
 
 =head1 DESCRIPTION
 
-This module encapsulates the logic to export sugar functions like
-C<Moose.pm>. It does this by building custom C<import> and C<unimport>
-methods for your module, based on a spec your provide.
+This module encapsulates the exporting of sugar functions in a
+C<Moose.pm>-like manner. It does this by building custom C<import> and
+C<unimport> methods for your module, based on a spec you provide.
 
-It also lets your "stack" Moose-alike modules so you can export
+It also lets you "stack" Moose-alike modules so you can export
 Moose's sugar as well as your own, along with sugar from any random
 C<MooseX> module, as long as they all use C<Moose::Exporter>.
 
+To simplify writing exporter modules, C<Moose::Exporter> also imports
+C<strict> and C<warnings> into your exporter module, as well as into
+modules that use it.
+
 =head1 METHODS
 
 This module provides two public methods:
 
-=head2 Moose::Exporter->setup_import_methods(...)
+=over 4
+
+=item  B<< Moose::Exporter->setup_import_methods(...) >>
 
 When you call this method, C<Moose::Exporter> build custom C<import>
 and C<unimport> methods for your module. The import method will export
@@ -411,7 +463,7 @@ exported functions.
 
 This method accepts the following parameters:
 
-=over 4
+=over 8
 
 =item * with_caller => [ ... ]
 
@@ -437,18 +489,23 @@ themselves, and therefore wants to keep it.
 This is a list of modules which contain functions that the caller
 wants to export. These modules must also use C<Moose::Exporter>. The
 most common use case will be to export the functions from C<Moose.pm>.
+Functions specified by C<with_caller> or C<as_is> take precedence over
+functions exported by modules specified by C<also>, so that a module
+can selectively override functions exported by another module.
 
 C<Moose::Exporter> also makes sure all these functions get removed
 when C<unimport> is called.
 
 =back
 
-=head2 Moose::Exporter->build_import_methods(...)
+=item B<< Moose::Exporter->build_import_methods(...) >>
 
 Returns two code refs, one for import and one for unimport.
 
 Used by C<setup_import_methods>.
 
+=back
+
 =head1 IMPORTING AND init_meta
 
 If you want to set an alternative base object class or metaclass
@@ -488,7 +545,7 @@ Stevan Little and others.
 
 =head1 COPYRIGHT AND LICENSE
 
-Copyright 2008 by Infinity Interactive, Inc.
+Copyright 2009 by Infinity Interactive, Inc.
 
 L<http://www.iinteractive.com>