File Coverage

blib/lib/Aion/Env/Etc.pm
Criterion Covered Total %
statement 63 65 96.9
branch 13 28 46.4
condition 3 9 33.3
subroutine 9 9 100.0
pod 4 4 100.0
total 92 115 80.0


line stmt bran cond sub pod time code
1             package Aion::Env::Etc;
2              
3 1     1   146170 use common::sense;
  1         2  
  1         5  
4              
5 1     1   548 use YAML::Syck qw//;
  1         1947  
  1         33  
6              
7 1     1   314 use Aion::Env AION_ENV_ETC_PATH => (default => 'etc/include.yml');
  1         2  
  1         5  
8 1     1   6 use Aion::Env APP_ENV => (default => 'prod');
  1         1  
  1         3  
9              
10             our %etc = %{ -e AION_ENV_ETC_PATH? parse(AION_ENV_ETC_PATH): () };
11              
12             sub import {
13 2     2   33 my ($cls, $name, %kw) = @_;
14 2         3 my $isa = delete $kw{isa};
15 2         3 my $is_default = exists $kw{default};
16 2         2 my $default = delete $kw{default};
17 2   66     9 my $key = delete $kw{key} // lc($name) =~ y/_/./r;
18 2 0       4 die sprintf "Unknown keyword%s: %s",
    50          
19             scalar keys %kw == 1? '': 's',
20             join ", ", sort keys %kw if keys %kw;
21              
22 2         3 my ($val, $key_exists) = by_key(\%etc, $key);
23 2 0 33     4 die "$name is'nt defined!" if !$key_exists and !$is_default;
24              
25 2         4 my $pkg = caller;
26 2 50       3 my $val = $key_exists? $val: $default;
27              
28 2 50       4 if($isa) {
29 2 50       4 if(UNIVERSAL::isa($isa, "Aion::Type")) { $isa->validate($val, $name) }
  0         0  
30             else {
31 2         3 local $_ = $val;
32 2 0       5 die UNIVERSAL::can($isa, "get_message")? $isa->get_message($val): "$name type is'nt isa!" unless $isa->();
    50          
33             }
34             }
35            
36 2         2026 constant->import("$pkg\::$name", $val);
37             }
38              
39             # Считывает и парсит конфигурационный файл с включениями
40             sub parse {
41 1     1 1 2 my ($path) = @_;
42              
43 1         2 my $etc;
44 1         1 my @S = $path;
45 1         3 while(@S) {
46 2         3 my $path = shift @S;
47 2 50       64 open my $f, '<:utf8', $path or die "$path :$!";
48 2         46 read $f, my $buf, -s $f;
49 2         18 close $f;
50 2   0     6 $buf =~ s!\$\{([a-z_]\w*)\}! val($ENV{$1} // $Aion::Env::env{$1}) !gie;
  0         0  
51 2         5 my $include = YAML::Syck::Load($buf); undef $buf;
  2         134  
52 2         3 push @S, @{$include->{includes}};
  2         5  
53 2         4 %$include = (%$include, %{$include->{'when@' . APP_ENV}});
  2         6  
54 2         5 $etc = merge_hashes($path, undef, $etc, $include);
55             }
56              
57             $etc
58 1         3 }
59              
60             # Рекурсивное объединение двух хешей
61             sub merge_hashes {
62 3     3 1 5 my ($file, $path, $x, $y) = @_;
63              
64 3         5 my %val = %$x;
65 3         5 for my $k (keys %$y) {
66 5 100       9 if(exists $val{$k}) {
67 1 50       2 my $a_path = $path? "$path.$k": $k;
68 1 50       3 die "$file > $a_path: x is'nt hash" if ref $val{$k} ne 'HASH';
69 1 50       3 die "$file > $a_path: y is'nt hash" if ref $y->{$k} ne 'HASH';
70 1         3 $val{$k} = merge_hashes($file, $a_path, $val{$k}, $y->{$k});
71             }
72 4         6 else { $val{$k} = $y->{$k} }
73             }
74              
75 3         13 \%val
76             }
77              
78             # Добавляет бэкслеши
79             sub val {
80 1     1 1 128750 my ($s) = @_;
81 1         10 $s =~ s/[\\"']/\\$&/g;
82 1         4 $s =~ s/\n/\\n/g;
83 1         3 $s =~ s/\r/\\r/g;
84 1         21 $s =~ s/\t/\\t/g;
85 1         4 $s
86             }
87              
88             # Получить значение по ключу
89             sub by_key($$) {
90 4     4 1 3185 my ($hash, $path) = @_;
91 4 100       24 exists $hash->{$_}? $hash = $hash->{$_}: return undef, 0 for split /\./, $path;
92 3         7 return $hash, 1;
93             }
94              
95             1;
96              
97             __END__