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