File Coverage

blib/arch/Config/MorePerl.pm
Criterion Covered Total %
statement 108 114 94.7
branch 39 52 75.0
condition 12 26 46.1
subroutine 13 13 100.0
pod 1 1 100.0
total 173 206 83.9


line stmt bran cond sub pod time code
1             package Config::MorePerl;
2 7     7   667300 use 5.012;
  7         77  
3 7     7   2088 use Path::Class;
  7         184664  
  7         366  
4 7     7   2774 use Data::Recursive(); # XS code needs xs::merge
  7         105925  
  7         561  
5              
6             our $VERSION = '1.2.3';
7              
8             XS::Loader::load();
9              
10             sub process {
11 8     8 1 4193 my ($class, $file, $initial_cfg) = @_;
12 8         63 $file = Path::Class::File->new($file);
13              
14 8         1207 my ($mstash, $nsstash);
15             {
16 7     7   47 no strict 'refs';
  7         14  
  7         1331  
  8         11  
17 8         12 $mstash = \%{"::"};
  8         23  
18 8         24 delete $mstash->{'NS::'};
19 8         19 $nsstash = \%{"NS::"};
  8         32  
20             }
21              
22 8 50 33     35 $DB::{disable_profile}() if $DB::{disable_profile} && !$ENV{MP_WRITE_NYTPROF};
23 8 100       70 _apply_initial_cfg('', Data::Recursive::clone($initial_cfg)) if $initial_cfg;
24 8         57 _process_file($file);
25 6 50 33     31 $DB::{enable_profile}() if $DB::{enable_profile} && !$ENV{MP_WRITE_NYTPROF};
26              
27 6         12 my $ret = {};
28              
29 6         12 my $cfg = {};
30 6 50       21 if(defined $nsstash->{'__CONFIG__'}){
31 0         0 $cfg = ${$nsstash->{'__CONFIG__'}};
  0         0  
32 0         0 delete $nsstash->{'__CONFIG__'};
33             }
34 6         29 _get_config($ret, $nsstash, $cfg, '');
35              
36             # remove garbage we've created
37 5         76 delete $mstash->{'NS::'};
38              
39 5         103 return $ret;
40             }
41              
42             sub _apply_initial_cfg {
43 4     4   10 my ($ns, $cfg) = @_;
44 4         16 foreach my $key (keys %$cfg) {
45 8 50       25 if (substr($key, -2, 2) eq '::') {
46 0         0 _apply_initial_cfg($ns.$key, $cfg->{$key});
47             } else {
48 7     7   36 no strict 'refs';
  7         16  
  7         1506  
49 8         16 *{"NS::$ns$key"} = \$cfg->{$key};
  8         27  
50             }
51             }
52             }
53              
54             sub _process_file {
55 14     14   261 my ($file, $ns) = @_;
56 14         44 my $content = $file->slurp;
57              
58 13         7459 my $curdir = $file->dir;
59              
60 13         150 $content =~ s/^[^\S\r\n]*#(namespace|namespace-abs|include)(?:[^\S\r\n]+(.+))?$/_process_directive($curdir, $ns, $1, $2)/gme;
  24         138  
61              
62 13 100       101 my $pkg = $ns ? "NS::$ns" : "NS";
63 13         39 $content = "package $pkg; sub { $content;\n }";
64 13         16 my $ok;
65             {
66 7     7   48 no strict;
  7         13  
  7         4896  
  13         19  
67 13         144 enable_op_tracking();
68 13         1595 my $sub = eval $content;
69 13         59 disable_op_tracking();
70 13 100       35 $ok = eval { $sub->(); 1 } if $sub;
  12         188  
  12         101  
71             }
72 13 100       32 unless ($ok) {
73 1         1 my $err = $@;
74 1 50       4 die $err if $err =~ /Error-prone code/;
75 1 50       5 $err =~ s/Config::MorePerl: //g unless ref $err;
76 1         4 die "Config::MorePerl: error while processing config $file: $err\n".
77             "================ Error-prone code ================\n".
78             _content_linno($content).
79             "==================================================";
80             }
81              
82 12         94 return;
83             }
84              
85             sub _process_directive {
86 24     24   73 my ($curdir, $ns, $directive, $rest) = @_;
87 24   100     54 $rest //= '';
88 24         42 $rest =~ s/\s+$//;
89 24 100       56 if (index($directive, 'namespace') == 0) {
    50          
90 19 100       34 $ns = '' if $directive eq 'namespace-abs';
91 19 100       32 my $pkg = $ns ? "NS::$ns" : 'NS';
92 19 100       66 $pkg .= "::$1" if $rest =~ /\s*(\S+)/;
93 19         91 return "package $pkg;";
94             }
95             elsif ($directive eq 'include') {
96 5         18 return "Config::MorePerl::_INCLUDE('$curdir', __PACKAGE__, $rest);";
97             }
98             }
99              
100             sub _INCLUDE {
101 5     5   12 my ($dir, $curpkg, $file) = @_;
102 5   33     30 $dir = $dir && Path::Class::Dir->new($dir);
103 5         213 $file = Path::Class::File->new($file);
104 5         142 my $ns = '';
105 5 50       13 if ($curpkg ne 'NS') {
106 5         7 $ns = $curpkg;
107 5         43 substr($ns, 0, 4, ''); # remove /^NS::/
108             }
109            
110 5 50 33     13 $file = $dir->file($file) if $dir && !$file->is_absolute;
111            
112 5 100       439 if (index($file, '*') >= 0) {
113 1         33 _process_file(Path::Class::File->new($_), $ns) for glob($file);
114             } else {
115 4         131 _process_file($file, $ns);
116             }
117             }
118              
119             sub _get_config {
120 23     23   45 my ($dest, $stash, $config, $ns) = @_;
121 23         24 my @ns_list;
122              
123             my $assign_proc;
124 23 50       51 $assign_proc = $config->{assign_proc} if defined $config->{assign_proc};
125 23         55 foreach my $key (keys %$stash) {
126 91 100 33     377 next if $key eq 'BEGIN' or $key eq 'DESTROY' or $key eq 'AUTOLOAD' or index($key, '__ANON__') == 0;
      33        
      66        
127 83 100       143 if (substr($key, -2, 2) eq '::') {
128 18         22 push @ns_list, $key;
129 18         22 next;
130             }
131 65 50       123 my $glob = $stash->{$key} or next;
132 65 50 66     131 next if !defined $$glob and defined *$glob{CODE};
133 65 50       86 if(defined $assign_proc){
134 0         0 $dest->{$key} = undef;
135 0         0 $assign_proc->($dest->{$key}, $$glob);
136             } else {
137 65         98 $dest->{$key} = $$glob;
138             }
139             }
140              
141 23         51 foreach my $subns (@ns_list) {
142 18         20 my $substash = \%{$stash->{$subns}};
  18         29  
143 18         33 substr($subns, -2, 2, '');
144 18 100       30 my $subns_full = $ns ? "${ns}::$subns" : $subns;
145 18 100       32 if (exists $dest->{$subns}) {
146 1         11 die "Config::MorePerl: conflict between variable '$subns' in namespace '$ns' and a namespace '$subns_full'. ".
147             "You shouldn't have variables that overlap with namespaces as they would merge into the same hash.\n";
148             }
149 17         52 _get_config($dest->{$subns} = {}, $substash, $config, $subns_full);
150             }
151             }
152              
153             sub _content_linno {
154 1     1   34 my $content = shift;
155 1         1 my $i = 0;
156 1         6 $content =~ s/^(.*)$/$i++; "$i: $1"/mge;
  7         8  
  7         17  
157 1         11 return $content;
158             }
159              
160             1;