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