bump version
[p5sagit/Package-Variant.git] / lib / Package / Variant.pm
CommitLineData
236a4386 1package Package::Variant;
2
3use strictures 1;
1bacb018 4use Import::Into;
3c1ca277 5use Module::Runtime qw(require_module);
1bacb018 6use Carp qw(croak);
236a4386 7
85dcc3b1 8our $VERSION = '1.002002';
eacc208a 9
10$VERSION = eval $VERSION;
11
236a4386 12our %Variable;
13
115c342b 14my $sanitize_importing = sub {
15 my ($me, $spec) = @_;
16 return []
17 unless defined $spec;
203d81fc 18 my @specced =
19 not(ref $spec)
20 ? ($spec)
21 : (ref($spec) eq 'ARRAY')
22 ? (@$spec)
23 : (ref($spec) eq 'HASH')
24 ? (map {
25 croak qq{The import argument list for '$_' is not an array ref}
26 unless ref($spec->{$_}) eq 'ARRAY';
27 ($_ => $spec->{$_});
28 } sort keys %$spec)
29 : croak q{The 'importing' option has to be either a hash or array ref};
115c342b 30 my @imports;
203d81fc 31 my $arg_count = 1;
115c342b 32 while (@specced) {
33 my $key = shift @specced;
203d81fc 34 croak qq{Value $arg_count in 'importing' is not a package string},
35 $arg_count
36 unless defined($key) and not(ref $key);
37 $arg_count++;
38 my $import_args =
39 (not(@specced) or (defined($specced[0]) and not ref($specced[0])))
40 ? []
41 : (ref($specced[0]) eq 'ARRAY')
42 ? do { $arg_count++; shift @specced }
43 : croak(
44 qq{Value $arg_count for package '$key' in 'importing' is not}
45 . qq{ a package string or array ref}
46 );
47 push @imports, [$key, $import_args];
115c342b 48 }
49 return \@imports;
50};
51
ed98a1a0 52my $sub_namer = eval {
53 require Sub::Name; sub { shift if @_ > 2; Sub::Name::subname(@_) }
54} || sub { $_[-1] };
55
236a4386 56sub import {
d61014ce 57 my $variable = caller;
236a4386 58 my $me = shift;
d61014ce 59 my $last = (split '::', $variable)[-1];
236a4386 60 my $anon = 'A000';
236a4386 61 my %args = @_;
62 no strict 'refs';
63 $Variable{$variable} = {
64 anon => $anon,
115c342b 65 args => {
66 %args,
67 importing => $me->$sanitize_importing($args{importing}),
68 },
236a4386 69 subs => {
70 map +($_ => sub {}), @{$args{subs}||[]},
71 },
72 };
d61014ce 73 *{"${variable}::import"} = sub {
236a4386 74 my $target = caller;
0a7db8d2 75 my (undef, %arg) = @_;
76 my $as = defined($arg{as}) ? $arg{as} : $last;
236a4386 77 no strict 'refs';
0a7db8d2 78 *{"${target}::${as}"} = sub {
236a4386 79 $me->build_variant_of($variable, @_);
80 };
81 };
82 my $subs = $Variable{$variable}{subs};
83 foreach my $name (keys %$subs) {
d61014ce 84 *{"${variable}::${name}"} = sub {
236a4386 85 goto &{$subs->{$name}}
86 };
87 }
d61014ce 88 *{"${variable}::install"} = sub {
236a4386 89 goto &{$Variable{$variable}{install}};
5a0662d9 90 };
91 *{"${variable}::build_variant"} = sub {
92 shift;
93 $me->build_variant_of($variable, @_);
94 };
236a4386 95}
96
97sub build_variant_of {
98 my ($me, $variable, @args) = @_;
99 my $variant_name = "${variable}::_Variant_".++$Variable{$variable}{anon};
1bacb018 100 foreach my $to_import (@{$Variable{$variable}{args}{importing}}) {
101 my ($pkg, $args) = @$to_import;
3c1ca277 102 require_module $pkg;
103 eval q{ BEGIN { $pkg->import::into($variant_name, @{$args}) }; 1; }
104 or die $@;
1bacb018 105 }
236a4386 106 my $subs = $Variable{$variable}{subs};
107 local @{$subs}{keys %$subs} = map $variant_name->can($_), keys %$subs;
108 local $Variable{$variable}{install} = sub {
ed98a1a0 109 my $full_name = "${variant_name}::".shift;
110
111 my $ref = $sub_namer->($full_name, @_);
112
236a4386 113 no strict 'refs';
ed98a1a0 114 *$full_name = $ref;
236a4386 115 };
116 $variable->make_variant($variant_name, @args);
117 return $variant_name;
118}
119
1201;
0c378352 121
122__END__
123
124=head1 NAME
125
126Package::Variant - Parameterizable packages
127
128=head1 SYNOPSIS
129
130 # declaring a variable Moo role
abc0e4ce 131 package My::VariableRole::ObjectAttr;
0c378352 132 use strictures 1;
133 use Package::Variant
134 # what modules to 'use'
efaab257 135 importing => ['Moo::Role'],
0c378352 136 # proxied subroutines
4e3b8177 137 subs => [ qw(has around before after with) ];
0c378352 138
139 sub make_variant {
140 my ($class, $target_package, %arguments) = @_;
141 # access arguments
142 my $name = $arguments{name};
143 # use proxied 'has' to add an attribute
144 has $name => (is => 'lazy');
145 # install a builder method
146 install "_build_${name}" => sub {
147 return $arguments{class}->new;
148 };
149 }
150
151 # using the role
152 package My::Class::WithObjectAttr;
153 use strictures 1;
154 use Moo;
abc0e4ce 155 use My::VariableRole::ObjectAttr;
0c378352 156
157 with ObjectAttr(name => 'some_obj', class => 'Some::Class');
158
159 # using our class
160 my $obj = My::Class::WithObjectAttr->new;
161 $obj->some_obj; # returns a Some::Class instance
162
163=head1 DESCRIPTION
164
c59b7a19 165This module allows you to build a variable package that contains a package
166template and can use it to build variant packages at runtime.
0c378352 167
c59b7a19 168Your variable package will export a subroutine which will build a variant
169package, combining its arguments with the template, and return the name of the
170new variant package.
171
172The implementation does not care about what kind of packages it builds, be they
173simple function exporters, classes, singletons or something entirely different.
0c378352 174
175=head2 Declaring a variable package
176
177There are two important parts to creating a variable package. You first
178have to give C<Package::Variant> some basic information about what kind of
abc0e4ce 179variant packages you want to provide, and how. The second part is implementing a
c59b7a19 180method which builds the components of the variant packages that use the user's
181arguments or cannot be provided with a static import.
0c378352 182
abc0e4ce 183=head3 Setting up the environment for building variants
0c378352 184
185When you C<use Package::Variant>, you pass along some arguments that
abc0e4ce 186describe how you intend to build your variants.
0c378352 187
188 use Package::Variant
189 importing => { $package => \@import_arguments, ... },
190 subs => [ @proxied_subroutine_names ];
191
efaab257 192The L</importing> option needs to be a hash or array reference with
193package names to be C<use>d as keys, and array references containing the
194import arguments as values. These packages will be imported into every new
c59b7a19 195variant package, to provide static functionality of the variant packages and to
196set up every declarative subroutine you require to build variants package
197components. The next option will allow you to use these functions. See
198L</importing> for more options. You can omit empty import argument lists when
199passing an array reference.
0c378352 200
201The L</subs> option is an array reference of subroutine names that are
202exported by the packages specified with L</importing>. These subroutines
abc0e4ce 203will be proxied from your variable package to the variant to be
0c378352 204generated.
205
206With L</importing> initializing your package and L</subs> declaring what
207subroutines you want to use to build a variant, you can now write a
208L</make_variant> method building your variants.
209
210=head3 Declaring a method to produce variants
211
c59b7a19 212Every time a user requests a new variant, a method named L</make_variant>
0c378352 213will be called with the name of the target package and the arguments from
214the user.
215
216It can then use the proxied subroutines declared with L</subs> to
abc0e4ce 217customize the variant package. An L</install> subroutine is exported as well
218allowing you to dynamically install methods into the variant package. If these
219options aren't flexible enough, you can use the passed name of the variant
0c378352 220package to do any other kind of customizations.
221
222 sub make_variant {
223 my ($class, $target, @arguments) = @_;
224 # ...
225 # customization goes here
226 # ...
227 }
228
abc0e4ce 229When the method is finished, the user will receive the name of the new variant
230package you just set up.
0c378352 231
232=head2 Using variable packages
233
234After your variable package is L<created|/Declaring a variable package>
c59b7a19 235your users can get a variant generator subroutine by simply importing
0c378352 236your package.
237
238 use My::Variant;
2b728d4c 239 my $new_variant_package = Variant(@variant_arguments);
abc0e4ce 240 # the variant package is now fully initialized and used
0c378352 241
abc0e4ce 242You can import the subroutine under a different name by specifying an C<as>
243argument.
0c378352 244
245=head2 Dynamic creation of variant packages
246
247For regular uses, the L<normal import|/Using variable packages> provides
abc0e4ce 248more than enough flexibility. However, if you want to create variants of
cfcf68cb 249dynamically determined packages, you can use the L</build_variant_of>
0c378352 250method.
251
abc0e4ce 252You can use this to create variants of other packages and pass arguments
253on to them to allow more modular and extensible variants.
0c378352 254
255=head1 OPTIONS
256
257These are the options that can be passed when importing
258C<Package::Variant>. They describe the environment in which the variants
259are created.
260
261 use Package::Variant
262 importing => { $package => \@import_arguments, ... },
263 subs => [ @proxied_subroutines ];
264
265=head2 importing
266
267This option is a hash reference mapping package names to array references
2b728d4c 268containing import arguments. The packages will be imported with the given
abc0e4ce 269arguments by every variant before the L</make_variant> method is asked
2b728d4c 270to create the package (this is done using L<Import::Into>).
0c378352 271
067e51ad 272If import order is important to you, you can also pass the C<importing>
efaab257 273arguments as a flat array reference:
067e51ad 274
275 use Package::Variant
efaab257 276 importing => [ 'PackageA', 'PackageB' ];
067e51ad 277
efaab257 278 # same as
279 use Package::Variant
280 importing => [ 'PackageA' => [], 'PackageB' => [] ];
281
282 # or
283 use Package::Variant
284 importing => { 'PackageA' => [], 'PackageB' => [] };
285
286The import method will be called even if the list of import arguments is
287empty or not specified,
067e51ad 288
bdc3f3ad 289If you just want to import a single package's default exports, you can
290also pass a string instead:
291
2b728d4c 292 use Package::Variant importing => 'Package';
bdc3f3ad 293
0c378352 294=head2 subs
295
296An array reference of strings listing the names of subroutines that should
297be proxied. These subroutines are expected to be installed into the new
298variant package by the modules imported with L</importing>. Subroutines
abc0e4ce 299with the same name will be available in your variable package, and will
0c378352 300proxy through to the newly created package when used within
301L</make_variant>.
302
303=head1 VARIABLE PACKAGE METHODS
304
305These are methods on the variable package you declare when you import
306C<Package::Variant>.
307
308=head2 make_variant
309
310 Some::Variant::Package->make_variant( $target, @arguments );
311
312B<You need to provide this method.> This method will be called for every
313new variant of your package. This method should use the subroutines
314declared in L</subs> to customize the new variant package.
315
316This is a class method receiving the C<$target> package and the
317C<@arguments> defining the requested variant.
318
319=head2 import
320
321 use Some::Variant::Package;
322 my $variant_package = Package( @arguments );
323
324This method is provided for you. It will allow a user to C<use> your
325package and receive a subroutine taking C<@arguments> defining the variant
326and returning the name of the newly created variant package.
327
0a7db8d2 328The following options can be specified when importing:
329
330=over
331
332=item * B<as>
333
334 use Some::Variant::Package as => 'Foo';
2b728d4c 335 my $variant_package = Foo(@arguments);
0a7db8d2 336
337Exports the generator subroutine under a different name than the default.
338
339=back
340
5a0662d9 341=head2 build_variant
342
343 use Some::Variant::Package ();
344 my $variant_package = Some::Variant::Package->build_variant( @arguments );
345
346This method is provided for you. It will generate a variant package
347and return its name, just like the generator sub provided by
348L</import>. This allows you to avoid importing anything into the
349consuming package.
350
0c378352 351=head1 C<Package::Variant> METHODS
352
353These methods are available on C<Package::Variant> itself.
354
cfcf68cb 355=head2 build_variant_of
0c378352 356
357 my $variant_package = Package::Variant
cfcf68cb 358 ->build_variant_of($variable_package, @arguments);
0c378352 359
360This is the dynamic method of creating new variants. It takes the
361C<$variable_package>, which is a pre-declared variable package, and a set
362of C<@arguments> passed to the package to generate a new
363C<$variant_package>, which will be returned.
364
365=head2 import
366
367 use Package::Variant @options;
368
369Sets up the environment in which you declare the variants of your
370packages. See L</OPTIONS> for details on the available options and
371L</EXPORTS> for a list of exported subroutines.
372
373=head1 EXPORTS
374
375Additionally to the proxies for subroutines provided in L</subs>, the
376following exports will be available in your variable package:
377
378=head2 install
379
2b728d4c 380 install($method_name, $code_reference);
0c378352 381
382Installs a method with the given C<$method_name> into the newly created
383variant package. The C<$code_reference> will be used as the body for the
2b728d4c 384method, and if L<Sub::Name> is available the coderef will be named. If you
385want to name it something else, then use:
386
387 install($method_name, $name_to_use, $code_reference);
0c378352 388
389=head1 AUTHOR
390
5b1d922a 391mst - Matt S. Trout (cpan:MSTROUT) <mst@shadowcat.co.uk>
0c378352 392
5b1d922a 393=head1 CONTRIBUTORS
0c378352 394
5b1d922a 395phaylon - Robert Sedlacek (cpan:PHAYLON) <r.sedlacek@shadowcat.co.uk>
0c378352 396
39c3689b 397haarg - Graham Knop (cpan:HAARG) <haarg@haarg.org>
398
0c378352 399=head1 COPYRIGHT
400
2b728d4c 401Copyright (c) 2010-2012 the C<Package::Variant> L</AUTHOR> and
57114c43 402L</CONTRIBUTORS> as listed above.
0c378352 403
404=head1 LICENSE
405
406This library is free software and may be distributed under the same
407terms as perl itself.
408
409=cut