File Coverage

blib/arch/Config/MorePerl.pm
Criterion Covered Total %
statement 106 112 94.6
branch 37 48 77.0
condition 10 20 50.0
subroutine 13 13 100.0
pod 1 1 100.0
total 167 194 86.0


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