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