X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FMoose%2FExporter.pm;h=dcdef3cbeba1afb3805b1685e46e0e324944d31c;hb=ef487af73b144341c8fd2e4640b93d395dc414ed;hp=d996d92e308660c59604c84a5901483670d5e47c;hpb=cee38bb4bd607adc31e26728c3c0e016fa5dc750;p=gitmo%2FMoose.git diff --git a/lib/Moose/Exporter.pm b/lib/Moose/Exporter.pm index d996d92..dcdef3c 100644 --- a/lib/Moose/Exporter.pm +++ b/lib/Moose/Exporter.pm @@ -3,26 +3,20 @@ package Moose::Exporter; use strict; use warnings; -our $VERSION = '0.93'; -$VERSION = eval $VERSION; -our $AUTHORITY = 'cpan:STEVAN'; - +use Class::Load qw(is_class_loaded); use Class::MOP; use List::MoreUtils qw( first_index uniq ); use Moose::Util::MetaRole; +use Scalar::Util qw(reftype); use Sub::Exporter 0.980; use Sub::Name qw(subname); -use XSLoader; - -XSLoader::load( 'Moose', $VERSION ); - my %EXPORT_SPEC; sub setup_import_methods { my ( $class, %args ) = @_; - my $exporting_package = $args{exporting_package} ||= caller(); + $args{exporting_package} ||= caller(); $class->build_import_methods( %args, @@ -35,6 +29,8 @@ sub build_import_methods { my $exporting_package = $args{exporting_package} ||= caller(); + my $meta_lookup = $args{meta_lookup} || sub { Class::MOP::class_of(shift) }; + $EXPORT_SPEC{$exporting_package} = \%args; my @exports_from = $class->_follow_also($exporting_package); @@ -43,16 +39,16 @@ sub build_import_methods { my $is_reexport = {}; my $exports = $class->_make_sub_exporter_params( - [ @exports_from, $exporting_package ], + [ $exporting_package, @exports_from ], $export_recorder, $is_reexport, + $args{meta_lookup}, # so that we don't pass through the default ); - my $exporter = Sub::Exporter::build_exporter( - { - exports => $exports, - groups => { default => [':all'] } - } + my $exporter = $class->_make_exporter( + $exports, + $is_reexport, + $meta_lookup, ); my %methods; @@ -60,19 +56,22 @@ sub build_import_methods { $exporting_package, $exporter, \@exports_from, - $is_reexport + $is_reexport, + $meta_lookup, ); $methods{unimport} = $class->_make_unimport_sub( $exporting_package, $exports, $export_recorder, - $is_reexport + $is_reexport, + $meta_lookup, ); $methods{init_meta} = $class->_make_init_meta( $exporting_package, - \%args + \%args, + $meta_lookup, ); my $package = Class::MOP::Package->initialize($exporting_package); @@ -87,6 +86,49 @@ sub build_import_methods { return ( $methods{import}, $methods{unimport}, $methods{init_meta} ); } +sub _make_exporter { + my ($class, $exports, $is_reexport, $meta_lookup) = @_; + + return Sub::Exporter::build_exporter( + { + exports => $exports, + groups => { default => [':all'] }, + installer => sub { + my ($arg, $to_export) = @_; + my $meta = $meta_lookup->($arg->{into}); + + goto &Sub::Exporter::default_installer unless $meta; + + # don't overwrite existing symbols with our magically flagged + # version of it if we would install the same sub that's already + # in the importer + + my @filtered_to_export; + my %installed; + for (my $i = 0; $i < @{ $to_export }; $i += 2) { + my ($as, $cv) = @{ $to_export }[$i, $i + 1]; + + next if !ref($as) + && $meta->has_package_symbol('&' . $as) + && $meta->get_package_symbol('&' . $as) == $cv; + + push @filtered_to_export, $as, $cv; + $installed{$as} = 1 unless ref $as; + } + + Sub::Exporter::default_installer($arg, \@filtered_to_export); + + for my $name ( keys %{$is_reexport} ) { + no strict 'refs'; + no warnings 'once'; + next unless exists $installed{$name}; + _flag_as_reexport( \*{ join q{::}, $arg->{into}, $name } ); + } + }, + } + ); +} + { my $seen = {}; @@ -103,7 +145,7 @@ sub build_import_methods { my $exporting_package = shift; if ( !exists $EXPORT_SPEC{$exporting_package} ) { - my $loaded = Class::MOP::is_class_loaded($exporting_package); + my $loaded = is_class_loaded($exporting_package); die "Package in also ($exporting_package) does not seem to " . "use Moose::Exporter" @@ -128,18 +170,50 @@ sub build_import_methods { } } +sub _parse_trait_aliases { + my $class = shift; + my ($package, $aliases) = @_; + + my @ret; + for my $alias (@$aliases) { + my $name; + if (ref($alias)) { + reftype($alias) eq 'ARRAY' + or Moose->throw_error(reftype($alias) . " references are not " + . "valid arguments to the 'trait_aliases' " + . "option"); + + ($alias, $name) = @$alias; + } + else { + ($name = $alias) =~ s/.*:://; + } + push @ret, subname "${package}::${name}" => sub () { $alias }; + } + + return @ret; +} + sub _make_sub_exporter_params { - my $class = shift; - my $packages = shift; - my $export_recorder = shift; - my $is_reexport = shift; + my $class = shift; + my $packages = shift; + my $export_recorder = shift; + my $is_reexport = shift; + my $meta_lookup_override = shift; my %exports; + my $current_meta_lookup; for my $package ( @{$packages} ) { my $args = $EXPORT_SPEC{$package} or die "The $package package does not use Moose::Exporter\n"; + $current_meta_lookup = $meta_lookup_override || $args->{meta_lookup}; + $meta_lookup_override = $current_meta_lookup; + + my $meta_lookup = $current_meta_lookup + || sub { Class::MOP::class_of(shift) }; + for my $name ( @{ $args->{with_meta} } ) { my $sub = $class->_sub_from_package( $package, $name ) or next; @@ -150,7 +224,8 @@ sub _make_sub_exporter_params { $fq_name, $sub, $export_recorder, - ); + $meta_lookup, + ) unless exists $exports{$name}; } for my $name ( @{ $args->{with_caller} } ) { @@ -163,10 +238,13 @@ sub _make_sub_exporter_params { $fq_name, $sub, $export_recorder, - ); + ) unless exists $exports{$name}; } - for my $name ( @{ $args->{as_is} } ) { + my @extra_exports = $class->_parse_trait_aliases( + $package, $args->{trait_aliases}, + ); + for my $name ( @{ $args->{as_is} }, @extra_exports ) { my ( $sub, $coderef_name ); if ( ref $name ) { @@ -189,7 +267,8 @@ sub _make_sub_exporter_params { $export_recorder->{$sub} = 1; - $exports{$coderef_name} = sub {$sub}; + $exports{$coderef_name} = sub { $sub } + unless exists $exports{$coderef_name}; } } @@ -245,13 +324,14 @@ sub _make_wrapped_sub_with_meta { my $fq_name = shift; my $sub = shift; my $export_recorder = shift; + my $meta_lookup = shift; return sub { my $caller = $CALLER; my $wrapper = $self->_late_curry_wrapper( $sub, $fq_name, - sub { Class::MOP::class_of(shift) } => $caller + $meta_lookup => $caller ); my $sub = subname( $fq_name => $wrapper ); @@ -306,7 +386,8 @@ sub _make_import_sub { my $exporting_package = shift; my $exporter = shift; my $exports_from = shift; - my $is_reexport = shift; + my $is_reexport = shift; + my $meta_lookup = shift; return sub { @@ -327,6 +408,9 @@ sub _make_import_sub { = Moose::Util::resolve_metaclass_alias( 'Class' => $metaclass ) if defined $metaclass && length $metaclass; + my $meta_name; + ( $meta_name, @_ ) = _strip_meta_name(@_); + # Normally we could look at $_[0], but in some weird cases # (involving goto &Moose::import), $_[0] ends as something # else (like Squirrel). @@ -350,7 +434,11 @@ sub _make_import_sub { # 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 ); + $c->init_meta( + for_class => $CALLER, + metaclass => $metaclass, + meta_name => $meta_name, + ); $did_init_meta = 1; } @@ -360,7 +448,7 @@ sub _make_import_sub { # Moose::Exporter, which in turn sets $CALLER, so we need # to protect against that. local $CALLER = $CALLER; - _apply_meta_traits( $CALLER, $traits ); + _apply_meta_traits( $CALLER, $traits, $meta_lookup ); } elsif ( @{$traits} ) { require Moose; @@ -379,17 +467,11 @@ sub _make_import_sub { } $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' } @_; + my $idx = first_index { ( $_ || '' ) eq '-traits' } @_; return ( [], @_ ) unless $idx >= 0 && $#_ >= $idx + 1; @@ -403,7 +485,7 @@ sub _strip_traits { } sub _strip_metaclass { - my $idx = first_index { $_ eq '-metaclass' } @_; + my $idx = first_index { ( $_ || '' ) eq '-metaclass' } @_; return ( undef, @_ ) unless $idx >= 0 && $#_ >= $idx + 1; @@ -414,12 +496,24 @@ sub _strip_metaclass { return ( $metaclass, @_ ); } +sub _strip_meta_name { + my $idx = first_index { ( $_ || '' ) eq '-meta_name' } @_; + + return ( 'meta', @_ ) unless $idx >= 0 && $#_ >= $idx + 1; + + my $meta_name = $_[ $idx + 1 ]; + + splice @_, $idx, 2; + + return ( $meta_name, @_ ); +} + sub _apply_meta_traits { - my ( $class, $traits ) = @_; + my ( $class, $traits, $meta_lookup ) = @_; return unless @{$traits}; - my $meta = Class::MOP::class_of($class); + my $meta = $meta_lookup->($class); my $type = ( split /::/, ref $meta )[-1] or Moose->throw_error( @@ -434,10 +528,16 @@ sub _apply_meta_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 { @@ -458,7 +558,8 @@ sub _make_unimport_sub { my $exporting_package = shift; my $exports = shift; my $export_recorder = shift; - my $is_reexport = shift; + my $is_reexport = shift; + my $meta_lookup = shift; return sub { my $caller = scalar caller(); @@ -476,7 +577,7 @@ sub _remove_keywords { my $package = shift; my $keywords = shift; my $recorded_exports = shift; - my $is_reexport = shift; + my $is_reexport = shift; no strict 'refs'; @@ -502,13 +603,15 @@ sub _remove_keywords { sub _make_init_meta { shift; - my $class = shift; - my $args = shift; + my $class = shift; + my $args = shift; + my $meta_lookup = shift; - my %metaclass_roles; + my %old_style_roles; for my $role ( map {"${_}_roles"} - qw(metaclass + qw( + metaclass attribute_metaclass method_metaclass wrapped_method_metaclass @@ -516,38 +619,43 @@ 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; my %options = @_; - return unless Class::MOP::class_of( $options{for_class} ); + return unless $meta_lookup->( $options{for_class} ); - Moose::Util::MetaRole::apply_metaclass_roles( - for_class => $options{for_class}, - %metaclass_roles, - ); + if ( %new_style_roles || %old_style_roles ) { + Moose::Util::MetaRole::apply_metaroles( + for => $options{for_class}, + %new_style_roles, + %old_style_roles, + ); + } Moose::Util::MetaRole::apply_base_class_roles( for_class => $options{for_class}, %base_class_roles, ) - if Class::MOP::class_of( $options{for_class} ) + if $meta_lookup->( $options{for_class} ) ->isa('Moose::Meta::Class'); - return Class::MOP::class_of( $options{for_class} ); + return $meta_lookup->( $options{for_class} ); }; } @@ -558,11 +666,9 @@ sub import { 1; -__END__ - -=head1 NAME +# ABSTRACT: make an import() and unimport() just like Moose.pm -Moose::Exporter - make an import() and unimport() just like Moose.pm +__END__ =head1 SYNOPSIS @@ -634,7 +740,7 @@ 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 -C will call C and +C will call C and C as needed. Note that if any of these methods already exist, they will not be @@ -664,6 +770,14 @@ removed by the C method. The reason for this is we cannot know if the caller I explicitly imported the sub themselves, and therefore wants to keep it. +=item * trait_aliases => [ ... ] + +This is a list of package names which should have shortened aliases exported, +similar to the functionality of L. Each element in the list can be +either a package name, in which case the export will be named as the last +namespace component of the package, or an arrayref, whose first element is the +package to alias to, and second element is the alias to export. + =item * also => $name or \@names This is a list of modules which contain functions that the caller @@ -676,11 +790,28 @@ can selectively override functions exported by another module. C also makes sure all these functions get removed when C is called. +=item * meta_lookup => sub { ... } + +This is a function which will be called to provide the metaclass +to be operated upon by the exporter. This is an advanced feature +intended for use by package generator modules in the vein of +L in order to simplify reusing sugar +from other modules that use C. This function is +used, for example, to select the metaclass to bind to functions +that are exported using the C option. + +This function will receive one parameter: the class name into which +the sugar is being exported. The default implementation is: + + sub { Class::MOP::class_of(shift) } + +Accordingly, this function is expected to return a metaclass. + =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_class_roles". =item B<< Moose::Exporter->build_import_methods(...) >> @@ -694,6 +825,10 @@ C, and C. Calling C is equivalent to calling C with C<< install => [qw(import unimport init_meta)] >> except that it doesn't also return the methods. +The C method is built using L. This means that it can +take a hashref of the form C<< { into => $package } >> to specify the package +it operates on. + Used by C. =back @@ -721,29 +856,32 @@ Keep in mind that C will return an C method for you, which you can also call from within your custom C: - my ( $import, $unimport, $init_meta ) = - Moose::Exporter->build_import_methods( ... ); + my ( $import, $unimport, $init_meta ) + = Moose::Exporter->build_import_methods(...); sub import { - my $class = shift; + my $class = shift; - ... + ... - $class->$import(...); + # You can either pass an explicit package to import into ... + $class->$import( { into => scalar(caller) }, ... ); - ... + ...; } + # ... or you can use 'goto' to provide the correct caller info to the + # generated method sub unimport { goto &$unimport } sub init_meta { - my $class = shift; + my $class = shift; - ... + ... - $class->$init_meta(...); + $class->$init_meta(...); - ... + ... } =head1 METACLASS TRAITS @@ -760,20 +898,8 @@ 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 AUTHOR - -Dave Rolsky Eautarch@urth.orgE - -This is largely a reworking of code in Moose.pm originally written by -Stevan Little and others. - -=head1 COPYRIGHT AND LICENSE - -Copyright 2009 by Infinity Interactive, Inc. - -L +=head1 BUGS -This library is free software; you can redistribute it and/or modify -it under the same terms as Perl itself. +See L for details on reporting bugs. =cut