Introduce install_subroutines() to reduce direct stash manipulation
[gitmo/Mouse.git] / lib / Mouse / Exporter.pm
CommitLineData
db9fc237 1package Mouse::Exporter;
2use strict;
3use warnings;
4
5use Carp qw(confess);
6
db9fc237 7my %SPEC;
8
99934a1b 9use constant _strict_bits => strict::bits(qw(subs refs vars));
1ff34b4c 10
0eb86915 11# it must be "require", because Mouse::Util depends on Mouse::Exporter,
12# which depends on Mouse::Util::import()
13require Mouse::Util;
14
1bd3c83e 15sub import{
99934a1b 16 $^H |= _strict_bits; # strict->import;
4b55a023 17 ${^WARNING_BITS} |= $warnings::Bits{all}; # warnings->import;
1bd3c83e 18 return;
19}
20
0eb86915 21
db9fc237 22sub setup_import_methods{
23 my($class, %args) = @_;
24
25 my $exporting_package = $args{exporting_package} ||= caller();
26
e6dc493d 27 my($import, $unimport) = $class->build_import_methods(%args);
28
1194aede 29 Mouse::Util::install_subroutines($exporting_package,
30 import => $import,
31 unimport => $unimport,
32
33 export_to_level => sub {
34 my($package, $level, undef, @args) = @_; # the third argument is redundant
35 $package->import({ into_level => $level + 1 }, @args);
36 },
37 export => sub {
38 my($package, $into, @args) = @_;
39 $package->import({ into => $into }, @args);
40 },
41 );
e6dc493d 42 return;
43}
44
45sub build_import_methods{
46 my($class, %args) = @_;
47
48 my $exporting_package = $args{exporting_package} ||= caller();
49
db9fc237 50 $SPEC{$exporting_package} = \%args;
51
52 # canonicalize args
53 my @export_from;
54 if($args{also}){
55 my %seen;
56 my @stack = ($exporting_package);
57
58 while(my $current = shift @stack){
59 push @export_from, $current;
60
61 my $also = $SPEC{$current}{also} or next;
0eb86915 62 push @stack, grep{ !$seen{$_}++ } ref($also) ? @{ $also } : $also;
db9fc237 63 }
64 }
65 else{
66 @export_from = ($exporting_package);
67 }
68
72dc2c9d 69 my %exports;
70 my @removables;
71 my @all;
72
73 my @init_meta_methods;
74
75 foreach my $package(@export_from){
76 my $spec = $SPEC{$package} or next;
1ff34b4c 77
72dc2c9d 78 if(my $as_is = $spec->{as_is}){
79 foreach my $thingy (@{$as_is}){
80 my($code_package, $code_name, $code);
81
82 if(ref($thingy)){
83 $code = $thingy;
84 ($code_package, $code_name) = Mouse::Util::get_code_info($code);
85 }
86 else{
72dc2c9d 87 $code_package = $package;
88 $code_name = $thingy;
1194aede 89 no strict 'refs';
72dc2c9d 90 $code = \&{ $code_package . '::' . $code_name };
91 }
92
93 push @all, $code_name;
94 $exports{$code_name} = $code;
95 if($code_package eq $package){
96 push @removables, $code_name;
1ff34b4c 97 }
98 }
db9fc237 99 }
1ff34b4c 100
72dc2c9d 101 if(my $init_meta = $package->can('init_meta')){
102 if(!grep{ $_ == $init_meta } @init_meta_methods){
103 push @init_meta_methods, $init_meta;
1ff34b4c 104 }
1ff34b4c 105 }
72dc2c9d 106 }
107 $args{EXPORTS} = \%exports;
108 $args{REMOVABLES} = \@removables;
1ff34b4c 109
72dc2c9d 110 $args{groups}{all} ||= \@all;
111
112 if(my $default_list = $args{groups}{default}){
113 my %default;
114 foreach my $keyword(@{$default_list}){
115 $default{$keyword} = $exports{$keyword}
116 || confess(qq{The $exporting_package package does not export "$keyword"});
1ff34b4c 117 }
72dc2c9d 118 $args{DEFAULT} = \%default;
119 }
120 else{
121 $args{groups}{default} ||= \@all;
122 $args{DEFAULT} = $args{EXPORTS};
123 }
124
125 if(@init_meta_methods){
126 $args{INIT_META} = \@init_meta_methods;
db9fc237 127 }
128
e6dc493d 129 return (\&do_import, \&do_unimport);
db9fc237 130}
131
db9fc237 132
133# the entity of general import()
134sub do_import {
1ff34b4c 135 my($package, @args) = @_;
db9fc237 136
1ff34b4c 137 my $spec = $SPEC{$package}
138 || confess("The package $package package does not use Mouse::Exporter");
db9fc237 139
140 my $into = _get_caller_package(ref($args[0]) ? shift @args : undef);
141
142 my @exports;
45bbec05 143 my @traits;
8cbcbb47 144
45bbec05 145 while(@args){
146 my $arg = shift @args;
1ff34b4c 147 if($arg =~ s/^-//){
45bbec05 148 if($arg eq 'traits'){
f12892e5 149 push @traits, ref($args[0]) ? @{shift(@args)} : shift(@args);
45bbec05 150 }
151 else {
152 Mouse::Util::not_supported("-$arg");
153 }
1ff34b4c 154 }
155 elsif($arg =~ s/^://){
1bd3c83e 156 my $group = $spec->{groups}{$arg}
1ff34b4c 157 || confess(qq{The $package package does not export the group "$arg"});
db9fc237 158 push @exports, @{$group};
159 }
160 else{
161 push @exports, $arg;
162 }
163 }
164
99934a1b 165 $^H |= _strict_bits; # strict->import;
4b55a023 166 ${^WARNING_BITS} |= $warnings::Bits{all}; # warnings->import;
db9fc237 167
1ff34b4c 168 if($spec->{INIT_META}){
45bbec05 169 my $meta;
1ff34b4c 170 foreach my $init_meta(@{$spec->{INIT_META}}){
134f7bcb 171 $meta = $package->$init_meta(for_class => $into);
1ff34b4c 172 }
db9fc237 173
45bbec05 174 if(@traits){
175 my $type = (split /::/, ref $meta)[-1]; # e.g. "Class" for "My::Meta::Class"
176 @traits =
f12892e5 177 map{
178 ref($_) ? $_
179 : Mouse::Util::resolve_metaclass_alias($type => $_, trait => 1)
180 } @traits;
45bbec05 181
45bbec05 182 require Mouse::Util::MetaRole;
3d7c6ec9 183 Mouse::Util::MetaRole::apply_metaroles(
184 for => $into,
185 Mouse::Util::is_a_metarole($into->meta)
186 ? (role_metaroles => { role => \@traits })
187 : (class_metaroles => { class => \@traits }),
45bbec05 188 );
189 }
db9fc237 190 }
f12892e5 191 elsif(@traits){
192 Carp::confess("Cannot provide traits when $package does not have an init_meta() method");
193 }
db9fc237 194
1ff34b4c 195 if(@exports){
1194aede 196 my @export_table;
1ff34b4c 197 foreach my $keyword(@exports){
1194aede 198 push @export_table,
199 $keyword => ($spec->{EXPORTS}{$keyword}
200 || confess(qq{The $package package does not export "$keyword"})
201 );
1ff34b4c 202 }
1194aede 203 Mouse::Util::install_subroutines($into, @export_table);
1ff34b4c 204 }
205 else{
1194aede 206 Mouse::Util::install_subroutines($into, %{$spec->{DEFAULT}});
db9fc237 207 }
208 return;
209}
210
211# the entity of general unimport()
212sub do_unimport {
1ff34b4c 213 my($package, $arg) = @_;
db9fc237 214
1ff34b4c 215 my $spec = $SPEC{$package}
216 || confess("The package $package does not use Mouse::Exporter");
db9fc237 217
218 my $from = _get_caller_package($arg);
219
220 my $stash = do{
221 no strict 'refs';
222 \%{$from . '::'}
223 };
224
225 for my $keyword (@{ $spec->{REMOVABLES} }) {
21df8e42 226 next if !exists $stash->{$keyword};
4ee3190e 227 my $gv = \$stash->{$keyword};
228 if(ref($gv) eq 'GLOB' && *{$gv}{CODE} == $spec->{EXPORTS}{$keyword}){ # make sure it is from us
229 delete $stash->{$keyword};
230 }
db9fc237 231 }
232 return;
233}
234
235sub _get_caller_package {
236 my($arg) = @_;
237
7ad9df77 238 # We need one extra level because it's called by import so there's a layer
239 # of indirection
db9fc237 240 if(ref $arg){
241 return defined($arg->{into}) ? $arg->{into}
7ad9df77 242 : defined($arg->{into_level}) ? scalar caller(1 + $arg->{into_level})
243 : scalar caller(1);
db9fc237 244 }
245 else{
7ad9df77 246 return scalar caller(1);
db9fc237 247 }
248}
249
99934a1b 250#sub _spec{ %SPEC }
251
db9fc237 2521;
db9fc237 253__END__
254
255=head1 NAME
256
8cbcbb47 257Mouse::Exporter - make an import() and unimport() just like Mouse.pm
db9fc237 258
a25ca8d6 259=head1 VERSION
260
4bc73e47 261This document describes Mouse version 0.50_03
a25ca8d6 262
db9fc237 263=head1 SYNOPSIS
264
7ad9df77 265 package MyApp::Mouse;
266
267 use Mouse ();
268 use Mouse::Exporter;
269
270 Mouse::Exporter->setup_import_methods(
271 as_is => [ 'has_rw', 'other_sugar', \&Some::Random::thing ],
272 also => 'Mouse',
273 );
274
8cbcbb47 275 sub has_rw {
7ad9df77 276 my $meta = caller->meta;
277 my ( $name, %options ) = @_;
278 $meta->add_attribute(
279 $name,
280 is => 'rw',
281 %options,
282 );
283 }
284
285 # then later ...
286 package MyApp::User;
287
288 use MyApp::Mouse;
289
290 has 'name';
291 has_rw 'size';
292 thing;
293
8cbcbb47 294 no MyApp::Mouse;
db9fc237 295
296=head1 DESCRIPTION
297
7ad9df77 298This module encapsulates the exporting of sugar functions in a
299C<Mouse.pm>-like manner. It does this by building custom C<import>,
300C<unimport> methods for your module, based on a spec you provide.
8cbcbb47 301
302Note that C<Mouse::Exporter> does not provide the C<with_meta> option,
303but you can easily get the metaclass by C<< caller->meta >> as L</SYNOPSIS> shows.
db9fc237 304
e6dc493d 305=head1 METHODS
306
307=head2 C<< setup_import_methods( ARGS ) >>
308
309=head2 C<< build_import_methods( ARGS ) -> (\&import, \&unimport) >>
310
db9fc237 311=head1 SEE ALSO
312
313L<Moose::Exporter>
314
5ea1528f 315=cut
316