1Sub::Exporter::CookbookU(s3e)r Contributed Perl DocumentaStuibo:n:Exporter::Cookbook(3)
2
3
4

NAME

6       Sub::Exporter::Cookbook - useful, demonstrative, or stupid
7       Sub::Exporter tricks
8

VERSION

10       version 0.987
11

OVERVIEW

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

THE RECIPES

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

AUTHOR

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)
Impressum