File Coverage

blib/lib/Package/Variant.pm
Criterion Covered Total %
statement 78 79 98.7
branch 5 6 83.3
condition n/a
subroutine 21 22 95.4
pod 1 1 100.0
total 105 108 97.2


line stmt bran cond sub pod time code
1             package Package::Variant;
2              
3 5     5   203921 use strictures 1;
  5         41  
  5         158  
4 5     5   4691 use Import::Into;
  5         16570  
  5         170  
5 5     5   42 use Module::Runtime qw(require_module);
  5         14  
  5         27  
6 5     5   389 use Carp qw(croak);
  5         10  
  5         2992  
7              
8             our $VERSION = '1.002002';
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 11     11   2843 my $variable = caller;
58 11         18 my $me = shift;
59 11         43 my $last = (split '::', $variable)[-1];
60 11         27 my $anon = 'A000';
61 11         31 my %args = @_;
62 5     5   29 no strict 'refs';
  5         10  
  5         800  
63             $Variable{$variable} = {
64             anon => $anon,
65             args => {
66             %args,
67             importing => $me->$sanitize_importing($args{importing}),
68             },
69             subs => {
70 11 100   0   51 map +($_ => sub {}), @{$args{subs}||[]},
  0         0  
  7         81  
71             },
72             };
73 7         37 *{"${variable}::import"} = sub {
74 7     7   2847 my $target = caller;
75 7         22 my (undef, %arg) = @_;
76 7 100       49 my $as = defined($arg{as}) ? $arg{as} : $last;
77 5     5   26 no strict 'refs';
  5         8  
  5         2977  
78 7         8141 *{"${target}::${as}"} = sub {
79 9     9   14229 $me->build_variant_of($variable, @_);
80 7         36 };
81 7         36 };
82 7         18 my $subs = $Variable{$variable}{subs};
83 7         21 foreach my $name (keys %$subs) {
84 5         27 *{"${variable}::${name}"} = sub {
85 7     7   1109 goto &{$subs->{$name}}
  7         24  
86 5         18 };
87             }
88 7         30 *{"${variable}::install"} = sub {
89 10     10   20536 goto &{$Variable{$variable}{install}};
  10         41  
90 7         25 };
91 7         947 *{"${variable}::build_variant"} = sub {
92 1     1   552 shift;
93 1         7 $me->build_variant_of($variable, @_);
94 7         31 };
95             }
96              
97             sub build_variant_of {
98 11     11 1 45 my ($me, $variable, @args) = @_;
99 11         61 my $variant_name = "${variable}::_Variant_".++$Variable{$variable}{anon};
100 11         34 foreach my $to_import (@{$Variable{$variable}{args}{importing}}) {
  11         46  
101 8         17 my ($pkg, $args) = @$to_import;
102 8         29 require_module $pkg;
103 3 50   3   7 eval q{ BEGIN { $pkg->import::into($variant_name, @{$args}) }; 1; }
  3     1   27  
  1     1   2  
  1     1   6  
  1     1   2  
  1     1   8  
  1         2  
  1         7  
  1         2  
  1         6  
  1         3  
  1         6  
  8         839  
104             or die $@;
105             }
106 11         185 my $subs = $Variable{$variable}{subs};
107 11         75 local @{$subs}{keys %$subs} = map $variant_name->can($_), keys %$subs;
  11         48  
108             local $Variable{$variable}{install} = sub {
109 10     10   37 my $full_name = "${variant_name}::".shift;
110              
111 10         51 my $ref = $sub_namer->($full_name, @_);
112            
113 5     5   29 no strict 'refs';
  5         10  
  5         559  
114 10         83 *$full_name = $ref;
115 11         75 };
116 11         60 $variable->make_variant($variant_name, @args);
117 11         137 return $variant_name;
118             }
119              
120             1;
121              
122             __END__