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