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