File Coverage

blib/lib/Config/Environment.pm
Criterion Covered Total %
statement 140 155 90.3
branch 38 58 65.5
condition 10 17 58.8
subroutine 16 16 100.0
pod 5 10 50.0
total 209 256 81.6


line stmt bran cond sub pod time code
1             # ABSTRACT: Application Configuration via Environment Variables
2             package Config::Environment;
3              
4 9     9   231826 use utf8;
  9         21  
  9         69  
5 9     9   342 use 5.10.0;
  9         34  
  9         437  
6              
7 9     9   13063 use Moo;
  9         198541  
  9         56  
8 9     9   25358 use Hash::Flatten ();
  9         55289  
  9         245  
9 9     9   10260 use Hash::Merge::Simple ();
  9         15201  
  9         33641  
10              
11             our $VERSION = '0.000010'; # VERSION
12              
13              
14             sub BUILDARGS {
15 38     38 0 31269 my ($class, @args) = @_;
16              
17 38 100 66     280 unshift @args, 'domain' if $args[0] && $#args == 0;
18 38         1015 return {@args};
19             }
20              
21             sub BUILD {
22 38     38 0 791 my ($self) = @_;
23              
24 38         114 $self->{domain} = lc $self->{domain};
25 38 100       215 if ($self->{domain} =~ s/[^a-zA-Z0-9]+/_/g) {
26 7         35 my ($dom, $subdom) = split /_/, $self->{domain}, 2;
27 7         16 $self->{domain} = $dom;
28 7         29 $self->{subdomain} = $self->to_sub_key($subdom);
29             }
30              
31 38         115 my $dom = $self->domain;
32 38         277 $self->{snapshot} = { map {$_ => $ENV{$_}} grep { /^$dom\_/i } keys %ENV };
  153         481  
  989         3029  
33 38 100       1329 return $self->load({%ENV}) if $self->autoload;
34             }
35              
36              
37             has autoload => (
38             is => 'ro',
39             required => 0,
40             default => 1
41             );
42              
43              
44             has domain => (
45             is => 'ro',
46             required => 1
47             );
48              
49              
50             has lifecycle => (
51             is => 'ro',
52             required => 0,
53             default => 0
54             );
55              
56              
57             has mirror => (
58             is => 'rw',
59             required => 0,
60             default => 1
61             );
62              
63              
64             has override => (
65             is => 'rw',
66             required => 0,
67             default => 1
68             );
69              
70              
71             has stash => (
72             is => 'ro',
73             required => 0,
74             default => sub {{}}
75             );
76              
77              
78             sub load {
79 84     84 1 132 my ($self, $hash) = @_;
80 84         226 my $dom = lc $self->domain;
81 84         108 my $env = { map {$_ => $hash->{$_}} grep { /^$dom\_/i } keys %{$hash} };
  117         410  
  469         1542  
  84         283  
82 84   100     429 my $reg = $self->{registry} //= {env => {}, map => {}};
83 84         150 my $map = $reg->{map};
84              
85 84         106 for my $key (sort keys %{$env}) {
  84         317  
86 117         247 my $value = delete $env->{$key};
87              
88 117         483 $key =~ s/_/./g;
89 117         641 $key =~ s/^$dom\.//gi;
90              
91 117         340 my $hash = {lc $key => $value};
92              
93 117 50       373 if (ref $value) {
94 0 0       0 if ('ARRAY' eq ref $value) {
95 0         0 my $i = 0;
96 0         0 $value = { map { ++$i => $_ } @{$value} };
  0         0  
  0         0  
97             }
98              
99 0         0 $hash = Hash::Flatten->new->flatten($value);
100              
101 0         0 for my $refkey (keys %{$hash}) {
  0         0  
102 0         0 (my $newref = $refkey) =~ s/(\w):(\d+)/"$1.".($2+1)/gpe;
  0         0  
103 0         0 $hash->{lc "$key.$newref"} = delete $hash->{$refkey};
104             }
105             }
106              
107 117         462 $map = Hash::Merge::Simple->merge(
108             $map => Hash::Flatten->new->unflatten($hash)
109             );
110              
111 117 100       21192 if ($self->mirror) {
112 112         147 while (my($key, $val) = each(%{$hash})) {
  224         976  
113 112         282 $ENV{$self->to_env_key($key)} = $val;
114             }
115             }
116             }
117              
118 84         212 $reg->{map} = $map;
119              
120 84         722 return $self;
121             }
122              
123              
124             sub param {
125 148     148 1 11395 my ($self, $key, $val) = @_;
126              
127 148 50       581 return unless defined $key;
128              
129 148         311 my $dom = $self->domain;
130              
131 148         396 $key = $self->to_dom_key($key);
132 148         865 $key =~ s/^$dom(\.)?//;
133              
134 148 100       384 if (@_ > 2) {
135 62         277 my $pairs = Hash::Flatten::flatten({$key => $val});
136 62         11557 while (my($key, $val) = each(%{$pairs})) {
  132         502  
137 70         154 $key =~ s/(\w):(\d+)/"$1.".($2+1)/gpe;
  10         48  
138 70         222 $key =~ s/\\//g;
139 70 100 100     207 unless (exists $ENV{$self->to_env_key($key)} && ! $self->override) {
140 68         140 $self->load({$self->to_env_key($key) => $val});
141 68         323 $self->{registry}{env}{$key} = $val;
142             }
143             }
144             }
145              
146 148         245 my $result;
147              
148             # env lookup
149 148 100       450 if (exists $self->{registry}{env}{$key}) {
150 108         225 $result = $self->{registry}{env}{$key};
151             }
152              
153             # env map walk
154 148 100       305 if (!$result) {
155 40         70 my $node = $self->{registry}{map};
156 40         129 my @steps = split /\./, $key;
157 40         121 for (my $i=0; $i<@steps; $i++) {
158 108         143 my $step = $steps[$i];
159 108 100       212 if (exists $node->{$step}) {
160 73 50 33     337 if ($i<@steps && 'HASH' ne ref $node) {
161 0 0       0 undef $node and last;
162             }
163 73         230 $node = $node->{$step};
164             }
165             else {
166 35 50       127 undef $node and last;
167             }
168             }
169 40         88 $result = $node;
170             }
171              
172             # stash walk
173 148 100       285 if (!$result) {
174 11         38 my $key = join '.', grep defined, $self->{subdomain}, $_[1]; #hack
175 11         64 $key =~ s/\.(\d+)\./".".($1-1)."."/gpe;
  11         47  
176 11 50       44 unless ($result = $self->stash->{$key}) {
177 11         19 my $node = $self->stash;
178 11         36 my @steps = split /\./, $key;
179 11         33 for (my $i=0; $i<@steps; $i++) {
180 35         44 my $step = $steps[$i];
181 35 100       84 if ('ARRAY' eq ref $node) {
    50          
182 11 50 33     48 if ($i<@steps && !defined $node->[$step]) {
183 0 0       0 undef $node and last;
184             }
185             else {
186 11         29 $node = $node->[$step];
187             }
188             }
189             elsif ('HASH' eq ref $node) {
190 24 50 33     115 if ($i<@steps && !defined $node->{$step}) {
191 0 0       0 undef $node and last;
192             }
193             else {
194 24         75 $node = $node->{$step};
195             }
196             }
197             else {
198 0 0       0 undef $node and last;
199             }
200             }
201 11         30 $result = $node;
202             }
203             }
204              
205 148         628 return $result;
206             }
207              
208              
209             sub params {
210 2     2 1 10 my ($self, @keys) = @_;
211              
212 2 100       15 if ($#keys == 0) {
213 1 50       8 if ('HASH' eq ref $keys[0]) {
214 1         3 while (my ($key, $value) = each%{$keys[0]}) {
  2         10  
215 1         3 $self->param($key, $value);
216             }
217 1         5 return;
218             }
219             }
220              
221 1         3 my @vals = map { $self->param($_) } @keys;
  2         5  
222 1 50       7 return wantarray ? @vals : $vals[0];
223             }
224              
225              
226             sub environment {
227 4     4 1 684 my ($self) = @_;
228 4         24 my $map = Hash::Merge::Simple->merge(
229             Hash::Flatten->new->flatten($self->{registry}{map}),
230             Hash::Flatten->new->flatten($self->stash),
231             );
232              
233 4         2712 for my $key (keys %{$map}) {
  4         17  
234 13         40 $map->{$self->to_env_key($key)} = delete $map->{$key};
235             }
236              
237 4         14 return $map;
238             }
239              
240              
241             sub subdomain {
242 22     22 1 5146 my ($self, $key) = @_;
243 22         60 my $dom = $self->domain;
244 22         733 my $copy = ref($self)->new(
245             autoload => 0,
246             override => $self->override,
247             lifecycle => $self->lifecycle,
248             mirror => $self->mirror,
249             stash => $self->stash,
250             domain => $dom
251             );
252              
253 22         164 $copy->{subdomain} = $self->to_sub_key($key);
254 22         53 $copy->{registry} = $self->{registry};
255              
256 22         72 return $copy;
257             }
258              
259             sub to_dom_key {
260 177     177 0 290 my ($self, $key) = @_;
261 177         295 my $dom = $self->domain;
262              
263 177         620 $key =~ s/^$dom//;
264              
265 177         339 my @prefix = ($dom);
266 177 100       525 push @prefix, $self->{subdomain} if defined $self->{subdomain};
267              
268 177         998 return lc join '.', @prefix, split /_/, $key;
269             }
270              
271             sub to_env_key {
272 263     263 0 371 my ($self, $key) = @_;
273 263         591 my $dom = $self->domain;
274              
275 263         720 $key =~ s/^$dom//;
276              
277 263         2120 return uc join '_', $dom, split /\./, $key
278             }
279              
280             sub to_sub_key {
281 29     29 0 51 my ($self, $key) = @_;
282 29         77 my $dom = $self->domain;
283              
284 29         112 ($key = $self->to_dom_key($key)) =~ s/^$dom(\.)?//;
285              
286 29         127 return $key;
287             }
288              
289             sub DESTROY {
290 38     38   16131 my ($self) = @_;
291              
292 38 100       1163 if ($self->lifecycle) {
293 2         7 my $environment = $self->environment;
294 2         4 my $snapshot = $self->{snapshot};
295              
296 2         3 delete $ENV{$_} for grep { ! exists $snapshot->{$_} }
  8         29  
  2         7  
297             keys %{$environment};
298 2         5 $ENV{$_} = $snapshot->{$_} for keys %{$snapshot};
  2         36  
299             }
300             }
301              
302             1;
303              
304             __END__