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