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

OVERVIEW

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

THE RECIPIES

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

AUTHOR

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