File Coverage

blib/lib/Package/Variant.pm
Criterion Covered Total %
statement 69 72 95.8
branch 5 8 62.5
condition n/a
subroutine 16 18 88.8
pod 1 2 50.0
total 91 100 91.0


line stmt bran cond sub pod time code
1             package Package::Variant;
2              
3 3     3   97716 use strictures 2;
  3         26  
  3         143  
4 3     3   2634 use Import::Into;
  3         5607  
  3         106  
5 3     3   19 use Module::Runtime qw(require_module);
  3         10  
  3         10  
6 3     3   131 use Carp qw(croak);
  3         4  
  3         1531  
7              
8             our $VERSION = '1.003002';
9              
10             $VERSION = eval $VERSION;
11              
12             our %Variable;
13              
14             my $sanitize_importing = sub {
15             my ($me, $spec) = @_;
16             return []
17             unless defined $spec;
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};
30             my @imports;
31             my $arg_count = 1;
32             while (@specced) {
33             my $key = shift @specced;
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];
48             }
49             return \@imports;
50             };
51              
52             my $sub_namer = eval {
53             require Sub::Name; sub { shift if @_ > 2; Sub::Name::subname(@_) }
54             } || sub { $_[-1] };
55              
56             sub import {
57 3     3   68 my $variable = caller;
58 3         5 my $me = shift;
59 3         12 my $last = (split '::', $variable)[-1];
60 3         6 my $anon = 'A000';
61 3         8 my %args = @_;
62 3     3   17 no strict 'refs';
  3         4  
  3         463  
63             $Variable{$variable} = {
64             anon => $anon,
65             args => {
66             %args,
67             importing => $me->$sanitize_importing($args{importing}),
68             },
69             subs => {
70 3 100   0   14 map +($_ => sub {}), @{$args{subs}||[]},
  3         35  
71             },
72             };
73 3         20 *{"${variable}::import"} = sub {
74 3     3   229 my $target = caller;
75 3         11 my (undef, %arg) = @_;
76 3 50       25 my $as = defined($arg{as}) ? $arg{as} : $last;
77 3     3   15 no strict 'refs';
  3         4  
  3         2593  
78 3         1834 *{"${target}::${as}"} = sub {
79 5     5   6208 $me->build_variant_of($variable, @_);
80 3         12 };
81 3         24 };
82 3         7 my $subs = $Variable{$variable}{subs};
83 3         9 foreach my $name (keys %$subs) {
84 4         17 *{"${variable}::${name}"} = sub {
85 1     1   6 goto &{$subs->{$name}}
  1         4  
86 4         11 };
87             }
88 3         13 *{"${variable}::install"} = sub {
89 5     5   13819 goto &{$Variable{$variable}{install}};
  5         21  
90 3         10 };
91 3         361 *{"${variable}::build_variant"} = sub {
92 0     0   0 shift;
93 0         0 $me->build_variant_of($variable, @_);
94 3         9 };
95             }
96              
97             sub build_variant_package_name {
98 5     5 0 8 my ($me, $variable, @args) = @_;
99 5 50       46 if ($variable->can('make_variant_package_name')) {
100 0         0 return $variable->make_variant_package_name(@args);
101             }
102 5         26 return "${variable}::_Variant_".++$Variable{$variable}{anon};
103             }
104              
105             sub build_variant_of {
106 5     5 1 14 my ($me, $variable, @args) = @_;
107 5         15 my $variant_name = $me->build_variant_package_name($variable, @args);
108 5         7 foreach my $to_import (@{$Variable{$variable}{args}{importing}}) {
  5         19  
109 2         6 my ($pkg, $args) = @$to_import;
110 2         10 require_module $pkg;
111 2 50   2   205 eval q{ BEGIN { $pkg->import::into($variant_name, @{$args}) }; 1; }
  2         3  
  2         14  
112             or die $@;
113             }
114 5         10 my $subs = $Variable{$variable}{subs};
115 5         27 local @{$subs}{keys %$subs} = map $variant_name->can($_), keys %$subs;
  5         9  
116             local $Variable{$variable}{install} = sub {
117 5     5   41 my $full_name = "${variant_name}::".shift;
118              
119 5         12 my $ref = $sub_namer->($full_name, @_);
120            
121 3     3   18 no strict 'refs';
  3         5  
  3         305  
122 5         38 *$full_name = $ref;
123 5         27 };
124 5         17 $variable->make_variant($variant_name, @args);
125 5         92 return $variant_name;
126             }
127              
128             1;
129              
130             __END__