File Coverage

blib/arch/Panda/Config/Perl.pm
Criterion Covered Total %
statement 108 114 94.7
branch 41 54 75.9
condition 8 14 57.1
subroutine 12 12 100.0
pod 1 1 100.0
total 170 195 87.1


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