Commit | Line | Data |
5bd4db9b |
1 | package Moose::Exporter; |
2 | |
3 | use strict; |
4 | use warnings; |
5 | |
6 | use Class::MOP; |
cd00320f |
7 | use namespace::clean 0.08 (); |
5bd4db9b |
8 | use Sub::Exporter; |
9 | |
10 | |
0338a411 |
11 | my %EXPORT_SPEC; |
1a601f52 |
12 | |
a5c426fc |
13 | sub build_import_methods { |
14 | my $class = shift; |
15 | my %args = @_; |
16 | |
17 | my $exporting_package = caller(); |
18 | |
0338a411 |
19 | $EXPORT_SPEC{$exporting_package} = \%args; |
a5c426fc |
20 | |
1a601f52 |
21 | my ( $exporter, $exported ) = $class->_build_exporter( |
22 | exporting_package => $exporting_package, |
23 | %args |
24 | ); |
a5c426fc |
25 | |
1a601f52 |
26 | my $import = $class->_make_import_sub( |
97a93056 |
27 | $exporting_package, $args{init_meta_args}, |
1a601f52 |
28 | $exporter |
29 | ); |
a5c426fc |
30 | |
1a601f52 |
31 | my $unimport = $class->_make_unimport_sub($exported); |
a5c426fc |
32 | |
33 | no strict 'refs'; |
1a601f52 |
34 | *{ $exporting_package . '::import' } = $import; |
a5c426fc |
35 | *{ $exporting_package . '::unimport' } = $unimport; |
36 | } |
37 | |
5bd4db9b |
38 | my %EXPORTED; |
a5c426fc |
39 | sub _build_exporter { |
5bd4db9b |
40 | my $class = shift; |
41 | my %args = @_; |
42 | |
a5c426fc |
43 | my $exporting_package = $args{exporting_package}; |
5bd4db9b |
44 | |
0338a411 |
45 | my @exported_names; |
5bd4db9b |
46 | my %exports; |
97a93056 |
47 | for my $name ( @{ $args{with_caller} } ) { |
48 | my $sub = do { no strict 'refs'; \&{ $exporting_package . '::' . $name } }; |
49 | |
3492a4cd |
50 | # We need to set the package at import time, so that when |
51 | # package Foo imports has(), we capture "Foo" as the |
52 | # package. This lets other packages call Foo::has() and get |
53 | # the right package. This is done for backwards compatibility |
54 | # with existing production code, not because this is a good |
55 | # idea ;) |
56 | $exports{$name} = sub { |
57 | my $caller; |
58 | |
59 | my $x = 0; |
60 | do |
61 | { |
62 | $caller = scalar caller($x++) |
63 | } |
64 | while ( $caller eq 'Sub::Exporter' ); |
65 | |
66 | Class::MOP::subname( $exporting_package . '::' |
67 | . $name => sub { $sub->( $caller, @_ ) } ); |
68 | }; |
97a93056 |
69 | |
70 | push @exported_names, $name; |
71 | } |
72 | |
73 | for my $name ( @{ $args{as_is} } ) { |
5bd4db9b |
74 | my $sub; |
97a93056 |
75 | |
5bd4db9b |
76 | if ( ref $name ) { |
77 | $sub = $name; |
78 | $name = ( Class::MOP::get_code_info($name) )[1]; |
79 | } |
80 | else { |
a5c426fc |
81 | $sub = do { no strict 'refs'; \&{ $exporting_package . '::' . $name } }; |
5bd4db9b |
82 | |
0338a411 |
83 | push @exported_names, $name; |
5bd4db9b |
84 | } |
85 | |
86 | $exports{$name} = sub { $sub }; |
87 | } |
88 | |
0338a411 |
89 | my $exporter = Sub::Exporter::build_exporter( |
5bd4db9b |
90 | { |
91 | exports => \%exports, |
92 | groups => { default => [':all'] } |
93 | } |
94 | ); |
5bd4db9b |
95 | |
0338a411 |
96 | return $exporter, \@exported_names; |
5bd4db9b |
97 | } |
98 | |
1a601f52 |
99 | sub _make_import_sub { |
100 | my $class = shift; |
101 | my $exporting_package = shift; |
102 | my $init_meta_args = shift; |
103 | my $exporter = shift; |
104 | |
105 | return sub { |
106 | my $caller = Moose::Exporter->_get_caller(@_); |
107 | |
108 | # this works because both pragmas set $^H (see perldoc perlvar) |
109 | # which affects the current compilation - i.e. the file who use'd |
110 | # us - which is why we don't need to do anything special to make |
111 | # it affect that file rather than this one (which is already compiled) |
112 | |
113 | strict->import; |
114 | warnings->import; |
115 | |
116 | # we should never export to main |
117 | if ( $caller eq 'main' ) { |
118 | warn |
119 | qq{$exporting_package does not export its sugar to the 'main' package.\n}; |
120 | return; |
121 | } |
122 | |
123 | if ( $exporting_package->can('_init_meta') ) { |
124 | $exporting_package->_init_meta( |
125 | for_class => $caller, |
97a93056 |
126 | %{ $init_meta_args || {} } |
1a601f52 |
127 | ); |
128 | } |
129 | |
130 | goto $exporter; |
131 | }; |
132 | } |
133 | |
134 | sub _get_caller { |
135 | # 1 extra level because it's called by import so there's a layer |
136 | # of indirection |
137 | my $offset = 1; |
138 | |
139 | return |
140 | ( ref $_[1] && defined $_[1]->{into} ) ? $_[1]->{into} |
141 | : ( ref $_[1] && defined $_[1]->{into_level} ) |
142 | ? caller( $offset + $_[1]->{into_level} ) |
143 | : caller($offset); |
144 | } |
145 | |
146 | sub _make_unimport_sub { |
147 | my $class = shift; |
148 | my $exported = shift; |
149 | |
150 | # [12:24] <mst> yes. that's horrible. I know. but it should work. |
151 | # |
152 | # This will hopefully be replaced in the future once |
153 | # namespace::clean has an API for it. |
154 | return sub { |
155 | @_ = ( 'namespace::clean', @{$exported} ); |
156 | |
157 | goto &namespace::clean::import; |
158 | }; |
159 | } |
160 | |
5bd4db9b |
161 | 1; |
2f29843c |
162 | |
163 | __END__ |
164 | |
165 | =head1 NAME |
166 | |
167 | Moose::Exporter - make an import() and unimport() just like Moose.pm |
168 | |
169 | =head1 SYNOPSIS |
170 | |
171 | package MyApp::Moose; |
172 | |
173 | use strict; |
174 | use warnings; |
175 | |
176 | use Moose (); |
177 | use Moose::Exporter; |
178 | |
179 | Moose::Exporter->build_export_methods( |
180 | export => [ 'sugar1', 'sugar2', \&Some::Random::thing ], |
181 | init_meta_args => { metaclass_class => 'MyApp::Meta::Class' ], |
182 | ); |
183 | |
184 | # then later ... |
185 | package MyApp::User; |
186 | |
187 | use MyApp::Moose; |
188 | |
189 | has 'name'; |
190 | sugar1 'do your thing'; |
191 | thing; |
192 | |
193 | no MyApp::Moose; |
194 | |
195 | =head1 DESCRIPTION |
196 | |
197 | This module encapsulates the logic to export sugar functions like |
198 | C<Moose.pm>. It does this by building custom C<import> and C<unimport> |
199 | methods for your module, based on a spec your provide. |
200 | |
201 | It also lets your "stack" Moose-alike modules so you can export |
202 | Moose's sugar as well as your own, along with sugar from any random |
203 | C<MooseX> module, as long as they all use C<Moose::Exporter>. |
204 | |
205 | =head1 METHODS |
206 | |
207 | This module provides exactly one public method: |
208 | |
209 | =head2 Moose::Exporter->build_import_methods(...) |
210 | |
211 | When you call this method, C<Moose::Exporter> build custom C<import> |
212 | and C<unimport> methods for your module. The import method will export |
213 | the functions you specify, and you can also tell it to export |
214 | functions exported by some other module (like C<Moose.pm>). |
215 | |
216 | The C<unimport> method cleans the callers namespace of all the |
217 | exported functions. |
218 | |
219 | This method accepts the following parameters: |
220 | |
221 | =over 4 |
222 | |
97a93056 |
223 | =item * with_caller => [ ... ] |
224 | |
225 | This a list of function I<names only> to be exported wrapped and then |
226 | exported. The wrapper will pass the name of the calling package as the |
227 | first argument to the function. Many sugar functions need to know |
228 | their caller so they can get the calling package's metaclass object. |
229 | |
230 | =item * as_is => [ ... ] |
2f29843c |
231 | |
232 | This a list of function names or sub references to be exported |
233 | as-is. You can identify a subroutine by reference, which is handy to |
234 | re-export some other module's functions directly by reference |
235 | (C<\&Some::Package::function>). |
236 | |
237 | =item * init_meta_args |
238 | |
239 | ... |
240 | |
241 | =back |
242 | |
243 | =head1 AUTHOR |
244 | |
245 | Dave Rolsky E<lt>autarch@urth.orgE<gt> |
246 | |
247 | This is largely a reworking of code in Moose.pm originally written by |
248 | Stevan Little and others. |
249 | |
250 | =head1 COPYRIGHT AND LICENSE |
251 | |
252 | Copyright 2008 by Infinity Interactive, Inc. |
253 | |
254 | L<http://www.iinteractive.com> |
255 | |
256 | This library is free software; you can redistribute it and/or modify |
257 | it under the same terms as Perl itself. |
258 | |
259 | =cut |