2672e419844011d89116d21d18faead2095c29fc
[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         for my $i (1..$#_) {
353             if (grep { $_ eq $_[$i] } @$init_meta_params) {
354                 $extra_args{$_[$i]} = $_[$i + 1];
355                 splice @_, $i, 2;
356             }
357         }
358
359         my $did_init_meta;
360         for my $c ( grep { $_->can('init_meta') } $class, @{$exports_from} ) {
361
362             # init_meta can apply a role, which when loaded uses
363             # Moose::Exporter, which in turn sets $CALLER, so we need
364             # to protect against that.
365             local $CALLER = $CALLER;
366             $c->init_meta(
367                 %extra_args,
368                 for_class => $CALLER,
369                 metaclass => $metaclass,
370             );
371             $did_init_meta = 1;
372         }
373
374         if ( $did_init_meta && @{$traits} ) {
375
376             # The traits will use Moose::Role, which in turn uses
377             # Moose::Exporter, which in turn sets $CALLER, so we need
378             # to protect against that.
379             local $CALLER = $CALLER;
380             _apply_meta_traits( $CALLER, $traits );
381         }
382         elsif ( @{$traits} ) {
383             require Moose;
384             Moose->throw_error(
385                 "Cannot provide traits when $class does not have an init_meta() method"
386             );
387         }
388
389         my ( undef, @args ) = @_;
390         my $extra = shift @args if ref $args[0] eq 'HASH';
391
392         $extra ||= {};
393         if ( !$extra->{into} ) {
394             $extra->{into_level} ||= 0;
395             $extra->{into_level}++;
396         }
397
398         $class->$exporter( $extra, @args );
399
400         for my $name ( keys %{$is_reexport} ) {
401             no strict 'refs';
402             no warnings 'once';
403             _flag_as_reexport( \*{ join q{::}, $CALLER, $name } );
404         }
405     };
406 }
407
408 sub _strip_traits {
409     my $idx = first_index { $_ eq '-traits' } @_;
410
411     return ( [], @_ ) unless $idx >= 0 && $#_ >= $idx + 1;
412
413     my $traits = $_[ $idx + 1 ];
414
415     splice @_, $idx, 2;
416
417     $traits = [$traits] unless ref $traits;
418
419     return ( $traits, @_ );
420 }
421
422 sub _strip_metaclass {
423     my $idx = first_index { $_ eq '-metaclass' } @_;
424
425     return ( undef, @_ ) unless $idx >= 0 && $#_ >= $idx + 1;
426
427     my $metaclass = $_[ $idx + 1 ];
428
429     splice @_, $idx, 2;
430
431     return ( $metaclass, @_ );
432 }
433
434 sub _apply_meta_traits {
435     my ( $class, $traits ) = @_;
436
437     return unless @{$traits};
438
439     my $meta = Class::MOP::class_of($class);
440
441     my $type = ( split /::/, ref $meta )[-1]
442         or Moose->throw_error(
443         'Cannot determine metaclass type for trait application . Meta isa '
444             . ref $meta );
445
446     my @resolved_traits = map {
447         ref $_
448             ? $_
449             : Moose::Util::resolve_metatrait_alias( $type => $_ )
450     } @$traits;
451
452     return unless @resolved_traits;
453
454     my %args = ( for => $class );
455
456     if ( $meta->isa('Moose::Meta::Role') ) {
457         $args{role_metaroles} = { role => \@resolved_traits };
458     }
459     else {
460         $args{class_metaroles} = { class => \@resolved_traits };
461     }
462
463     Moose::Util::MetaRole::apply_metaroles(%args);
464 }
465
466 sub _get_caller {
467
468     # 1 extra level because it's called by import so there's a layer
469     # of indirection
470     my $offset = 1;
471
472     return
473           ( ref $_[1] && defined $_[1]->{into} ) ? $_[1]->{into}
474         : ( ref $_[1] && defined $_[1]->{into_level} )
475         ? caller( $offset + $_[1]->{into_level} )
476         : caller($offset);
477 }
478
479 sub _make_unimport_sub {
480     shift;
481     my $exporting_package = shift;
482     my $exports           = shift;
483     my $export_recorder   = shift;
484     my $is_reexport    = shift;
485
486     return sub {
487         my $caller = scalar caller();
488         Moose::Exporter->_remove_keywords(
489             $caller,
490             [ keys %{$exports} ],
491             $export_recorder,
492             $is_reexport,
493         );
494     };
495 }
496
497 sub _remove_keywords {
498     shift;
499     my $package          = shift;
500     my $keywords         = shift;
501     my $recorded_exports = shift;
502     my $is_reexport   = shift;
503
504     no strict 'refs';
505
506     foreach my $name ( @{$keywords} ) {
507         if ( defined &{ $package . '::' . $name } ) {
508             my $sub = \&{ $package . '::' . $name };
509
510             # make sure it is from us
511             next unless $recorded_exports->{$sub};
512
513             if ( $is_reexport->{$name} ) {
514                 no strict 'refs';
515                 next
516                     unless _export_is_flagged(
517                             \*{ join q{::} => $package, $name } );
518             }
519
520             # and if it is from us, then undef the slot
521             delete ${ $package . '::' }{$name};
522         }
523     }
524 }
525
526 sub _make_init_meta {
527     shift;
528     my $class = shift;
529     my $args  = shift;
530
531     my %old_style_roles;
532     for my $role (
533         map {"${_}_roles"}
534         qw(
535         metaclass
536         attribute_metaclass
537         method_metaclass
538         wrapped_method_metaclass
539         instance_metaclass
540         constructor_class
541         destructor_class
542         error_class
543         )
544         ) {
545         $old_style_roles{$role} = $args->{$role}
546             if exists $args->{$role};
547     }
548
549     my %base_class_roles;
550     %base_class_roles = ( roles => $args->{base_class_roles} )
551         if exists $args->{base_class_roles};
552
553     my %new_style_roles = map { $_ => $args->{$_} }
554         grep { exists $args->{$_} } qw( class_metaroles role_metaroles );
555
556     return unless %new_style_roles || %old_style_roles || %base_class_roles;
557
558     return sub {
559         shift;
560         my %options = @_;
561
562         return unless Class::MOP::class_of( $options{for_class} );
563
564         Moose::Util::MetaRole::apply_metaroles(
565             for => $options{for_class},
566             %new_style_roles,
567             %old_style_roles,
568         );
569
570         Moose::Util::MetaRole::apply_base_class_roles(
571             for_class => $options{for_class},
572             %base_class_roles,
573             )
574             if Class::MOP::class_of( $options{for_class} )
575                 ->isa('Moose::Meta::Class');
576
577         return Class::MOP::class_of( $options{for_class} );
578     };
579 }
580
581 sub import {
582     strict->import;
583     warnings->import;
584 }
585
586 1;
587
588 __END__
589
590 =head1 NAME
591
592 Moose::Exporter - make an import() and unimport() just like Moose.pm
593
594 =head1 SYNOPSIS
595
596   package MyApp::Moose;
597
598   use Moose ();
599   use Moose::Exporter;
600
601   Moose::Exporter->setup_import_methods(
602       with_meta => [ 'has_rw', 'sugar2' ],
603       as_is     => [ 'sugar3', \&Some::Random::thing ],
604       also      => 'Moose',
605   );
606
607   sub has_rw {
608       my ( $meta, $name, %options ) = @_;
609       $meta->add_attribute(
610           $name,
611           is => 'rw',
612           %options,
613       );
614   }
615
616   # then later ...
617   package MyApp::User;
618
619   use MyApp::Moose;
620
621   has 'name';
622   has_rw 'size';
623   thing;
624
625   no MyApp::Moose;
626
627 =head1 DESCRIPTION
628
629 This module encapsulates the exporting of sugar functions in a
630 C<Moose.pm>-like manner. It does this by building custom C<import>,
631 C<unimport>, and C<init_meta> methods for your module, based on a spec you
632 provide.
633
634 It also lets you "stack" Moose-alike modules so you can export Moose's sugar
635 as well as your own, along with sugar from any random C<MooseX> module, as
636 long as they all use C<Moose::Exporter>. This feature exists to let you bundle
637 a set of MooseX modules into a policy module that developers can use directly
638 instead of using Moose itself.
639
640 To simplify writing exporter modules, C<Moose::Exporter> also imports
641 C<strict> and C<warnings> into your exporter module, as well as into
642 modules that use it.
643
644 =head1 METHODS
645
646 This module provides two public methods:
647
648 =over 4
649
650 =item  B<< Moose::Exporter->setup_import_methods(...) >>
651
652 When you call this method, C<Moose::Exporter> builds custom C<import>,
653 C<unimport>, and C<init_meta> methods for your module. The C<import> method
654 will export the functions you specify, and can also re-export functions
655 exported by some other module (like C<Moose.pm>).
656
657 The C<unimport> method cleans the caller's namespace of all the exported
658 functions. This includes any functions you re-export from other
659 packages. However, if the consumer of your package also imports those
660 functions from the original package, they will I<not> be cleaned.
661
662 If you pass any parameters for L<Moose::Util::MetaRole>, this method will
663 generate an C<init_meta> for you as well (see below for details). This
664 C<init_meta> will call C<Moose::Util::MetaRole::apply_metaclass_roles> and
665 C<Moose::Util::MetaRole::apply_base_class_roles> as needed.
666
667 Note that if any of these methods already exist, they will not be
668 overridden, you will have to use C<build_import_methods> to get the
669 coderef that would be installed.
670
671 This method accepts the following parameters:
672
673 =over 8
674
675 =item * with_meta => [ ... ]
676
677 This list of function I<names only> will be wrapped and then exported. The
678 wrapper will pass the metaclass object for the caller as its first argument.
679
680 Many sugar functions will need to use this metaclass object to do something to
681 the calling package.
682
683 =item * as_is => [ ... ]
684
685 This list of function names or sub references will be exported as-is. You can
686 identify a subroutine by reference, which is handy to re-export some other
687 module's functions directly by reference (C<\&Some::Package::function>).
688
689 If you do export some other package's function, this function will never be
690 removed by the C<unimport> method. The reason for this is we cannot know if
691 the caller I<also> explicitly imported the sub themselves, and therefore wants
692 to keep it.
693
694 =item * also => $name or \@names
695
696 This is a list of modules which contain functions that the caller
697 wants to export. These modules must also use C<Moose::Exporter>. The
698 most common use case will be to export the functions from C<Moose.pm>.
699 Functions specified by C<with_meta> or C<as_is> take precedence over
700 functions exported by modules specified by C<also>, so that a module
701 can selectively override functions exported by another module.
702
703 C<Moose::Exporter> also makes sure all these functions get removed
704 when C<unimport> is called.
705
706 =back
707
708 You can also provide parameters for C<Moose::Util::MetaRole::apply_metaroles>
709 and C<Moose::Util::MetaRole::base_class_roles>. Specifically, valid parameters
710 are "class_metaroles", "role_metaroles", and "base_object_roles".
711
712 =item B<< Moose::Exporter->build_import_methods(...) >>
713
714 Returns two or three code refs, one for C<import>, one for
715 C<unimport>, and optionally one for C<init_meta>, if the appropriate
716 options are passed in.
717
718 Accepts the additional C<install> option, which accepts an arrayref of method
719 names to install into your exporting package. The valid options are C<import>,
720 C<unimport>, and C<init_meta>. Calling C<setup_import_methods> is equivalent
721 to calling C<build_import_methods> with C<< install => [qw(import unimport
722 init_meta)] >> except that it doesn't also return the methods.
723
724 Used by C<setup_import_methods>.
725
726 =back
727
728 =head1 IMPORTING AND init_meta
729
730 If you want to set an alternative base object class or metaclass class, see
731 above for details on how this module can call L<Moose::Util::MetaRole> for
732 you.
733
734 If you want to do something that is not supported by this module, simply
735 define an C<init_meta> method in your class. The C<import> method that
736 C<Moose::Exporter> generates for you will call this method (if it exists). It
737 will always pass the caller to this method via the C<for_class> parameter.
738
739 Most of the time, your C<init_meta> method will probably just call C<<
740 Moose->init_meta >> to do the real work:
741
742   sub init_meta {
743       shift; # our class name
744       return Moose->init_meta( @_, metaclass => 'My::Metaclass' );
745   }
746
747 Keep in mind that C<build_import_methods> will return an C<init_meta>
748 method for you, which you can also call from within your custom
749 C<init_meta>:
750
751   my ( $import, $unimport, $init_meta ) =
752       Moose::Exporter->build_import_methods( ... );
753
754   sub import {
755      my $class = shift;
756
757      ...
758
759      $class->$import(...);
760
761      ...
762   }
763
764   sub unimport { goto &$unimport }
765
766   sub init_meta {
767      my $class = shift;
768
769      ...
770
771      $class->$init_meta(...);
772
773      ...
774   }
775
776 =head1 METACLASS TRAITS
777
778 The C<import> method generated by C<Moose::Exporter> will allow the
779 user of your module to specify metaclass traits in a C<-traits>
780 parameter passed as part of the import:
781
782   use Moose -traits => 'My::Meta::Trait';
783
784   use Moose -traits => [ 'My::Meta::Trait', 'My::Other::Trait' ];
785
786 These traits will be applied to the caller's metaclass
787 instance. Providing traits for an exporting class that does not create
788 a metaclass for the caller is an error.
789
790 =head1 BUGS
791
792 See L<Moose/BUGS> for details on reporting bugs.
793
794 =head1 AUTHOR
795
796 Dave Rolsky E<lt>autarch@urth.orgE<gt>
797
798 This is largely a reworking of code in Moose.pm originally written by
799 Stevan Little and others.
800
801 =head1 COPYRIGHT AND LICENSE
802
803 Copyright 2009 by Infinity Interactive, Inc.
804
805 L<http://www.iinteractive.com>
806
807 This library is free software; you can redistribute it and/or modify
808 it under the same terms as Perl itself.
809
810 =cut