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.988
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

PERL VERSION SUPPORT

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

THE RECIPES

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

AUTHOR

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.36.0                      2022-07-22        Sub::Exporter::Cookbook(3)
Impressum