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