1Sub::Exporter::CookbookU(s3e)r Contributed Perl DocumentaStuibo:n:Exporter::Cookbook(3)
2
3
4
6 Sub::Exporter::Cookbook - useful, demonstrative, or stupid
7 Sub::Exporter tricks
8
10 Sub::Exporter is a fairly simple tool, and can be used to achieve some
11 very simple goals. Its basic behaviors and their basic application
12 (that is, "traditional" exporting of routines) are described in
13 Sub::Exporter::Tutorial and Sub::Exporter. This document presents
14 applications that may not be immediately obvious, or that can demon‐
15 strate how certain features can be put to use (for good or evil).
16
18 Exporting Methods as Routines
19
20 With Exporter.pm, exporting methods is a non-starter. Sub::Exporter
21 makes it simple. By using the "curry_method" utility provided in
22 Sub::Exporter::Util, a method can be exported with the invocant built
23 in.
24
25 package Object::Strenuous;
26
27 use Sub::Exporter::Util;
28 use Sub::Exporter -setup => {
29 exports => [ objection => curry_method('new') ],
30 };
31
32 With this configuration, the importing code may contain:
33
34 my $obj = objection("irrelevant");
35
36 ...and this will be equivalent to:
37
38 my $obj = Object::Strenuous->new("irrelevant");
39
40 The built-in invocant is determined by the invocant for the "import"
41 method. That means that if we were to subclass Object::Strenuous as
42 follows:
43
44 package Object::Strenuous::Repeated;
45 @ISA = 'Object::Strenuous';
46
47 ...then importing "objection" from the subclass would build-in that
48 subclass.
49
50 Finally, since the invocant can be an object, you can write something
51 like this:
52
53 package Cypher;
54 use Sub::Exporter -setup => {
55 exports => [ encypher => curry_method ],
56 };
57
58 with the expectation that "import" will be called an instantiated
59 Cypher object:
60
61 BEGIN {
62 my $cypher = Cypher->new( ... );
63 $cypher->import('encypher');
64 }
65
66 Now there is a globally-available "encypher" routine which calls the
67 encypher method on an otherwise unavailable Cypher object.
68
69 Exporting Methods as Methods
70
71 While exporting modules usually export subroutines to be called as sub‐
72 routines, it's easy to use Sub::Exporter to export subroutines meant to
73 be called as methods on the importing package or its objects.
74
75 Here's a trivial (and naive) example:
76
77 package Mixin::DumpObj;
78
79 use Data::Dumper;
80
81 use Sub::Exporter -setup => {
82 exports => [ qw(dump) ]
83 };
84
85 sub dump {
86 my ($self) = @_;
87 return Dumper($self);
88 }
89
90 When writing your own object class, you can then import "dump" to be
91 used as a method, called like so:
92
93 $object->dump;
94
95 By assuming that the importing class will provide a certain interface,
96 a method-exporting module can be used as a simple plugin:
97
98 package Number::Plugin::Upto;
99 use Sub::Exporter -setup => {
100 into => 'Number',
101 exports => [ qw(upto) ],
102 groups => [ default => [ qw(upto) ] ],
103 };
104
105 sub upto {
106 my ($self) = @_;
107 return 1 .. abs($self->as_integer);
108 }
109
110 The "into" line in the configuration says that this plugin will export,
111 by default, into the Number package, not into the "use"-ing package.
112 It can be exported anyway, though, and will work as long as the desti‐
113 nation provides an "as_integer" method like the one it expects. To
114 import it to a different destination, one can just write:
115
116 use Number::Plugin::Upto { into => 'Quantity' };
117
118 Mixing-in Complex External Behavior
119
120 When exporting methods to be used as methods (see above), one very pow‐
121 erful option is to export methods that are generated routines that
122 maintain an enclosed reference to the exporting module. This allows a
123 user to import a single method which is implemented in terms of a com‐
124 plete, well-structured package.
125
126 Here is a very small example:
127
128 package Data::Analyzer;
129
130 use Sub::Exporter -setup => {
131 exports => [ analyze => \'_generate_analyzer' ],
132 };
133
134 sub _generate_analyzer {
135 my ($mixin, $name, $arg, $col) = @_;
136
137 return sub {
138 my ($self) = @_;
139
140 my $values = [ $self->values ];
141
142 my $analyzer = $mixin->new($values);
143 $analyzer->perform_analysis;
144 $analyzer->aggregate_results;
145
146 return $analyzer->summary;
147 };
148 }
149
150 If imported by any package providing a "values" method, this plugin
151 will provide a single "analyze" method that acts as a simple interface
152 to a more complex set of behaviors.
153
154 Even more importantly, because the $mixin value will be the invocant on
155 which the "import" was actually called, one can subclass "Data::Ana‐
156 lyzer" and replace only individual pieces of the complex behavior, mak‐
157 ing it easy to write complex, subclassable toolkits with simple single
158 points of entry for external interfaces.
159
160 Exporting Constants
161
162 While Sub::Exporter isn't in the constant-exporting business, it's easy
163 to export constants by using one of its sister modules, Package::Gener‐
164 ator.
165
166 package Important::Constants;
167
168 use Sub::Exporter -setup => {
169 collectors => [ constants => \'_set_constants' ],
170 };
171
172 sub _set_constants {
173 my ($class, $value, $data) = @_;
174
175 Package::Generator->assign_symbols(
176 $data->{into},
177 [
178 MEANING_OF_LIFE => 42,
179 ONE_TRUE_BASE => 13,
180 FACTORS => [ 6, 9 ],
181 ],
182 );
183 }
184
185 Then, someone can write:
186
187 use Important::Constants 'constants';
188
189 print "The factors @FACTORS produce $MEANING_OF_LIFE in $ONE_TRUE_BASE.";
190
191 (The constants must be exported via a collector, because they are
192 effectively altering the importing class in a way other than installing
193 subroutines.)
194
195 Altering the Importer's @ISA
196
197 It's trivial to make a collector that changes the inheritence of an
198 importing package:
199
200 use Sub::Exporter -setup => {
201 collectors => { -base => \'_make_base' },
202 };
203
204 sub _make_base {
205 my ($class, $value, $data) = @_;
206
207 my $target = $data->{into};
208 push @{"$target\::ISA"}, $class;
209 }
210
211 Then, the user of your class can write:
212
213 use Some::Class -base;
214
215 and become a subclass. This can be quite useful in building, for exam‐
216 ple, a module that helps build plugins. We may want a few utilities
217 imported, but we also want to inherit behavior from some base plugin
218 class;
219
220 package Framework::Util;
221
222 use Sub::Exporter -setup => {
223 exports => [ qw(log global_config) ],
224 collectors => { _become_plugin => \'_become_plugin' },
225 groups => [ -plugin => [ qw(log global_config _become_plugin) ]
226 };
227
228 sub _become_plugin {
229 my ($class, $value, $data) = @_;
230
231 my $target = $data->{into};
232 push @{"$target\::ISA"}, $class->plugin_base_class;
233 }
234
235 Now, you can write a plugin like this:
236
237 package Framework::Plugin::AirFreshener;
238 use Framework::Util -plugin;
239
240 Eating Exporter.pm's Brain
241
242 You probably shouldn't actually do this in production. It's offered
243 more as a demonstration than a suggestion.
244
245 sub exporter_upgrade {
246 my ($pkg) = @_;
247 my $new_pkg = "$pkg\::UsingSubExporter";
248
249 return $new_pkg if $new_pkg->isa($pkg);
250
251 Sub::Exporter::setup_exporter({
252 as => 'import',
253 into => $new_pkg,
254 exports => [ @{"$pkg\::EXPORT_OK"} ],
255 groups => {
256 %{{"$pkg\::EXPORT_TAG"},
257 default => [ @{"$pkg\::EXPORTS"} ],
258 },
259 });
260
261 @{"$new_pkg\::ISA"} = $class;
262 return $new_pkg;
263 }
264
265 This routine, given the name of an existing package configured to use
266 Exporter.pm, returns the name of a new package with a
267 Sub::Exporter-powered "import" routine. This lets you write:
268
269 BEGIN {
270 require Toolkit;
271 exporter_upgrade('Toolkit')->import(exported_sub => { -as => 'foo' })
272 }
273
274 If you're feeling particularly naughty, this routine could have been
275 declared in the UNIVERSAL package, meaning you could write:
276
277 BEGIN {
278 require Toolkit;
279 Toolkit->exporter_upgrade->import(exported_sub => { -as => 'foo' })
280 }
281
282 The new package will have all the same exporter configuration as the
283 original, but will support export and group renaming, including export‐
284 ing into scalar references. Further, since Sub::Exporter uses "can" to
285 find the routine being exported, the new package may be subclassed and
286 some of its exports replaced.
287
289 Ricardo SIGNES, "<rjbs@cpan.org>"
290
292 Copyright 2007, Ricardo SIGNES. This is free software; you can redis‐
293 tribute it and/or modify it under the same terms as Perl itself.
294
295
296
297perl v5.8.8 2007-07-05 Sub::Exporter::Cookbook(3)