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   908922 use 5.012;
  7         84  
3 7     7   2786 use Path::Class;
  7         235044  
  7         423  
4 7     7   3459 use Data::Recursive(); # XS code needs xs::merge
  7         124115  
  7         614  
5              
6             our $VERSION = '1.2.1';
7              
8             XS::Loader::load();
9              
10             sub process {
11 8     8 1 4952 my ($class, $file, $initial_cfg) = @_;
12 8         84 $file = Path::Class::File->new($file);
13              
14 8         1429 my ($mstash, $nsstash);
15             {
16 7     7   60 no strict 'refs';
  7         14  
  7         1426  
  8         15  
17 8         17 $mstash = \%{"::"};
  8         39  
18 8         32 delete $mstash->{'NS::'};
19 8         21 $nsstash = \%{"NS::"};
  8         37  
20             }
21              
22 8 100       89 _apply_initial_cfg('', Data::Recursive::clone($initial_cfg)) if $initial_cfg;
23 8         34 _process_file($file);
24              
25 6         14 my $ret = {};
26              
27 6         15 my $cfg = {};
28 6 50       32 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         113 delete $mstash->{'NS::'};
36              
37 5         131 return $ret;
38             }
39              
40             sub _apply_initial_cfg {
41 4     4   14 my ($ns, $cfg) = @_;
42 4         19 foreach my $key (keys %$cfg) {
43 8 50       31 if (substr($key, -2, 2) eq '::') {
44 0         0 _apply_initial_cfg($ns.$key, $cfg->{$key});
45             } else {
46 7     7   48 no strict 'refs';
  7         16  
  7         1882  
47 8         16 *{"NS::$ns$key"} = \$cfg->{$key};
  8         38  
48             }
49             }
50             }
51              
52             sub _process_file {
53 14     14   444 my ($file, $ns) = @_;
54 14         59 my $content = $file->slurp;
55              
56 13         65998 my $curdir = $file->dir;
57              
58 13         235 $content =~ s/^[^\S\r\n]*#(namespace|namespace-abs|include)(?:[^\S\r\n]+(.+))?$/_process_directive($curdir, $ns, $1, $2)/gme;
  24         155  
59              
60 13 100       102 my $pkg = $ns ? "NS::$ns" : "NS";
61 13         71 $content = "package $pkg; sub { $content;\n }";
62 13         27 my $ok;
63             {
64 7     7   56 no strict;
  7         13  
  7         6336  
  13         20  
65 13         152 enable_op_tracking();
66 13         2276 my $sub = eval $content;
67 13         70 disable_op_tracking();
68 13 100       48 $ok = eval { $sub->(); 1 } if $sub;
  12         252  
  12         140  
69             }
70 13 100       47 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         6 die "Config::MorePerl: error while processing config $file: $err\n".
75             "================ Error-prone code ================\n".
76             _content_linno($content).
77             "==================================================";
78             }
79              
80 12         158 return;
81             }
82              
83             sub _process_directive {
84 24     24   95 my ($curdir, $ns, $directive, $rest) = @_;
85 24   100     96 $rest //= '';
86 24         50 $rest =~ s/\s+$//;
87 24 100       80 if (index($directive, 'namespace') == 0) {
    50          
88 19 100       71 $ns = '' if $directive eq 'namespace-abs';
89 19 100       42 my $pkg = $ns ? "NS::$ns" : 'NS';
90 19 100       88 $pkg .= "::$1" if $rest =~ /\s*(\S+)/;
91 19         143 return "package $pkg;";
92             }
93             elsif ($directive eq 'include') {
94 5         23 return "Config::MorePerl::_INCLUDE('$curdir', __PACKAGE__, $rest);";
95             }
96             }
97              
98             sub _INCLUDE {
99 5     5   16 my ($dir, $curpkg, $file) = @_;
100 5   33     47 $dir = $dir && Path::Class::Dir->new($dir);
101 5         316 $file = Path::Class::File->new($file);
102 5         205 my $ns = '';
103 5 50       16 if ($curpkg ne 'NS') {
104 5         9 $ns = $curpkg;
105 5         17 substr($ns, 0, 4, ''); # remove /^NS::/
106             }
107            
108 5 50 33     15 $file = $dir->file($file) if $dir && !$file->is_absolute;
109            
110 5 100       623 if (index($file, '*') >= 0) {
111 1         46 _process_file(Path::Class::File->new($_), $ns) for glob($file);
112             } else {
113 4         183 _process_file($file, $ns);
114             }
115             }
116              
117             sub _get_config {
118 23     23   54 my ($dest, $stash, $config, $ns) = @_;
119 23         37 my @ns_list;
120              
121             my $assign_proc;
122 23 50       50 $assign_proc = $config->{assign_proc} if defined $config->{assign_proc};
123 23         75 foreach my $key (keys %$stash) {
124 91 100 33     564 next if $key eq 'BEGIN' or $key eq 'DESTROY' or $key eq 'AUTOLOAD' or index($key, '__ANON__') == 0;
      33        
      66        
125 83 100       187 if (substr($key, -2, 2) eq '::') {
126 18         32 push @ns_list, $key;
127 18         37 next;
128             }
129 65 50       208 my $glob = $stash->{$key} or next;
130 65 50 66     144 next if !defined $$glob and defined *$glob{CODE};
131 65 50       109 if(defined $assign_proc){
132 0         0 $dest->{$key} = undef;
133 0         0 $assign_proc->($dest->{$key}, $$glob);
134             } else {
135 65         142 $dest->{$key} = $$glob;
136             }
137             }
138              
139 23         81 foreach my $subns (@ns_list) {
140 18         29 my $substash = \%{$stash->{$subns}};
  18         44  
141 18         40 substr($subns, -2, 2, '');
142 18 100       63 my $subns_full = $ns ? "${ns}::$subns" : $subns;
143 18 100       45 if (exists $dest->{$subns}) {
144 1         14 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         73 _get_config($dest->{$subns} = {}, $substash, $config, $subns_full);
148             }
149             }
150              
151             sub _content_linno {
152 1     1   42 my $content = shift;
153 1         3 my $i = 0;
154 1         7 $content =~ s/^(.*)$/$i++; "$i: $1"/mge;
  7         10  
  7         26  
155 1         14 return $content;
156             }
157              
158             1;