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