File Coverage

blib/lib/Config/ENV/Multi.pm
Criterion Covered Total %
statement 157 168 93.4
branch 39 52 75.0
condition 7 8 87.5
subroutine 35 38 92.1
pod 0 9 0.0
total 238 275 86.5


line stmt bran cond sub pod time code
1             package Config::ENV::Multi;
2 12     12   72624 use 5.008001;
  12         49  
3 12     12   66 use strict;
  12         28  
  12         239  
4 12     12   68 use warnings;
  12         25  
  12         350  
5 12     12   74 use Carp qw/croak/;
  12         34  
  12         1204  
6              
7             our $VERSION = "0.04";
8              
9 12     12   94 use constant DELIMITER => '@#%@#';
  12         37  
  12         1213  
10              
11             sub import {
12 15     15   22519 my $class = shift;
13 15         52 my $package = caller(0);
14              
15 12     12   96 no strict 'refs';
  12         23  
  12         2072  
16 15 100       79 if (__PACKAGE__ eq $class) {
17 13         63 my $envs = shift;
18 13         56 my %opts = @_;
19             #
20             # rule => '{ENV}_{REGION}',
21             # any => '*',
22             # unset => '&';
23             #
24              
25 13         35 push @{"$package\::ISA"}, __PACKAGE__;
  13         178  
26              
27 13         84 for my $method (qw/common config any unset parent load/) {
28 78         153 *{"$package\::$method"} = \&{__PACKAGE__ . "::" . $method}
  78         350  
  78         258  
29             }
30              
31 13         65 my %wildcard = (
32             any => '*',
33             unset => '!',
34             );
35 13 100       75 $wildcard{any} = $opts{any} if $opts{any};
36 13 100       72 $wildcard{unset} = $opts{unset} if $opts{unset};
37              
38 13 100       85 $envs = [$envs] unless ref $envs;
39 13 100       99 my $mode = $opts{rule} ? 'rule': 'env';
40              
41 12     12   83 no warnings 'once';
  12         32  
  12         8441  
42 13         4478 ${"$package\::data"} = +{
43             configs => {},
44             mode => $mode, # env or rule
45             envs => $envs,
46             rule => $opts{rule},
47             wildcard => \%wildcard,
48             cache => {},
49             local => [],
50             export => $opts{export},
51 13         134 };
52             } else {
53 2         9 my %opts = @_;
54 2         11 my $data = _data($class);
55 2 100 66     73 if (my $export = $opts{export} || $data->{export}) {
56 1     0   45 *{"$package\::$export"} = sub () { $class };
  1         37  
  0         0  
57             }
58             }
59             }
60              
61             # copy from Config::ENV
62             sub load ($) { ## no critic
63 0     0 0 0 my $filename = shift;
64 0         0 my $hash = do "$filename";
65              
66 0 0       0 croak $@ if $@;
67 0 0       0 croak $^E unless defined $hash;
68 0 0       0 unless (ref($hash) eq 'HASH') {
69 0         0 croak "$filename does not return HashRef.";
70             }
71              
72 0 0       0 wantarray ? %$hash : $hash;
73             }
74              
75             sub parent ($) { ## no critic
76 1     1 0 14 my $package = caller(0);
77 1         3 my $e_or_r = shift;
78              
79 1         2 my $target;
80 1         7 my $data = _data($package);
81 1 50       7 if ($data->{mode} eq 'env') {
82 1         3 $target = __envs2key($e_or_r);
83             } else {
84 0         0 $target = __envs2key(__clip_rule($data->{rule}, $e_or_r));
85             }
86 1 50       3 %{ $data->{configs}{$target}->as_hashref || {} };
  1         8  
87             }
88              
89             sub any {
90 1     1 0 10 my $package = caller(0);
91 1         7 _data($package)->{wildcard}{any};
92             }
93              
94             sub unset {
95 2     2 0 7 my $package = caller(0);
96 2         6 _data($package)->{wildcard}{unset};
97             }
98              
99             # {ENV}_{REGION}
100             # => ['ENV', 'REGION]
101             sub __parse_rule {
102 3     3   5035 my $rule = shift;
103             return [
104 11 100       42 grep { defined && length }
105             map {
106 11 100       40 /^\{(.+?)\}$/ ? $1 : undef
107             }
108 3 50       24 grep { defined && length }
  14         50  
109             split /(\{.+?\})/, $rule
110             ];
111             }
112              
113             # {ENV}_{REGION} + 'prod_jp'
114             # => ['prod', 'jp']
115             sub __clip_rule {
116 6     6   3984 my ($template, $rule) = @_;
117             my $spliter = [
118 20 100       87 grep { defined && length }
119             map {
120 20 100       90 /^\{(.+?)\}$/ ? undef : $_
121             }
122 6 50       51 grep { defined && length }
  26         117  
123             split /(\{.+?\})/, $template
124             ];
125 6         32 my $pattern = '(.*)' . ( join '(.*)', @{$spliter} ) . '(.*)';
  6         24  
126 6         83 my @clip = ( $rule =~ /$pattern/g );
127 6         31 return \@clip;
128             }
129              
130             sub _data {
131 160     160   310 my $package = shift;
132 12     12   100 no strict 'refs';
  12         28  
  12         391  
133 12     12   75 no warnings 'once';
  12         36  
  12         9321  
134 160         308 ${"$package\::data"};
  160         698  
135             }
136              
137             sub common {
138 8     8 0 105 my $package = caller(0);
139 8         23 my $hash = shift;
140 8         36 my $data = _data($package);
141 8         33 my $envs = $data->{envs};
142 8 50       53 $envs = [$envs] unless ref $envs;
143 8         29 my $any = $data->{wildcard}{any};
144 8         26 _config_env($package, [ map { "$any" } @{$envs} ], $hash);
  16         78  
  8         28  
145             }
146              
147             sub config {
148 25     25 0 199 my $package = caller(0);
149 25 100       68 if (_data($package)->{mode} eq 'env') {
150 22         89 return _config_env($package, @_);
151             } else {
152 3         14 return _config_rule($package, @_);
153             }
154             }
155              
156             sub _config_env {
157 33     33   98 my ($package, $envs, $hash) = @_;
158              
159 33         80 my $data = _data($package);
160 33         86 my $wildcard = $data->{wildcard};
161 33 100       137 $envs = [ $envs ] unless ref $envs;
162              
163             $data->{configs}{__envs2key($envs)} = Config::ENV::Multi::ConfigInstance->new(
164 33         101 order => 0 + ( grep { $_ ne $wildcard->{any} } @$envs ),
  64         299  
165             pattern => $envs,
166             hash => $hash,
167             wildcard => $wildcard,
168             );
169             }
170              
171             sub _config_rule {
172 3     3   13 my ($package, $rule, $hash) = @_;
173 3         12 _config_env($package, __clip_rule(_data($package)->{rule}, $rule), $hash);
174             }
175              
176             sub current {
177 40     40 0 22929 my $package = shift;
178 40         129 my $data = _data($package);
179              
180 40         93 my $target_env = [ map { $ENV{$_} } @{ $data->{envs} } ];
  76         272  
  40         120  
181              
182             my $vals = $data->{cache}->{__envs2key($target_env)} ||= +{
183 40   100     144 %{ _match($package, $target_env) }
  38         130  
184             };
185             }
186              
187             sub local :method {
188 8     8 0 47 my ($package, %hash) = @_;
189 8 100       159 not defined wantarray and croak "local returns guard object; Can't use in void context.";
190              
191 7         17 my $data = _data($package);
192 7         10 push @{ $data->{local} }, \%hash;
  7         15  
193 7         12 %{ $data->{cache} } = ();
  7         18  
194              
195             bless sub {
196 7     7   15 @{ $data->{local} } = grep { $_ != \%hash } @{ $data->{local} };
  7         17  
  12         39  
  7         17  
197 7         12 %{ $data->{cache} } = ();
  7         46  
198 7         37 }, 'Config::ENV::Multi::Local';
199             }
200              
201             sub param {
202 14     14 0 81 my ($package, $name) = @_;
203 14         38 $package->current->{$name};
204             }
205              
206             sub __envs2key {
207 77     77   4180 my $v = shift;
208 77 100       259 $v = [$v] unless ref $v;
209 77 100       147 join DELIMITER(), map { defined $_ ? $_ : '' } @{$v};
  149         790  
  77         173  
210             }
211              
212             sub __key2envs {
213 0     0   0 my $f = shift;
214 0         0 [split DELIMITER(), $f];
215             }
216              
217             sub _match {
218 38     38   100 my ( $package, $target_envs ) = @_;
219              
220 38         98 my $data = _data($package);
221              
222             return +{
223 75         327 (map { %{ $_->as_hashref } }
  75         198  
224 163         1112 grep { $_->match($target_envs) }
225 199         482 sort { $a->{order} - $b->{order} }
226 38         201 values %{ $data->{configs} }),
227 38         94 (map { %$_ } @{ $data->{local} })
  17         144  
  38         465  
228             };
229             }
230              
231             1;
232              
233             package Config::ENV::Multi::ConfigInstance;
234 12     12   182 use strict;
  12         36  
  12         292  
235 12     12   63 use warnings;
  12         27  
  12         404  
236              
237 12     12   7238 use List::MoreUtils qw/ all pairwise /;
  12         123770  
  12         138  
238              
239             sub new {
240 33     33   148 my ( $class, %args ) = @_;
241              
242             bless +{
243             order => $args{order},
244             pattern => $args{pattern},
245             hash => $args{hash},
246             wildcard => $args{wildcard},
247 33         266 }, $class;
248             }
249              
250             sub match {
251 163     163   358 my ( $self, $target ) = @_;
252              
253 245     245   1601 return all { $_ } pairwise {
254             $a eq $self->{wildcard}{any} ? 1 :
255 319 100 100 319   6287 $a eq $self->{wildcard}{unset} ? !defined $b :
    100          
256             defined $b && $b eq $a;
257 163         674 } @{ $self->{pattern} }, @{ $target };
  163         386  
  163         487  
258             }
259              
260 76     76   333 sub as_hashref { $_[0]->{hash} }
261              
262             package # to hide from pause
263             Config::ENV::Multi::Local;
264              
265             sub DESTROY {
266 7     7   1869 my $self = shift;
267 7         24 $self->();
268             }
269              
270             1;
271              
272             __END__