Squashed commit of the following:
[gitmo/Moose.git] / lib / Moose / Exporter.pm
1 package Moose::Exporter;
2
3 use strict;
4 use warnings;
5
6 our $VERSION = '0.93';
7 $VERSION = eval $VERSION;
8 our $AUTHORITY = 'cpan:STEVAN';
9
10 use Class::MOP;
11 use List::MoreUtils qw( first_index uniq );
12 use Moose::Util::MetaRole;
13 use Sub::Exporter 0.980;
14 use Sub::Name qw(subname);
15
16 use XSLoader;
17
18 XSLoader::load( 'Moose', $VERSION );
19
20 my %EXPORT_SPEC;
21
22 sub setup_import_methods {
23     my ( $class, %args ) = @_;
24
25     my $exporting_package = $args{exporting_package} ||= caller();
26
27     $class->build_import_methods(
28         %args,
29         install => [qw(import unimport init_meta)]
30     );
31 }
32
33 sub build_import_methods {
34     my ( $class, %args ) = @_;
35
36     my $exporting_package = $args{exporting_package} ||= caller();
37
38     $EXPORT_SPEC{$exporting_package} = \%args;
39
40     my @exports_from = $class->_follow_also($exporting_package);
41
42     my $export_recorder = {};
43     my $is_reexport     = {};
44
45     my $exports = $class->_make_sub_exporter_params(
46         [ @exports_from, $exporting_package ],
47         $export_recorder,
48         $is_reexport,
49     );
50
51     my $exporter = Sub::Exporter::build_exporter(
52         {
53             exports => $exports,
54             groups  => { default => [':all'] }
55         }
56     );
57
58     my %methods;
59     $methods{import} = $class->_make_import_sub(
60         $exporting_package,
61         $exporter,
62         \@exports_from,
63         $is_reexport
64     );
65
66     $methods{unimport} = $class->_make_unimport_sub(
67         $exporting_package,
68         $exports,
69         $export_recorder,
70         $is_reexport
71     );
72
73     $methods{init_meta} = $class->_make_init_meta(
74         $exporting_package,
75         \%args
76     );
77
78     my $package = Class::MOP::Package->initialize($exporting_package);
79     for my $to_install ( @{ $args{install} || [] } ) {
80         my $symbol = '&' . $to_install;
81         next
82             unless $methods{$to_install}
83                 && !$package->has_package_symbol($symbol);
84         $package->add_package_symbol( $symbol, $methods{$to_install} );
85     }
86
87     return ( $methods{import}, $methods{unimport}, $methods{init_meta} );
88 }
89
90 {
91     my $seen = {};
92
93     sub _follow_also {
94         my $class             = shift;
95         my $exporting_package = shift;
96
97         local %$seen = ( $exporting_package => 1 );
98
99         return uniq( _follow_also_real($exporting_package) );
100     }
101
102     sub _follow_also_real {
103         my $exporting_package = shift;
104
105         if ( !exists $EXPORT_SPEC{$exporting_package} ) {
106             my $loaded = Class::MOP::is_class_loaded($exporting_package);
107
108             die "Package in also ($exporting_package) does not seem to "
109                 . "use Moose::Exporter"
110                 . ( $loaded ? "" : " (is it loaded?)" );
111         }
112
113         my $also = $EXPORT_SPEC{$exporting_package}{also};
114
115         return unless defined $also;
116
117         my @also = ref $also ? @{$also} : $also;
118
119         for my $package (@also) {
120             die
121                 "Circular reference in 'also' parameter to Moose::Exporter between $exporting_package and $package"
122                 if $seen->{$package};
123
124             $seen->{$package} = 1;
125         }
126
127         return @also, map { _follow_also_real($_) } @also;
128     }
129 }
130
131 sub _make_sub_exporter_params {
132     my $class           = shift;
133     my $packages        = shift;
134     my $export_recorder = shift;
135     my $is_reexport  = shift;
136
137     my %exports;
138
139     for my $package ( @{$packages} ) {
140         my $args = $EXPORT_SPEC{$package}
141             or die "The $package package does not use Moose::Exporter\n";
142
143         for my $name ( @{ $args->{with_meta} } ) {
144             my $sub = $class->_sub_from_package( $package, $name )
145                 or next;
146
147             my $fq_name = $package . '::' . $name;
148
149             $exports{$name} = $class->_make_wrapped_sub_with_meta(
150                 $fq_name,
151                 $sub,
152                 $export_recorder,
153             );
154         }
155
156         for my $name ( @{ $args->{with_caller} } ) {
157             my $sub = $class->_sub_from_package( $package, $name )
158                 or next;
159
160             my $fq_name = $package . '::' . $name;
161
162             $exports{$name} = $class->_make_wrapped_sub(
163                 $fq_name,
164                 $sub,
165                 $export_recorder,
166             );
167         }
168
169         for my $name ( @{ $args->{as_is} } ) {
170             my ( $sub, $coderef_name );
171
172             if ( ref $name ) {
173                 $sub = $name;
174
175                 my $coderef_pkg;
176                 ( $coderef_pkg, $coderef_name )
177                     = Class::MOP::get_code_info($name);
178
179                 if ( $coderef_pkg ne $package ) {
180                     $is_reexport->{$coderef_name} = 1;
181                 }
182             }
183             else {
184                 $sub = $class->_sub_from_package( $package, $name )
185                     or next;
186
187                 $coderef_name = $name;
188             }
189
190             $export_recorder->{$sub} = 1;
191
192             $exports{$coderef_name} = sub {$sub};
193         }
194     }
195
196     return \%exports;
197 }
198
199 sub _sub_from_package {
200     my $sclass  = shift;
201     my $package = shift;
202     my $name    = shift;
203
204     my $sub = do {
205         no strict 'refs';
206         \&{ $package . '::' . $name };
207     };
208
209     return $sub if defined &$sub;
210
211     Carp::cluck "Trying to export undefined sub ${package}::${name}";
212
213     return;
214 }
215
216 our $CALLER;
217
218 sub _make_wrapped_sub {
219     my $self            = shift;
220     my $fq_name         = shift;
221     my $sub             = shift;
222     my $export_recorder = shift;
223
224     # We need to set the package at import time, so that when
225     # package Foo imports has(), we capture "Foo" as the
226     # package. This lets other packages call Foo::has() and get
227     # the right package. This is done for backwards compatibility
228     # with existing production code, not because this is a good
229     # idea ;)
230     return sub {
231         my $caller = $CALLER;
232
233         my $wrapper = $self->_curry_wrapper( $sub, $fq_name, $caller );
234
235         my $sub = subname( $fq_name => $wrapper );
236
237         $export_recorder->{$sub} = 1;
238
239         return $sub;
240     };
241 }
242
243 sub _make_wrapped_sub_with_meta {
244     my $self            = shift;
245     my $fq_name         = shift;
246     my $sub             = shift;
247     my $export_recorder = shift;
248
249     return sub {
250         my $caller = $CALLER;
251
252         my $wrapper = $self->_late_curry_wrapper(
253             $sub, $fq_name,
254             sub { Class::MOP::class_of(shift) } => $caller
255         );
256
257         my $sub = subname( $fq_name => $wrapper );
258
259         $export_recorder->{$sub} = 1;
260
261         return $sub;
262     };
263 }
264
265 sub _curry_wrapper {
266     my $class   = shift;
267     my $sub     = shift;
268     my $fq_name = shift;
269     my @extra   = @_;
270
271     my $wrapper = sub { $sub->( @extra, @_ ) };
272     if ( my $proto = prototype $sub ) {
273
274         # XXX - Perl's prototype sucks. Use & to make set_prototype
275         # ignore the fact that we're passing "private variables"
276         &Scalar::Util::set_prototype( $wrapper, $proto );
277     }
278     return $wrapper;
279 }
280
281 sub _late_curry_wrapper {
282     my $class   = shift;
283     my $sub     = shift;
284     my $fq_name = shift;
285     my $extra   = shift;
286     my @ex_args = @_;
287
288     my $wrapper = sub {
289
290         # resolve curried arguments at runtime via this closure
291         my @curry = ( $extra->(@ex_args) );
292         return $sub->( @curry, @_ );
293     };
294
295     if ( my $proto = prototype $sub ) {
296
297         # XXX - Perl's prototype sucks. Use & to make set_prototype
298         # ignore the fact that we're passing "private variables"
299         &Scalar::Util::set_prototype( $wrapper, $proto );
300     }
301     return $wrapper;
302 }
303
304 sub _make_import_sub {
305     shift;
306     my $exporting_package = shift;
307     my $exporter          = shift;
308     my $exports_from      = shift;
309     my $is_reexport    = shift;
310
311     return sub {
312
313         # I think we could use Sub::Exporter's collector feature
314         # to do this, but that would be rather gross, since that
315         # feature isn't really designed to return a value to the
316         # caller of the exporter sub.
317         #
318         # Also, this makes sure we preserve backwards compat for
319         # _get_caller, so it always sees the arguments in the
320         # expected order.
321         my $traits;
322         ( $traits, @_ ) = _strip_traits(@_);
323
324         my $metaclass;
325         ( $metaclass, @_ ) = _strip_metaclass(@_);
326         $metaclass
327             = Moose::Util::resolve_metaclass_alias( 'Class' => $metaclass )
328             if defined $metaclass && length $metaclass;
329
330         # Normally we could look at $_[0], but in some weird cases
331         # (involving goto &Moose::import), $_[0] ends as something
332         # else (like Squirrel).
333         my $class = $exporting_package;
334
335         $CALLER = _get_caller(@_);
336
337         # this works because both pragmas set $^H (see perldoc
338         # perlvar) which affects the current compilation -
339         # i.e. the file who use'd us - which is why we don't need
340         # to do anything special to make it affect that file
341         # rather than this one (which is already compiled)
342
343         strict->import;
344         warnings->import;
345
346         my $did_init_meta;
347         for my $c ( grep { $_->can('init_meta') } $class, @{$exports_from} ) {
348
349             # init_meta can apply a role, which when loaded uses
350             # Moose::Exporter, which in turn sets $CALLER, so we need
351             # to protect against that.
352             local $CALLER = $CALLER;
353             $c->init_meta( for_class => $CALLER, metaclass => $metaclass );
354             $did_init_meta = 1;
355         }
356
357         if ( $did_init_meta && @{$traits} ) {
358
359             # The traits will use Moose::Role, which in turn uses
360             # Moose::Exporter, which in turn sets $CALLER, so we need
361             # to protect against that.
362             local $CALLER = $CALLER;
363             _apply_meta_traits( $CALLER, $traits );
364         }
365         elsif ( @{$traits} ) {
366             require Moose;
367             Moose->throw_error(
368                 "Cannot provide traits when $class does not have an init_meta() method"
369             );
370         }
371
372         my ( undef, @args ) = @_;
373         my $extra = shift @args if ref $args[0] eq 'HASH';
374
375         $extra ||= {};
376         if ( !$extra->{into} ) {
377             $extra->{into_level} ||= 0;
378             $extra->{into_level}++;
379         }
380
381         $class->$exporter( $extra, @args );
382
383         for my $name ( keys %{$is_reexport} ) {
384             no strict 'refs';
385             no warnings 'once';
386             _flag_as_reexport( \*{ join q{::}, $CALLER, $name } );
387         }
388     };
389 }
390
391 sub _strip_traits {
392     my $idx = first_index { $_ eq '-traits' } @_;
393
394     return ( [], @_ ) unless $idx >= 0 && $#_ >= $idx + 1;
395
396     my $traits = $_[ $idx + 1 ];
397
398     splice @_, $idx, 2;
399
400     $traits = [$traits] unless ref $traits;
401
402     return ( $traits, @_ );
403 }
404
405 sub _strip_metaclass {
406     my $idx = first_index { $_ eq '-metaclass' } @_;
407
408     return ( undef, @_ ) unless $idx >= 0 && $#_ >= $idx + 1;
409
410     my $metaclass = $_[ $idx + 1 ];
411
412     splice @_, $idx, 2;
413
414     return ( $metaclass, @_ );
415 }
416
417 sub _apply_meta_traits {
418     my ( $class, $traits ) = @_;
419
420     return unless @{$traits};
421
422     my $meta = Class::MOP::class_of($class);
423
424     my $type = ( split /::/, ref $meta )[-1]
425         or Moose->throw_error(
426         'Cannot determine metaclass type for trait application . Meta isa '
427             . ref $meta );
428
429     my @resolved_traits = map {
430         ref $_
431             ? $_
432             : Moose::Util::resolve_metatrait_alias( $type => $_ )
433     } @$traits;
434
435     return unless @resolved_traits;
436
437     Moose::Util::MetaRole::apply_metaclass_roles(
438         for_class       => $class,
439         metaclass_roles => \@resolved_traits,
440     );
441 }
442
443 sub _get_caller {
444
445     # 1 extra level because it's called by import so there's a layer
446     # of indirection
447     my $offset = 1;
448
449     return
450           ( ref $_[1] && defined $_[1]->{into} ) ? $_[1]->{into}
451         : ( ref $_[1] && defined $_[1]->{into_level} )
452         ? caller( $offset + $_[1]->{into_level} )
453         : caller($offset);
454 }
455
456 sub _make_unimport_sub {
457     shift;
458     my $exporting_package = shift;
459     my $exports           = shift;
460     my $export_recorder   = shift;
461     my $is_reexport    = shift;
462
463     return sub {
464         my $caller = scalar caller();
465         Moose::Exporter->_remove_keywords(
466             $caller,
467             [ keys %{$exports} ],
468             $export_recorder,
469             $is_reexport,
470         );
471     };
472 }
473
474 sub _remove_keywords {
475     shift;
476     my $package          = shift;
477     my $keywords         = shift;
478     my $recorded_exports = shift;
479     my $is_reexport   = shift;
480
481     no strict 'refs';
482
483     foreach my $name ( @{$keywords} ) {
484         if ( defined &{ $package . '::' . $name } ) {
485             my $sub = \&{ $package . '::' . $name };
486
487             # make sure it is from us
488             next unless $recorded_exports->{$sub};
489
490             if ( $is_reexport->{$name} ) {
491                 no strict 'refs';
492                 next
493                     unless _export_is_flagged(
494                             \*{ join q{::} => $package, $name } );
495             }
496
497             # and if it is from us, then undef the slot
498             delete ${ $package . '::' }{$name};
499         }
500     }
501 }
502
503 sub _make_init_meta {
504     shift;
505     my $class = shift;
506     my $args  = shift;
507
508     my %metaclass_roles;
509     for my $role (
510         map {"${_}_roles"}
511         qw(metaclass
512         attribute_metaclass
513         method_metaclass
514         wrapped_method_metaclass
515         instance_metaclass
516         constructor_class
517         destructor_class
518         error_class
519         application_to_class_class
520         application_to_role_class
521         application_to_instance_class)
522         ) {
523         $metaclass_roles{$role} = $args->{$role} if exists $args->{$role};
524     }
525
526     my %base_class_roles;
527     %base_class_roles = ( roles => $args->{base_class_roles} )
528         if exists $args->{base_class_roles};
529
530     return unless %metaclass_roles || %base_class_roles;
531
532     return sub {
533         shift;
534         my %options = @_;
535
536         return unless Class::MOP::class_of( $options{for_class} );
537
538         Moose::Util::MetaRole::apply_metaclass_roles(
539             for_class => $options{for_class},
540             %metaclass_roles,
541         );
542
543         Moose::Util::MetaRole::apply_base_class_roles(
544             for_class => $options{for_class},
545             %base_class_roles,
546             )
547             if Class::MOP::class_of( $options{for_class} )
548                 ->isa('Moose::Meta::Class');
549
550         return Class::MOP::class_of( $options{for_class} );
551     };
552 }
553
554 sub import {
555     strict->import;
556     warnings->import;
557 }
558
559 1;
560
561 __END__
562
563 =head1 NAME
564
565 Moose::Exporter - make an import() and unimport() just like Moose.pm
566
567 =head1 SYNOPSIS
568
569   package MyApp::Moose;
570
571   use Moose ();
572   use Moose::Exporter;
573
574   Moose::Exporter->setup_import_methods(
575       with_meta => [ 'has_rw', 'sugar2' ],
576       as_is     => [ 'sugar3', \&Some::Random::thing ],
577       also      => 'Moose',
578   );
579
580   sub has_rw {
581       my ( $meta, $name, %options ) = @_;
582       $meta->add_attribute(
583           $name,
584           is => 'rw',
585           %options,
586       );
587   }
588
589   # then later ...
590   package MyApp::User;
591
592   use MyApp::Moose;
593
594   has 'name';
595   has_rw 'size';
596   thing;
597
598   no MyApp::Moose;
599
600 =head1 DESCRIPTION
601
602 This module encapsulates the exporting of sugar functions in a
603 C<Moose.pm>-like manner. It does this by building custom C<import>,
604 C<unimport>, and C<init_meta> methods for your module, based on a spec you
605 provide.
606
607 It also lets you "stack" Moose-alike modules so you can export Moose's sugar
608 as well as your own, along with sugar from any random C<MooseX> module, as
609 long as they all use C<Moose::Exporter>. This feature exists to let you bundle
610 a set of MooseX modules into a policy module that developers can use directly
611 instead of using Moose itself.
612
613 To simplify writing exporter modules, C<Moose::Exporter> also imports
614 C<strict> and C<warnings> into your exporter module, as well as into
615 modules that use it.
616
617 =head1 METHODS
618
619 This module provides two public methods:
620
621 =over 4
622
623 =item  B<< Moose::Exporter->setup_import_methods(...) >>
624
625 When you call this method, C<Moose::Exporter> builds custom C<import>,
626 C<unimport>, and C<init_meta> methods for your module. The C<import> method
627 will export the functions you specify, and can also re-export functions
628 exported by some other module (like C<Moose.pm>).
629
630 The C<unimport> method cleans the caller's namespace of all the exported
631 functions.
632
633 If you pass any parameters for L<Moose::Util::MetaRole>, this method will
634 generate an C<init_meta> for you as well (see below for details). This
635 C<init_meta> will call C<Moose::Util::MetaRole::apply_metaclass_roles> and
636 C<Moose::Util::MetaRole::apply_base_class_roles> as needed.
637
638 Note that if any of these methods already exist, they will not be
639 overridden, you will have to use C<build_import_methods> to get the
640 coderef that would be installed.
641
642 This method accepts the following parameters:
643
644 =over 8
645
646 =item * with_meta => [ ... ]
647
648 This list of function I<names only> will be wrapped and then exported. The
649 wrapper will pass the metaclass object for the caller as its first argument.
650
651 Many sugar functions will need to use this metaclass object to do something to
652 the calling package.
653
654 =item * as_is => [ ... ]
655
656 This list of function names or sub references will be exported as-is. You can
657 identify a subroutine by reference, which is handy to re-export some other
658 module's functions directly by reference (C<\&Some::Package::function>).
659
660 If you do export some other package's function, this function will never be
661 removed by the C<unimport> method. The reason for this is we cannot know if
662 the caller I<also> explicitly imported the sub themselves, and therefore wants
663 to keep it.
664
665 =item * also => $name or \@names
666
667 This is a list of modules which contain functions that the caller
668 wants to export. These modules must also use C<Moose::Exporter>. The
669 most common use case will be to export the functions from C<Moose.pm>.
670 Functions specified by C<with_meta> or C<as_is> take precedence over
671 functions exported by modules specified by C<also>, so that a module
672 can selectively override functions exported by another module.
673
674 C<Moose::Exporter> also makes sure all these functions get removed
675 when C<unimport> is called.
676
677 =back
678
679 Any of the C<*_roles> options for
680 C<Moose::Util::MetaRole::apply_metaclass_roles> and
681 C<Moose::Util::MetaRole::base_class_roles> are also acceptable.
682
683 =item B<< Moose::Exporter->build_import_methods(...) >>
684
685 Returns two or three code refs, one for C<import>, one for
686 C<unimport>, and optionally one for C<init_meta>, if the appropriate
687 options are passed in.
688
689 Accepts the additional C<install> option, which accepts an arrayref of method
690 names to install into your exporting package. The valid options are C<import>,
691 C<unimport>, and C<init_meta>. Calling C<setup_import_methods> is equivalent
692 to calling C<build_import_methods> with C<< install => [qw(import unimport
693 init_meta)] >> except that it doesn't also return the methods.
694
695 Used by C<setup_import_methods>.
696
697 =back
698
699 =head1 IMPORTING AND init_meta
700
701 If you want to set an alternative base object class or metaclass class, see
702 above for details on how this module can call L<Moose::Util::MetaRole> for
703 you.
704
705 If you want to do something that is not supported by this module, simply
706 define an C<init_meta> method in your class. The C<import> method that
707 C<Moose::Exporter> generates for you will call this method (if it exists). It
708 will always pass the caller to this method via the C<for_class> parameter.
709
710 Most of the time, your C<init_meta> method will probably just call C<<
711 Moose->init_meta >> to do the real work:
712
713   sub init_meta {
714       shift; # our class name
715       return Moose->init_meta( @_, metaclass => 'My::Metaclass' );
716   }
717
718 Keep in mind that C<build_import_methods> will return an C<init_meta>
719 method for you, which you can also call from within your custom
720 C<init_meta>:
721
722   my ( $import, $unimport, $init_meta ) =
723       Moose::Exporter->build_import_methods( ... );
724
725   sub import {
726      my $class = shift;
727
728      ...
729
730      $class->$import(...);
731
732      ...
733   }
734
735   sub unimport { goto &$unimport }
736
737   sub init_meta {
738      my $class = shift;
739
740      ...
741
742      $class->$init_meta(...);
743
744      ...
745   }
746
747 =head1 METACLASS TRAITS
748
749 The C<import> method generated by C<Moose::Exporter> will allow the
750 user of your module to specify metaclass traits in a C<-traits>
751 parameter passed as part of the import:
752
753   use Moose -traits => 'My::Meta::Trait';
754
755   use Moose -traits => [ 'My::Meta::Trait', 'My::Other::Trait' ];
756
757 These traits will be applied to the caller's metaclass
758 instance. Providing traits for an exporting class that does not create
759 a metaclass for the caller is an error.
760
761 =head1 AUTHOR
762
763 Dave Rolsky E<lt>autarch@urth.orgE<gt>
764
765 This is largely a reworking of code in Moose.pm originally written by
766 Stevan Little and others.
767
768 =head1 COPYRIGHT AND LICENSE
769
770 Copyright 2009 by Infinity Interactive, Inc.
771
772 L<http://www.iinteractive.com>
773
774 This library is free software; you can redistribute it and/or modify
775 it under the same terms as Perl itself.
776
777 =cut