Reimplemented metaclass traits with Moose::Exporter. This
[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
5b5187e0 204 _apply_meta_traits( $CALLER, $traits )
205 if $did_init_meta;
206
e05b7c8e 207 goto $exporter;
208 };
209 }
1a601f52 210}
211
5b5187e0 212sub _strip_traits {
213 my $idx = first_index { $_ eq '-traits' } @_;
214
215 return ( undef, @_ ) unless $idx >= 0 && $#_ >= $idx + 1;
216
217 my $traits = $_[ $idx + 1 ];
218
219 splice @_, $idx, 2;
220
221 return ( $traits, @_ );
222}
223
224sub _apply_meta_traits {
225 my ( $class, $traits ) = @_;
226
227 return
228 unless $traits && @$traits;
229
230 my $meta = $class->meta();
231
232 my $type = ( split /::/, ref $meta )[-1]
233 or confess
234 'Cannot determine metaclass type for trait application . Meta isa '
235 . ref $meta;
236
237 # We can only call does_role() on Moose::Meta::Class objects, and
238 # we can only do that on $meta->meta() if it has already had at
239 # least one trait applied to it. By default $meta->meta() returns
240 # a Class::MOP::Class object (not a Moose::Meta::Class).
241 my @traits = grep {
242 $meta->meta()->can('does_role')
243 ? not $meta->meta()->does_role($_)
244 : 1
245 }
246 map { Moose::Util::resolve_metatrait_alias( $type => $_ ) } @$traits;
247
248 return unless @traits;
249
250 Moose::Util::apply_all_roles_with_method( $meta,
251 'apply_to_metaclass_instance', \@traits );
252}
253
1a601f52 254sub _get_caller {
255 # 1 extra level because it's called by import so there's a layer
256 # of indirection
257 my $offset = 1;
258
259 return
260 ( ref $_[1] && defined $_[1]->{into} ) ? $_[1]->{into}
261 : ( ref $_[1] && defined $_[1]->{into_level} )
262 ? caller( $offset + $_[1]->{into_level} )
263 : caller($offset);
264}
265
266sub _make_unimport_sub {
b30e7781 267 shift;
268 my $sources = shift;
269 my $keywords = shift;
1a601f52 270
1a601f52 271 return sub {
b30e7781 272 my $class = shift;
2c9c8797 273 my $caller = scalar caller();
b30e7781 274 Moose::Exporter->_remove_keywords(
275 $caller,
276 [ $class, @{$sources} ],
277 $keywords
278 );
1a601f52 279 };
280}
281
2c9c8797 282sub _remove_keywords {
283 shift;
284 my $package = shift;
b30e7781 285 my $sources = shift;
2c9c8797 286 my $keywords = shift;
287
b30e7781 288 my %sources = map { $_ => 1 } @{$sources};
289
2c9c8797 290 no strict 'refs';
291
292 # loop through the keywords ...
293 foreach my $name ( @{$keywords} ) {
294
295 # if we find one ...
296 if ( defined &{ $package . '::' . $name } ) {
297 my $keyword = \&{ $package . '::' . $name };
298
299 # make sure it is from us
300 my ($pkg_name) = Class::MOP::get_code_info($keyword);
b30e7781 301 next unless $sources{$pkg_name};
2c9c8797 302
303 # and if it is from us, then undef the slot
304 delete ${ $package . '::' }{$name};
305 }
306 }
307}
308
5bd4db9b 3091;
2f29843c 310
311__END__
312
313=head1 NAME
314
315Moose::Exporter - make an import() and unimport() just like Moose.pm
316
317=head1 SYNOPSIS
318
319 package MyApp::Moose;
320
321 use strict;
322 use warnings;
323
324 use Moose ();
325 use Moose::Exporter;
326
327 Moose::Exporter->build_export_methods(
328 export => [ 'sugar1', 'sugar2', \&Some::Random::thing ],
329 init_meta_args => { metaclass_class => 'MyApp::Meta::Class' ],
330 );
331
332 # then later ...
333 package MyApp::User;
334
335 use MyApp::Moose;
336
337 has 'name';
338 sugar1 'do your thing';
339 thing;
340
341 no MyApp::Moose;
342
343=head1 DESCRIPTION
344
345This module encapsulates the logic to export sugar functions like
346C<Moose.pm>. It does this by building custom C<import> and C<unimport>
347methods for your module, based on a spec your provide.
348
349It also lets your "stack" Moose-alike modules so you can export
350Moose's sugar as well as your own, along with sugar from any random
351C<MooseX> module, as long as they all use C<Moose::Exporter>.
352
353=head1 METHODS
354
355This module provides exactly one public method:
356
357=head2 Moose::Exporter->build_import_methods(...)
358
359When you call this method, C<Moose::Exporter> build custom C<import>
360and C<unimport> methods for your module. The import method will export
361the functions you specify, and you can also tell it to export
362functions exported by some other module (like C<Moose.pm>).
363
364The C<unimport> method cleans the callers namespace of all the
365exported functions.
366
367This method accepts the following parameters:
368
369=over 4
370
97a93056 371=item * with_caller => [ ... ]
372
373This a list of function I<names only> to be exported wrapped and then
374exported. The wrapper will pass the name of the calling package as the
375first argument to the function. Many sugar functions need to know
376their caller so they can get the calling package's metaclass object.
377
378=item * as_is => [ ... ]
2f29843c 379
380This a list of function names or sub references to be exported
381as-is. You can identify a subroutine by reference, which is handy to
382re-export some other module's functions directly by reference
383(C<\&Some::Package::function>).
384
42ade269 385=item * also => $name or \@names
2f29843c 386
42ade269 387This is a list of modules which contain functions that the caller
388wants to export. These modules must also use C<Moose::Exporter>. The
389most common use case will be to export the functions from C<Moose.pm>.
390
391C<Moose::Exporter> also makes sure all these functions get removed
392when C<unimport> is called.
2f29843c 393
394=back
395
42ade269 396=head1 IMPORTING AND init_meta
397
398If you want to set an alternative base object class or metaclass
399class, simply define an C<init_meta> method in your class. The
400C<import> method that C<Moose::Exporter> generates for you will call
401this method (if it exists). It will always pass the caller to this
402method via the C<for_class> parameter.
403
404Most of the time, your C<init_meta> method will probably just call C<<
405Moose->init_meta >> to do the real work:
406
407 sub init_meta {
408 shift; # our class name
409 return Moose->init_meta( @_, metaclass => 'My::Metaclass' );
410 }
411
2f29843c 412=head1 AUTHOR
413
414Dave Rolsky E<lt>autarch@urth.orgE<gt>
415
416This is largely a reworking of code in Moose.pm originally written by
417Stevan Little and others.
418
419=head1 COPYRIGHT AND LICENSE
420
421Copyright 2008 by Infinity Interactive, Inc.
422
423L<http://www.iinteractive.com>
424
425This library is free software; you can redistribute it and/or modify
426it under the same terms as Perl itself.
427
428=cut