X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMoose%2FExporter.pm;h=ed4d22005c8dd6a05c204be47b7b093cc210f669;hb=8ff7989077ecc7b9c2bc3e73eaa19b4db49f1804;hp=db6f935d06bafbfcfa8a75d14da0917dbba305d6;hpb=e0d3eb1053e46f28a5e4f46ca7769478c0d2d106;p=gitmo%2FMoose.git diff --git a/lib/Moose/Exporter.pm b/lib/Moose/Exporter.pm index db6f935..ed4d220 100644 --- a/lib/Moose/Exporter.pm +++ b/lib/Moose/Exporter.pm @@ -3,7 +3,8 @@ package Moose::Exporter; use strict; use warnings; -our $VERSION = '0.93'; +our $VERSION = '1.01'; +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,6 +473,7 @@ sub _make_unimport_sub { $caller, [ keys %{$exports} ], $export_recorder, + $is_reexport, ); }; } @@ -449,16 +483,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 +512,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 +524,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 +545,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( @@ -593,7 +639,9 @@ will export the functions you specify, and can also re-export functions exported by some other module (like C). The C 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 be cleaned. If you pass any parameters for L, this method will generate an C for you as well (see below for details). This @@ -641,9 +689,9 @@ when C is called. =back -Any of the C<*_roles> options for -C and -C are also acceptable. +You can also provide parameters for C +and C. Specifically, valid parameters +are "class_metaroles", "role_metaroles", and "base_object_roles". =item B<< Moose::Exporter->build_import_methods(...) >> @@ -723,6 +771,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 for details on reporting bugs. + =head1 AUTHOR Dave Rolsky Eautarch@urth.orgE