use strict;
use warnings;
-our $VERSION = '1.10';
-our $XS_VERSION = $VERSION;
-$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::Deprecated;
use Moose::Util::MetaRole;
+use Scalar::Util qw(reftype);
use Sub::Exporter 0.980;
use Sub::Name qw(subname);
-use XSLoader;
-
-XSLoader::load( 'Moose', $XS_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,
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);
[ @exports_from, $exporting_package ],
$export_recorder,
$is_reexport,
+ $meta_lookup,
);
- my $exporter = Sub::Exporter::build_exporter(
- {
- exports => $exports,
- groups => { default => [':all'] }
- }
+ my $exporter = $class->_make_exporter(
+ $exports,
+ $is_reexport,
+ $meta_lookup,
);
my %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);
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 = {};
local %$seen = ( $exporting_package => 1 );
- return uniq( _follow_also_real($exporting_package) );
+ return reverse uniq( _follow_also_real($exporting_package) );
}
sub _follow_also_real {
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"
}
}
+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 $is_reexport = shift;
+ my $meta_lookup = shift;
my %exports;
$fq_name,
$sub,
$export_recorder,
+ $meta_lookup,
);
}
);
}
- 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 ) {
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 );
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 {
= 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).
# 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;
}
# 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;
}
$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;
}
sub _strip_metaclass {
- my $idx = first_index { $_ eq '-metaclass' } @_;
+ my $idx = first_index { ( $_ || '' ) eq '-metaclass' } @_;
return ( undef, @_ ) unless $idx >= 0 && $#_ >= $idx + 1;
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(
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();
my $package = shift;
my $keywords = shift;
my $recorded_exports = shift;
- my $is_reexport = shift;
+ my $is_reexport = shift;
no strict 'refs';
sub _make_init_meta {
shift;
- my $class = shift;
- my $args = shift;
+ my $class = shift;
+ my $args = shift;
+ my $meta_lookup = shift;
my %old_style_roles;
for my $role (
shift;
my %options = @_;
- return unless Class::MOP::class_of( $options{for_class} );
+ return unless $meta_lookup->( $options{for_class} );
if ( %new_style_roles || %old_style_roles ) {
Moose::Util::MetaRole::apply_metaroles(
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} );
};
}
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
the caller I<also> 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<aliased>. 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
C<Moose::Exporter> also makes sure all these functions get removed
when C<unimport> 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<MooseX::Role::Parameterized> in order to simplify reusing sugar
+from other modules that use C<Moose::Exporter>. This function is
+used, for example, to select the metaclass to bind to functions
+that are exported using the C<with_meta> 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
You can also provide parameters for C<Moose::Util::MetaRole::apply_metaroles>
to calling C<build_import_methods> with C<< install => [qw(import unimport
init_meta)] >> except that it doesn't also return the methods.
+The C<import> method is built using L<Sub::Exporter>. This means that it can
+take a hashref of the form C<< { into => $package } >> to specify the package
+it operates on.
+
Used by C<setup_import_methods>.
=back
method for you, which you can also call from within your custom
C<init_meta>:
- 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
See L<Moose/BUGS> for details on reporting bugs.
-=head1 AUTHOR
-
-Dave Rolsky E<lt>autarch@urth.orgE<gt>
-
-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<http://www.iinteractive.com>
-
-This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself.
-
=cut