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