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