File Coverage

blib/lib/Config/Settings.pm
Criterion Covered Total %
statement 66 66 100.0
branch 24 24 100.0
condition 2 2 100.0
subroutine 12 12 100.0
pod 3 3 100.0
total 107 107 100.0


line stmt bran cond sub pod time code
1             package Config::Settings;
2              
3 1     1   1096 use Carp qw/confess/;
  1         2  
  1         79  
4 1     1   1755 use Parse::RecDescent;
  1         53546  
  1         10  
5              
6 1     1   88 use strict;
  1         2  
  1         34  
7 1     1   6 use warnings;
  1         1  
  1         764  
8              
9             our $VERSION = '0.02';
10              
11             my $parser = Parse::RecDescent->new (<<'EOF');
12             config:
13             scope
14              
15             scope:
16             assignment(s? /;+/) /;*/
17             { $return = [ 'SCOPE',@{ $item[1] } ] }
18              
19             assignment:
20             deep_assignment | direct_assignment | true_assignment |
21              
22             deep_assignment:
23             keyword keyword value
24             { $return = [ $item[1] => $item[2] => $item[3] ]; 1 }
25              
26             direct_assignment:
27             keyword value
28             { $return = [ $item[1] => $item[2] ]; 1 }
29              
30             true_assignment:
31             keyword
32             { $return = [ $item[1] => 1 ]; 1 }
33              
34             keyword:
35             integer | string | bareword
36              
37             value:
38             integer | string | list | hash | symbol
39              
40             bareword:
41             /[\w:]+/
42              
43             integer:
44             /\d+/
45              
46             string:
47            
48             { $return = $item[1][2]; 1 }
49              
50             list:
51             "[" value(s?) "]"
52             { $return = [ 'LIST',@{ $item[2] } ]; 1 }
53              
54             hash:
55             "{" scope "}"
56             { $return = $item[2]; 1 }
57              
58             symbol:
59             bareword
60             { $return = [ 'SYMBOL',$item[1] ]; 1 }
61              
62             EOF
63              
64             my %default_symbols = (
65             null => undef,
66             true => 1,
67             false => '',
68             );
69              
70             sub new {
71 3     3 1 797 my $class = shift;
72              
73 3 100       17 my $node = (ref $_[0] eq 'HASH' ? $_[0] : { @_ });
74              
75 3   100     23 $node->{symbol_table} ||= { %default_symbols };
76              
77 3         15 return bless $node,$class;
78             }
79              
80             sub parse_file {
81 2     2 1 1955 my ($self,$file) = @_;
82              
83 2 100       144 open (my $fh,$file) or confess $!;
84              
85 1         2 my $content = do { local $/; <$fh> };
  1         4  
  1         26  
86              
87 1         14 close $fh;
88              
89 1         5 return $self->parse ($content);
90             }
91              
92             sub parse {
93 12     12 1 87 my ($self,$content) = @_;
94              
95 12         105 return $self->_process_value ($parser->config ($content));
96             }
97              
98             sub _process_scope {
99 14     14   20 my ($self,$scope) = @_;
100              
101 14         22 my %result;
102              
103 14         27 foreach my $assignment (@$scope) {
104 16         35 my ($key,$value) = @$assignment;
105              
106 16 100       40 if (@$assignment > 2) {
107 2         11 $self->_deep_assignment (\%result,@$assignment);
108             } else {
109 14         45 $self->_simple_assignment (\%result,@$assignment);
110             }
111             }
112              
113 13         39 return \%result;
114             }
115              
116             sub _simple_assignment {
117 14     14   24 my ($self,$hashref,$key,$value) = @_;
118              
119 14         36 $value = $self->_process_value ($value);
120              
121 13 100       37 if (exists $hashref->{$key}) {
122 2 100       6 if (ref $hashref->{$key} eq 'ARRAY') {
123 1         3 push @{ $hashref->{$key} },$value;
  1         4  
124             } else {
125 1         4 $hashref->{$key} = [ $hashref->{$key},$value ];
126             }
127             } else {
128 11         26 $hashref->{$key} = $value;
129             }
130              
131 13         41 return;
132             }
133              
134             sub _deep_assignment {
135 2     2   7 my ($self,$hashref,$key1,$key2,$value) = @_;
136              
137 2         6 $value = $self->_process_value ($value);
138              
139 2         6 $key2 = $self->_process_value ($key2);
140              
141 2 100       7 if (ref $hashref->{$key1} eq 'HASH') {
142 1         4 $hashref->{$key1}->{$key2} = $value;
143             } else {
144 1         5 $hashref->{$key1} = { $key2 => $value };
145             }
146              
147 2         8 return;
148             }
149              
150             sub _process_value {
151 34     34   96452 my ($self,$value) = @_;
152              
153 34 100       101 if (ref $value) {
154 19         39 my $value_type = shift @$value;
155              
156 19 100       71 if ($value_type eq 'SCOPE') {
    100          
    100          
157 14         51 $value = $self->_process_scope ($value);
158             } elsif ($value_type eq 'LIST') {
159 1         4 $value = [ map { $self->_process_value ($_) } @$value ];
  3         8  
160             } elsif ($value_type eq 'SYMBOL') {
161 3         14 $value = $self->_process_symbol (@$value);
162             } else {
163 1         16 confess "Uh oh, this should never happen";
164             }
165             }
166              
167 31         158 return $value;
168             }
169              
170             sub _process_symbol {
171 3     3   7 my ($self,$symbol) = @_;
172              
173 3         4 my $value;
174              
175 3 100       19 if (exists $self->{symbol_table}->{ $symbol }) {
176 2         5 my $symbol_entry = $self->{symbol_table}->{ $symbol };
177              
178 2 100       9 $value = (ref $symbol_entry eq 'CODE' ? $symbol_entry->() : $symbol_entry);
179             } else {
180 1         64 confess "No such symbol '$symbol' in symbol table";
181             }
182              
183 2         8 return $value;
184             }
185              
186             1;
187              
188             __END__