1Package::Variant(3) User Contributed Perl Documentation Package::Variant(3)
2
3
4
6 Package::Variant - Parameterizable packages
7
9 Creation of anonymous variants:
10
11 # declaring a variable Moo role
12 package My::VariableRole::ObjectAttr;
13 use strictures 2;
14 use Package::Variant
15 # what modules to 'use'
16 importing => ['Moo::Role'],
17 # proxied subroutines
18 subs => [ qw(has around before after with) ];
19
20 sub make_variant {
21 my ($class, $target_package, %arguments) = @_;
22 # access arguments
23 my $name = $arguments{name};
24 # use proxied 'has' to add an attribute
25 has $name => (is => 'lazy');
26 # install a builder method
27 install "_build_${name}" => sub {
28 return $arguments{class}->new;
29 };
30 }
31
32 # using the role
33 package My::Class::WithObjectAttr;
34 use strictures 2;
35 use Moo;
36 use My::VariableRole::ObjectAttr;
37
38 with ObjectAttr(name => 'some_obj', class => 'Some::Class');
39
40 # using our class
41 my $obj = My::Class::WithObjectAttr->new;
42 $obj->some_obj; # returns a Some::Class instance
43
44 And the same thing, only with named variants:
45
46 # declaring a variable Moo role that can be named
47 package My::VariableRole::ObjectAttrNamed;
48 use strictures 2;
49 use Package::Variant importing => ['Moo::Role'],
50 subs => [ qw(has around before after with) ];
51 use Module::Runtime 'module_notional_filename'; # only if you need protection
52
53 # this method is run at variant creation time to determine its custom
54 # package name. it can use the arguments or do something entirely else.
55 sub make_variant_package_name {
56 my ($class, $package, %arguments) = @_;
57 $package = "Private::$package"; # you can munge the input here if you like
58 # only if you *need* protection
59 die "Won't clobber $package" if $INC{module_notional_filename $package};
60 return $package;
61 }
62
63 # same as in the example above, except for the argument list. in this example
64 # $package is the user input, and
65 # $target_package is the actual package in which the variant gets installed
66 sub make_variant {
67 my ($class, $target_package, $package, %arguments) = @_;
68 my $name = $arguments{name};
69 has $name => (is => 'lazy');
70 install "_build_${name}" => sub {return $arguments{class}->new};
71 }
72
73 # using the role
74 package My::Class::WithObjectAttr;
75 use strictures 2;
76 use Moo;
77 use My::VariableRole::ObjectAttrNamed;
78
79 # create the role under a specific name
80 ObjectAttrNamed "My::Role" => (name => 'some_obj', class => 'Some::Class');
81 # and use it
82 with "Private::My::Role";
83
84 # using our class
85 my $obj = My::Class::WithObjectAttr->new;
86 $obj->some_obj; # returns a Some::Class instance
87
89 This module allows you to build a variable package that contains a
90 package template and can use it to build variant packages at runtime.
91
92 Your variable package will export a subroutine which will build a
93 variant package, combining its arguments with the template, and return
94 the name of the new variant package.
95
96 The implementation does not care about what kind of packages it builds,
97 be they simple function exporters, classes, singletons or something
98 entirely different.
99
100 Declaring a variable package
101 There are two important parts to creating a variable package. You first
102 have to give "Package::Variant" some basic information about what kind
103 of variant packages you want to provide, and how. The second part is
104 implementing a method which builds the components of the variant
105 packages that use the user's arguments or cannot be provided with a
106 static import.
107
108 Setting up the environment for building variants
109
110 When you "use Package::Variant", you pass along some arguments that
111 describe how you intend to build your variants.
112
113 use Package::Variant
114 importing => { $package => \@import_arguments, ... },
115 subs => [ @proxied_subroutine_names ];
116
117 The "importing" option needs to be a hash or array reference with
118 package names to be "use"d as keys, and array references containing the
119 import arguments as values. These packages will be imported into every
120 new variant package, to provide static functionality of the variant
121 packages and to set up every declarative subroutine you require to
122 build variants package components. The next option will allow you to
123 use these functions. See "importing" for more options. You can omit
124 empty import argument lists when passing an array reference.
125
126 The "subs" option is an array reference of subroutine names that are
127 exported by the packages specified with "importing". These subroutines
128 will be proxied from your variable package to the variant to be
129 generated.
130
131 With "importing" initializing your package and "subs" declaring what
132 subroutines you want to use to build a variant, you can now write a
133 "make_variant" method building your variants.
134
135 Declaring a method to produce variants
136
137 Every time a user requests a new variant, a method named "make_variant"
138 will be called with the name of the target package and the arguments
139 from the user.
140
141 It can then use the proxied subroutines declared with "subs" to
142 customize the variant package. An "install" subroutine is exported as
143 well allowing you to dynamically install methods into the variant
144 package. If these options aren't flexible enough, you can use the
145 passed name of the variant package to do any other kind of
146 customizations.
147
148 sub make_variant {
149 my ($class, $target, @arguments) = @_;
150 # ...
151 # customization goes here
152 # ...
153 }
154
155 When the method is finished, the user will receive the name of the new
156 variant package you just set up.
157
158 Using variable packages
159 After your variable package is created your users can get a variant
160 generator subroutine by simply importing your package.
161
162 use My::Variant;
163 my $new_variant_package = Variant(@variant_arguments);
164 # the variant package is now fully initialized and used
165
166 You can import the subroutine under a different name by specifying an
167 "as" argument.
168
169 Dynamic creation of variant packages
170 For regular uses, the normal import provides more than enough
171 flexibility. However, if you want to create variants of dynamically
172 determined packages, you can use the "build_variant_of" method.
173
174 You can use this to create variants of other packages and pass
175 arguments on to them to allow more modular and extensible variants.
176
178 These are the options that can be passed when importing
179 "Package::Variant". They describe the environment in which the variants
180 are created.
181
182 use Package::Variant
183 importing => { $package => \@import_arguments, ... },
184 subs => [ @proxied_subroutines ];
185
186 importing
187 This option is a hash reference mapping package names to array
188 references containing import arguments. The packages will be imported
189 with the given arguments by every variant before the "make_variant"
190 method is asked to create the package (this is done using
191 Import::Into).
192
193 If import order is important to you, you can also pass the "importing"
194 arguments as a flat array reference:
195
196 use Package::Variant
197 importing => [ 'PackageA', 'PackageB' ];
198
199 # same as
200 use Package::Variant
201 importing => [ 'PackageA' => [], 'PackageB' => [] ];
202
203 # or
204 use Package::Variant
205 importing => { 'PackageA' => [], 'PackageB' => [] };
206
207 The import method will be called even if the list of import arguments
208 is empty or not specified,
209
210 If you just want to import a single package's default exports, you can
211 also pass a string instead:
212
213 use Package::Variant importing => 'Package';
214
215 subs
216 An array reference of strings listing the names of subroutines that
217 should be proxied. These subroutines are expected to be installed into
218 the new variant package by the modules imported with "importing".
219 Subroutines with the same name will be available in your variable
220 package, and will proxy through to the newly created package when used
221 within "make_variant".
222
224 These are methods on the variable package you declare when you import
225 "Package::Variant".
226
227 make_variant
228 Some::Variant::Package->make_variant( $target, @arguments );
229
230 You need to provide this method. This method will be called for every
231 new variant of your package. This method should use the subroutines
232 declared in "subs" to customize the new variant package.
233
234 This is a class method receiving the $target package and the @arguments
235 defining the requested variant.
236
237 make_variant_package_name
238 Some::Variant::Package->make_variant_package_name( @arguments );
239
240 You may optionally provide this method. If present, this method will be
241 used to determine the package name for a particular variant being
242 constructed.
243
244 If you do not implement it, a unique package name something like
245
246 Some::Variant::Package::_Variant_A003
247
248 will be created for you.
249
250 import
251 use Some::Variant::Package;
252 my $variant_package = Package( @arguments );
253
254 This method is provided for you. It will allow a user to "use" your
255 package and receive a subroutine taking @arguments defining the variant
256 and returning the name of the newly created variant package.
257
258 The following options can be specified when importing:
259
260 • as
261
262 use Some::Variant::Package as => 'Foo';
263 my $variant_package = Foo(@arguments);
264
265 Exports the generator subroutine under a different name than the
266 default.
267
268 build_variant
269 use Some::Variant::Package ();
270 my $variant_package = Some::Variant::Package->build_variant( @arguments );
271
272 This method is provided for you. It will generate a variant package
273 and return its name, just like the generator sub provided by "import".
274 This allows you to avoid importing anything into the consuming package.
275
277 These methods are available on "Package::Variant" itself.
278
279 build_variant_of
280 my $variant_package = Package::Variant
281 ->build_variant_of($variable_package, @arguments);
282
283 This is the dynamic method of creating new variants. It takes the
284 $variable_package, which is a pre-declared variable package, and a set
285 of @arguments passed to the package to generate a new $variant_package,
286 which will be returned.
287
288 import
289 use Package::Variant @options;
290
291 Sets up the environment in which you declare the variants of your
292 packages. See "OPTIONS" for details on the available options and
293 "EXPORTS" for a list of exported subroutines.
294
296 Additionally to the proxies for subroutines provided in "subs", the
297 following exports will be available in your variable package:
298
299 install
300 install($method_name, $code_reference);
301
302 Installs a method with the given $method_name into the newly created
303 variant package. The $code_reference will be used as the body for the
304 method, and if Sub::Name is available the coderef will be named. If you
305 want to name it something else, then use:
306
307 install($method_name, $name_to_use, $code_reference);
308
310 mst - Matt S. Trout (cpan:MSTROUT) <mst@shadowcat.co.uk>
311
313 phaylon - Robert Sedlacek (cpan:PHAYLON) <r.sedlacek@shadowcat.co.uk>
314
315 haarg - Graham Knop (cpan:HAARG) <haarg@haarg.org>
316
318 Copyright (c) 2010-2012 the "Package::Variant" "AUTHOR" and
319 "CONTRIBUTORS" as listed above.
320
322 This library is free software and may be distributed under the same
323 terms as perl itself.
324
325
326
327perl v5.38.0 2023-07-21 Package::Variant(3)