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