make the docs more consistent in where it refers to variable/variant
[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
0012971d 8our $VERSION = '1.002000';
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
abc0e4ce 165This module allows you to build a variable package that returns different
166variant packages depending on what parameters are given.
0c378352 167
abc0e4ce 168Users of your variable package will receive a subroutine able to take parameters
328258e0 169and return the name of a suitable variant package. The implementation does
abc0e4ce 170not care about what kind of variant package it builds.
0c378352 171
172=head2 Declaring a variable package
173
174There are two important parts to creating a variable package. You first
175have to give C<Package::Variant> some basic information about what kind of
abc0e4ce 176variant packages you want to provide, and how. The second part is implementing a
177method receiving the user's arguments and generating your variant packages.
0c378352 178
abc0e4ce 179=head3 Setting up the environment for building variants
0c378352 180
181When you C<use Package::Variant>, you pass along some arguments that
abc0e4ce 182describe how you intend to build your variants.
0c378352 183
184 use Package::Variant
185 importing => { $package => \@import_arguments, ... },
186 subs => [ @proxied_subroutine_names ];
187
efaab257 188The L</importing> option needs to be a hash or array reference with
189package names to be C<use>d as keys, and array references containing the
190import arguments as values. These packages will be imported into every new
abc0e4ce 191variant package, and need to set up every declarative subroutine you require to
0c378352 192build your variable package. The next option will allow you to use these
efaab257 193functions. See L</importing> for more options. You can omit empty import
194argument lists when passing an array reference.
0c378352 195
196The L</subs> option is an array reference of subroutine names that are
197exported by the packages specified with L</importing>. These subroutines
abc0e4ce 198will be proxied from your variable package to the variant to be
0c378352 199generated.
200
201With L</importing> initializing your package and L</subs> declaring what
202subroutines you want to use to build a variant, you can now write a
203L</make_variant> method building your variants.
204
205=head3 Declaring a method to produce variants
206
207Every time a user requests a new variant a method named L</make_variant>
208will be called with the name of the target package and the arguments from
209the user.
210
211It can then use the proxied subroutines declared with L</subs> to
abc0e4ce 212customize the variant package. An L</install> subroutine is exported as well
213allowing you to dynamically install methods into the variant package. If these
214options aren't flexible enough, you can use the passed name of the variant
0c378352 215package to do any other kind of customizations.
216
217 sub make_variant {
218 my ($class, $target, @arguments) = @_;
219 # ...
220 # customization goes here
221 # ...
222 }
223
abc0e4ce 224When the method is finished, the user will receive the name of the new variant
225package you just set up.
0c378352 226
227=head2 Using variable packages
228
229After your variable package is L<created|/Declaring a variable package>
230your users can get a variant generating subroutine by simply importing
231your package.
232
233 use My::Variant;
2b728d4c 234 my $new_variant_package = Variant(@variant_arguments);
abc0e4ce 235 # the variant package is now fully initialized and used
0c378352 236
abc0e4ce 237You can import the subroutine under a different name by specifying an C<as>
238argument.
0c378352 239
240=head2 Dynamic creation of variant packages
241
242For regular uses, the L<normal import|/Using variable packages> provides
abc0e4ce 243more than enough flexibility. However, if you want to create variants of
cfcf68cb 244dynamically determined packages, you can use the L</build_variant_of>
0c378352 245method.
246
abc0e4ce 247You can use this to create variants of other packages and pass arguments
248on to them to allow more modular and extensible variants.
0c378352 249
250=head1 OPTIONS
251
252These are the options that can be passed when importing
253C<Package::Variant>. They describe the environment in which the variants
254are created.
255
256 use Package::Variant
257 importing => { $package => \@import_arguments, ... },
258 subs => [ @proxied_subroutines ];
259
260=head2 importing
261
262This option is a hash reference mapping package names to array references
2b728d4c 263containing import arguments. The packages will be imported with the given
abc0e4ce 264arguments by every variant before the L</make_variant> method is asked
2b728d4c 265to create the package (this is done using L<Import::Into>).
0c378352 266
067e51ad 267If import order is important to you, you can also pass the C<importing>
efaab257 268arguments as a flat array reference:
067e51ad 269
270 use Package::Variant
efaab257 271 importing => [ 'PackageA', 'PackageB' ];
067e51ad 272
efaab257 273 # same as
274 use Package::Variant
275 importing => [ 'PackageA' => [], 'PackageB' => [] ];
276
277 # or
278 use Package::Variant
279 importing => { 'PackageA' => [], 'PackageB' => [] };
280
281The import method will be called even if the list of import arguments is
282empty or not specified,
067e51ad 283
bdc3f3ad 284If you just want to import a single package's default exports, you can
285also pass a string instead:
286
2b728d4c 287 use Package::Variant importing => 'Package';
bdc3f3ad 288
0c378352 289=head2 subs
290
291An array reference of strings listing the names of subroutines that should
292be proxied. These subroutines are expected to be installed into the new
293variant package by the modules imported with L</importing>. Subroutines
abc0e4ce 294with the same name will be available in your variable package, and will
0c378352 295proxy through to the newly created package when used within
296L</make_variant>.
297
298=head1 VARIABLE PACKAGE METHODS
299
300These are methods on the variable package you declare when you import
301C<Package::Variant>.
302
303=head2 make_variant
304
305 Some::Variant::Package->make_variant( $target, @arguments );
306
307B<You need to provide this method.> This method will be called for every
308new variant of your package. This method should use the subroutines
309declared in L</subs> to customize the new variant package.
310
311This is a class method receiving the C<$target> package and the
312C<@arguments> defining the requested variant.
313
314=head2 import
315
316 use Some::Variant::Package;
317 my $variant_package = Package( @arguments );
318
319This method is provided for you. It will allow a user to C<use> your
320package and receive a subroutine taking C<@arguments> defining the variant
321and returning the name of the newly created variant package.
322
0a7db8d2 323The following options can be specified when importing:
324
325=over
326
327=item * B<as>
328
329 use Some::Variant::Package as => 'Foo';
2b728d4c 330 my $variant_package = Foo(@arguments);
0a7db8d2 331
332Exports the generator subroutine under a different name than the default.
333
334=back
335
5a0662d9 336=head2 build_variant
337
338 use Some::Variant::Package ();
339 my $variant_package = Some::Variant::Package->build_variant( @arguments );
340
341This method is provided for you. It will generate a variant package
342and return its name, just like the generator sub provided by
343L</import>. This allows you to avoid importing anything into the
344consuming package.
345
0c378352 346=head1 C<Package::Variant> METHODS
347
348These methods are available on C<Package::Variant> itself.
349
cfcf68cb 350=head2 build_variant_of
0c378352 351
352 my $variant_package = Package::Variant
cfcf68cb 353 ->build_variant_of($variable_package, @arguments);
0c378352 354
355This is the dynamic method of creating new variants. It takes the
356C<$variable_package>, which is a pre-declared variable package, and a set
357of C<@arguments> passed to the package to generate a new
358C<$variant_package>, which will be returned.
359
360=head2 import
361
362 use Package::Variant @options;
363
364Sets up the environment in which you declare the variants of your
365packages. See L</OPTIONS> for details on the available options and
366L</EXPORTS> for a list of exported subroutines.
367
368=head1 EXPORTS
369
370Additionally to the proxies for subroutines provided in L</subs>, the
371following exports will be available in your variable package:
372
373=head2 install
374
2b728d4c 375 install($method_name, $code_reference);
0c378352 376
377Installs a method with the given C<$method_name> into the newly created
378variant package. The C<$code_reference> will be used as the body for the
2b728d4c 379method, and if L<Sub::Name> is available the coderef will be named. If you
380want to name it something else, then use:
381
382 install($method_name, $name_to_use, $code_reference);
0c378352 383
384=head1 AUTHOR
385
5b1d922a 386mst - Matt S. Trout (cpan:MSTROUT) <mst@shadowcat.co.uk>
0c378352 387
5b1d922a 388=head1 CONTRIBUTORS
0c378352 389
5b1d922a 390phaylon - Robert Sedlacek (cpan:PHAYLON) <r.sedlacek@shadowcat.co.uk>
0c378352 391
39c3689b 392haarg - Graham Knop (cpan:HAARG) <haarg@haarg.org>
393
0c378352 394=head1 COPYRIGHT
395
2b728d4c 396Copyright (c) 2010-2012 the C<Package::Variant> L</AUTHOR> and
57114c43 397L</CONTRIBUTORS> as listed above.
0c378352 398
399=head1 LICENSE
400
401This library is free software and may be distributed under the same
402terms as perl itself.
403
404=cut