fixed a method exclusion/aliasing bug
[gitmo/Moose.git] / lib / Moose / Exporter.pm
CommitLineData
e606ae5f 1package Moose::Exporter;
2
3use strict;
4use warnings;
5
ae18d5ec 6our $VERSION = '0.61';
7$VERSION = eval $VERSION;
8our $AUTHORITY = 'cpan:STEVAN';
9
e606ae5f 10use Class::MOP;
11use List::MoreUtils qw( first_index uniq );
12use Moose::Util::MetaRole;
13use Sub::Exporter;
14
15
16my %EXPORT_SPEC;
17
18sub setup_import_methods {
19 my ( $class, %args ) = @_;
20
21 my $exporting_package = $args{exporting_package} ||= caller();
22
23 my ( $import, $unimport ) = $class->build_import_methods(%args);
24
25 no strict 'refs';
26 *{ $exporting_package . '::import' } = $import;
27 *{ $exporting_package . '::unimport' } = $unimport;
28}
29
30sub build_import_methods {
31 my ( $class, %args ) = @_;
32
33 my $exporting_package = $args{exporting_package} ||= caller();
34
35 $EXPORT_SPEC{$exporting_package} = \%args;
36
37 my @exports_from = $class->_follow_also( $exporting_package );
38
39 my $export_recorder = {};
40
e05fb8ae 41 my ( $exports, $is_removable )
42 = $class->_make_sub_exporter_params(
e606ae5f 43 [ $exporting_package, @exports_from ], $export_recorder );
44
45 my $exporter = Sub::Exporter::build_exporter(
46 {
47 exports => $exports,
48 groups => { default => [':all'] }
49 }
50 );
51
52 # $args{_export_to_main} exists for backwards compat, because
53 # Moose::Util::TypeConstraints did export to main (unlike Moose &
54 # Moose::Role).
55 my $import = $class->_make_import_sub( $exporting_package, $exporter,
56 \@exports_from, $args{_export_to_main} );
57
58 my $unimport = $class->_make_unimport_sub( $exporting_package, $exports,
e05fb8ae 59 $is_removable, $export_recorder );
e606ae5f 60
61 return ( $import, $unimport )
62}
63
64{
65 my $seen = {};
66
67 sub _follow_also {
68 my $class = shift;
69 my $exporting_package = shift;
70
71 local %$seen = ( $exporting_package => 1 );
72
73 return uniq( _follow_also_real($exporting_package) );
74 }
75
76 sub _follow_also_real {
77 my $exporting_package = shift;
78
f5d03831 79 die "Package in also ($exporting_package) does not seem to use Moose::Exporter"
e606ae5f 80 unless exists $EXPORT_SPEC{$exporting_package};
81
82 my $also = $EXPORT_SPEC{$exporting_package}{also};
83
84 return unless defined $also;
85
86 my @also = ref $also ? @{$also} : $also;
87
88 for my $package (@also)
89 {
f5d03831 90 die "Circular reference in also parameter to Moose::Exporter between $exporting_package and $package"
e606ae5f 91 if $seen->{$package};
92
93 $seen->{$package} = 1;
94 }
95
96 return @also, map { _follow_also_real($_) } @also;
97 }
98}
99
100sub _make_sub_exporter_params {
101 my $class = shift;
102 my $packages = shift;
103 my $export_recorder = shift;
104
105 my %exports;
e05fb8ae 106 my %is_removable;
e606ae5f 107
108 for my $package ( @{$packages} ) {
109 my $args = $EXPORT_SPEC{$package}
110 or die "The $package package does not use Moose::Exporter\n";
111
112 for my $name ( @{ $args->{with_caller} } ) {
113 my $sub = do {
114 no strict 'refs';
115 \&{ $package . '::' . $name };
116 };
117
118 my $fq_name = $package . '::' . $name;
119
120 $exports{$name} = $class->_make_wrapped_sub(
121 $fq_name,
122 $sub,
123 $export_recorder,
124 );
e05fb8ae 125
126 $is_removable{$name} = 1;
e606ae5f 127 }
128
129 for my $name ( @{ $args->{as_is} } ) {
130 my $sub;
131
132 if ( ref $name ) {
133 $sub = $name;
e05fb8ae 134
135 # Even though Moose re-exports things from Carp &
136 # Scalar::Util, we don't want to remove those at
137 # unimport time, because the importing package may
138 # have imported them explicitly ala
139 #
140 # use Carp qw( confess );
141 #
142 # This is a hack. Since we can't know whether they
143 # really want to keep these subs or not, we err on the
144 # safe side and leave them in.
145 my $coderef_pkg;
146 ( $coderef_pkg, $name ) = Class::MOP::get_code_info($name);
147
148 $is_removable{$name} = $coderef_pkg eq $package ? 1 : 0;
e606ae5f 149 }
150 else {
151 $sub = do {
152 no strict 'refs';
153 \&{ $package . '::' . $name };
154 };
e05fb8ae 155
156 $is_removable{$name} = 1;
e606ae5f 157 }
158
159 $export_recorder->{$sub} = 1;
160
161 $exports{$name} = sub {$sub};
162 }
163 }
164
e05fb8ae 165 return ( \%exports, \%is_removable );
e606ae5f 166}
167
96bb13ea 168our $CALLER;
169
170sub _make_wrapped_sub {
171 shift;
172 my $fq_name = shift;
173 my $sub = shift;
174 my $export_recorder = shift;
175
176 # We need to set the package at import time, so that when
177 # package Foo imports has(), we capture "Foo" as the
178 # package. This lets other packages call Foo::has() and get
179 # the right package. This is done for backwards compatibility
180 # with existing production code, not because this is a good
181 # idea ;)
182 return sub {
183 my $caller = $CALLER;
184
185 my $sub
186 = Class::MOP::subname( $fq_name => sub { $sub->( $caller, @_ ) } );
187
188 $export_recorder->{$sub} = 1;
189
190 return $sub;
191 };
192}
e606ae5f 193
96bb13ea 194sub _make_import_sub {
195 shift;
196 my $exporting_package = shift;
197 my $exporter = shift;
198 my $exports_from = shift;
199 my $export_to_main = shift;
200
201 return sub {
202
203 # I think we could use Sub::Exporter's collector feature
204 # to do this, but that would be rather gross, since that
205 # feature isn't really designed to return a value to the
206 # caller of the exporter sub.
207 #
208 # Also, this makes sure we preserve backwards compat for
209 # _get_caller, so it always sees the arguments in the
210 # expected order.
211 my $traits;
212 ( $traits, @_ ) = _strip_traits(@_);
213
214 # Normally we could look at $_[0], but in some weird cases
215 # (involving goto &Moose::import), $_[0] ends as something
216 # else (like Squirrel).
217 my $class = $exporting_package;
218
219 $CALLER = _get_caller(@_);
220
221 # this works because both pragmas set $^H (see perldoc
222 # perlvar) which affects the current compilation -
223 # i.e. the file who use'd us - which is why we don't need
224 # to do anything special to make it affect that file
225 # rather than this one (which is already compiled)
226
227 strict->import;
228 warnings->import;
229
230 # we should never export to main
231 if ( $CALLER eq 'main' && !$export_to_main ) {
232 warn
233 qq{$class does not export its sugar to the 'main' package.\n};
234 return;
235 }
e606ae5f 236
96bb13ea 237 my $did_init_meta;
238 for my $c ( grep { $_->can('init_meta') } $class, @{$exports_from} ) {
239 $c->init_meta( for_class => $CALLER );
240 $did_init_meta = 1;
241 }
e606ae5f 242
96bb13ea 243 if ( $did_init_meta && @{$traits} ) {
244 # The traits will use Moose::Role, which in turn uses
245 # Moose::Exporter, which in turn sets $CALLER, so we need
246 # to protect against that.
247 local $CALLER = $CALLER;
248 _apply_meta_traits( $CALLER, $traits );
249 }
250 elsif ( @{$traits} ) {
251 Moose->throw_error(
252 "Cannot provide traits when $class does not have an init_meta() method"
253 );
254 }
e606ae5f 255
96bb13ea 256 goto $exporter;
257 };
e606ae5f 258}
259
96bb13ea 260
e606ae5f 261sub _strip_traits {
262 my $idx = first_index { $_ eq '-traits' } @_;
263
264 return ( [], @_ ) unless $idx >= 0 && $#_ >= $idx + 1;
265
266 my $traits = $_[ $idx + 1 ];
267
268 splice @_, $idx, 2;
269
270 $traits = [ $traits ] unless ref $traits;
271
272 return ( $traits, @_ );
273}
274
275sub _apply_meta_traits {
276 my ( $class, $traits ) = @_;
277
278 return unless @{$traits};
279
280 my $meta = $class->meta();
281
282 my $type = ( split /::/, ref $meta )[-1]
c245d69b 283 or Moose->throw_error(
e606ae5f 284 'Cannot determine metaclass type for trait application . Meta isa '
4c0b3599 285 . ref $meta );
e606ae5f 286
287 my @resolved_traits
288 = map { Moose::Util::resolve_metatrait_alias( $type => $_ ) }
289 @$traits;
290
291 return unless @resolved_traits;
292
293 Moose::Util::MetaRole::apply_metaclass_roles(
294 for_class => $class,
295 metaclass_roles => \@resolved_traits,
296 );
297}
298
299sub _get_caller {
300 # 1 extra level because it's called by import so there's a layer
301 # of indirection
302 my $offset = 1;
303
304 return
305 ( ref $_[1] && defined $_[1]->{into} ) ? $_[1]->{into}
306 : ( ref $_[1] && defined $_[1]->{into_level} )
307 ? caller( $offset + $_[1]->{into_level} )
308 : caller($offset);
309}
310
311sub _make_unimport_sub {
312 shift;
313 my $exporting_package = shift;
314 my $exports = shift;
e05fb8ae 315 my $is_removable = shift;
e606ae5f 316 my $export_recorder = shift;
317
318 return sub {
319 my $caller = scalar caller();
320 Moose::Exporter->_remove_keywords(
321 $caller,
322 [ keys %{$exports} ],
e05fb8ae 323 $is_removable,
e606ae5f 324 $export_recorder,
325 );
326 };
327}
328
329sub _remove_keywords {
330 shift;
331 my $package = shift;
332 my $keywords = shift;
e05fb8ae 333 my $is_removable = shift;
e606ae5f 334 my $recorded_exports = shift;
335
336 no strict 'refs';
337
338 foreach my $name ( @{ $keywords } ) {
e05fb8ae 339 next unless $is_removable->{$name};
e606ae5f 340
341 if ( defined &{ $package . '::' . $name } ) {
342 my $sub = \&{ $package . '::' . $name };
343
344 # make sure it is from us
345 next unless $recorded_exports->{$sub};
346
347 # and if it is from us, then undef the slot
348 delete ${ $package . '::' }{$name};
349 }
350 }
351}
352
3531;
354
355__END__
356
357=head1 NAME
358
359Moose::Exporter - make an import() and unimport() just like Moose.pm
360
361=head1 SYNOPSIS
362
363 package MyApp::Moose;
364
365 use strict;
366 use warnings;
367
368 use Moose ();
369 use Moose::Exporter;
370
371 Moose::Exporter->setup_import_methods(
82ad7804 372 with_caller => [ 'has_rw', 'sugar2' ],
e606ae5f 373 as_is => [ 'sugar3', \&Some::Random::thing ],
374 also => 'Moose',
375 );
376
82ad7804 377 sub has_rw {
6daad0b9 378 my ($caller, $name, %options) = @_;
82ad7804 379 Class::MOP::Class->initialize($caller)->add_attribute($name,
380 is => 'rw',
381 %options,
382 );
383 }
384
e606ae5f 385 # then later ...
386 package MyApp::User;
387
388 use MyApp::Moose;
389
390 has 'name';
6daad0b9 391 has_rw 'size';
e606ae5f 392 thing;
393
394 no MyApp::Moose;
395
396=head1 DESCRIPTION
397
398This module encapsulates the logic to export sugar functions like
399C<Moose.pm>. It does this by building custom C<import> and C<unimport>
400methods for your module, based on a spec your provide.
401
402It also lets your "stack" Moose-alike modules so you can export
403Moose's sugar as well as your own, along with sugar from any random
404C<MooseX> module, as long as they all use C<Moose::Exporter>.
405
406=head1 METHODS
407
408This module provides two public methods:
409
410=head2 Moose::Exporter->setup_import_methods(...)
411
412When you call this method, C<Moose::Exporter> build custom C<import>
413and C<unimport> methods for your module. The import method will export
414the functions you specify, and you can also tell it to export
415functions exported by some other module (like C<Moose.pm>).
416
417The C<unimport> method cleans the callers namespace of all the
418exported functions.
419
420This method accepts the following parameters:
421
422=over 4
423
424=item * with_caller => [ ... ]
425
426This a list of function I<names only> to be exported wrapped and then
427exported. The wrapper will pass the name of the calling package as the
428first argument to the function. Many sugar functions need to know
429their caller so they can get the calling package's metaclass object.
430
431=item * as_is => [ ... ]
432
433This a list of function names or sub references to be exported
434as-is. You can identify a subroutine by reference, which is handy to
435re-export some other module's functions directly by reference
436(C<\&Some::Package::function>).
437
e05fb8ae 438If you do export some other packages function, this function will
439never be removed by the C<unimport> method. The reason for this is we
440cannot know if the caller I<also> explicitly imported the sub
441themselves, and therefore wants to keep it.
442
e606ae5f 443=item * also => $name or \@names
444
445This is a list of modules which contain functions that the caller
446wants to export. These modules must also use C<Moose::Exporter>. The
447most common use case will be to export the functions from C<Moose.pm>.
448
449C<Moose::Exporter> also makes sure all these functions get removed
450when C<unimport> is called.
451
452=back
453
454=head2 Moose::Exporter->build_import_methods(...)
455
456Returns two code refs, one for import and one for unimport.
457
458Used by C<setup_import_methods>.
459
460=head1 IMPORTING AND init_meta
461
462If you want to set an alternative base object class or metaclass
463class, simply define an C<init_meta> method in your class. The
464C<import> method that C<Moose::Exporter> generates for you will call
465this method (if it exists). It will always pass the caller to this
466method via the C<for_class> parameter.
467
468Most of the time, your C<init_meta> method will probably just call C<<
469Moose->init_meta >> to do the real work:
470
471 sub init_meta {
472 shift; # our class name
473 return Moose->init_meta( @_, metaclass => 'My::Metaclass' );
474 }
475
476=head1 METACLASS TRAITS
477
478The C<import> method generated by C<Moose::Exporter> will allow the
479user of your module to specify metaclass traits in a C<-traits>
480parameter passed as part of the import:
481
482 use Moose -traits => 'My::Meta::Trait';
483
484 use Moose -traits => [ 'My::Meta::Trait', 'My::Other::Trait' ];
485
486These traits will be applied to the caller's metaclass
487instance. Providing traits for an exporting class that does not create
488a metaclass for the caller is an error.
489
490=head1 AUTHOR
491
492Dave Rolsky E<lt>autarch@urth.orgE<gt>
493
494This is largely a reworking of code in Moose.pm originally written by
495Stevan Little and others.
496
497=head1 COPYRIGHT AND LICENSE
498
499Copyright 2008 by Infinity Interactive, Inc.
500
501L<http://www.iinteractive.com>
502
503This library is free software; you can redistribute it and/or modify
504it under the same terms as Perl itself.
505
506=cut