File Coverage

blib/lib/WebDAO/Config.pm
Criterion Covered Total %
statement 112 124 90.3
branch 40 52 76.9
condition 12 20 60.0
subroutine 14 15 93.3
pod 1 6 16.6
total 179 217 82.4


line stmt bran cond sub pod time code
1             package WebDAO::Config;
2              
3             =head1 NAME
4              
5             WebDAO::Config - Configuration file class.
6              
7             =head1 SYNOPSIS
8              
9             use WebDAO::Config;
10             my $conf = new WebDAO::Config:: ( $opt{config} );
11             my $value = $conf->general->{db_name};
12              
13              
14             =head1 DESCRIPTION
15              
16             Configuration file class
17              
18             =head3 Format of INI-FILE
19              
20             Data is organized in sections. Each key/value pair is delimited with an
21             equal (=) sign. Sections are declared on their own lines enclosed in
22             '[' and ']':
23              
24             [BLOCK1]
25             KEY1 ?=VALUE1
26             KEY2 +=VALUE2
27              
28              
29             [BLOCK2]
30             KEY1=VALUE1
31             KEY2=VALUE2
32              
33             #%INCLUDE file.inc%
34              
35             =item B<?=> - set value unless it defined before
36              
37             =item B<+=> - add value
38              
39             =item B<=> - set value to key
40              
41             =item B<#%INCLUDE file.inc%> - include config ini file
42              
43             =cut
44              
45 1     1   70354 use strict;
  1         2  
  1         24  
46 1     1   5 use warnings;
  1         2  
  1         20  
47 1     1   292 use WebDAO::Base;
  1         3  
  1         54  
48 1     1   6 use base 'WebDAO::Base';
  1         2  
  1         87  
49 1     1   5 use vars qw($AUTOLOAD);
  1         2  
  1         41  
50 1     1   378 use IO::File;
  1         6224  
  1         121  
51             our $VERSION = '0.4';
52             our $PERL_SINGLE_QUOTE;
53              
54             __PACKAGE__->mk_attr ( __conf=>undef, _path=>undef );
55              
56             sub parse_line {
57 22     22 0 71 my($delimiter, $keep, $line) = @_;
58 22         49 my($word, @pieces);
59              
60 1     1   7 no warnings 'uninitialized'; # we will be testing undef strings
  1         2  
  1         1044  
61              
62 22         55 while (length($line)) {
63             # This pattern is optimised to be stack conservative on older perls.
64             # Do not refactor without being careful and testing it on very long strings.
65             # See Perl bug #42980 for an example of a stack busting input.
66 28 50       279 $line =~ s/^
67             (?:
68             # double quoted string
69             (") # $quote
70             ((?>[^\\"]*(?:\\.[^\\"]*)*))" # $quoted
71             | # --OR--
72             # singe quoted string
73             (') # $quote
74             ((?>[^\\']*(?:\\.[^\\']*)*))' # $quoted
75             | # --OR--
76             # unquoted string
77             ( # $unquoted
78             (?:\\.|[^\\"'])*?
79             )
80             # followed by
81             ( # $delim
82             \Z(?!\n) # EOL
83             | # --OR--
84             (?-x:$delimiter) # delimiter
85             | # --OR--
86             (?!^)(?=["']) # a quote
87             )
88             )//xs or return; # extended layout
89 28 100       125 my ($quote, $quoted, $unquoted, $delim) = (($1 ? ($1,$2) : ($3,$4)), $5, $6);
90              
91              
92 28 50 100     113 return() unless( defined($quote) || length($unquoted) || length($delim));
      66        
93              
94 28 50       70 if ($keep) {
95 0         0 $quoted = "$quote$quoted$quote";
96             }
97             else {
98 28         53 $unquoted =~ s/\\(.)/$1/sg;
99 28 100       71 if (defined $quote) {
100 16 100       48 $quoted =~ s/\\(.)/$1/sg if ($quote eq '"');
101 16 50 33     47 $quoted =~ s/\\([\\'])/$1/g if ( $PERL_SINGLE_QUOTE && $quote eq "'");
102             }
103             }
104 28         62 $word .= substr($line, 0, 0); # leave results tainted
105 28 100       72 $word .= defined $quote ? $quoted : $unquoted;
106            
107 28 100       67 if (length($delim)) {
108 4         9 push(@pieces, $word);
109 4 50       12 push(@pieces, $delim) if ($keep eq 'delimiters');
110 4         7 undef $word;
111             }
112 28 100       76 if (!length($line)) {
113 22         74 push(@pieces, $word);
114             }
115             }
116 22         81 return(@pieces);
117             }
118              
119             #method for convert 'file_name', \*FH, \$string, <IO::File> to hash
120              
121             sub convert_ini2hash {
122 5     5 0 16 my $data = shift;
123              
124             #if we got filename
125 5 100       18 unless ( ref $data ) {
126 1         7 my $fh = new IO::File:: "< $data";
127 1         84 my $res = &convert_ini2hash($fh);
128 1         13 close $fh;
129 1         14 return $res;
130             }
131              
132             #We got file descriptor ?
133 4 50 66     50 if ( ref $data
      33        
      66        
134             and ( UNIVERSAL::isa( $data, 'IO::Handle' ) or ( ref $data ) eq 'GLOB' )
135             or UNIVERSAL::isa( $data, 'Tie::Handle' ) )
136             {
137              
138             #read all data from file descripto to scalar
139 1         3 my $str;
140             {
141 1         7 local $/;
  1         5  
142 1         26 $str = <$data>;
143             }
144 1         8 return &convert_ini2hash( \$str );
145             }
146 3         9 my %result = ();
147 3         8 my $line_num = 0;
148 3         7 my $section = 'default';
149              
150             #if in param ref to scalar
151 3         142 foreach ( split /(?:\015{1,2}\012|\015|\012)/, $$data ) {
152 36         72 my $line = $_;
153 36         60 $line_num++;
154              
155             # skipping comments and empty lines:
156              
157 36 100       132 $line =~ /^\s*(\n|\#|;)/ and next;
158 31 100       105 $line =~ /\S/ or next;
159              
160 30         61 chomp $line;
161              
162 30         109 $line =~ s/^\s+//g;
163 30         90 $line =~ s/\s+$//g;
164              
165             # parsing the block name:
166 30 100       114 $line =~ /^\s*\[\s*([^\]]+)\s*\]$/ and $section = lc($1), next;
167              
168             # parsing key/value pairs
169             # process ?= and += features
170 22 50       99 if ( $line =~ /^\s*([^=]*\w)\s*([\?\+]?=)\s*(.*)\s*$/ ) {
171 22         60 my $key = lc($1);
172 22         57 my @value = parse_line( '\s*,\s*', 0, $3 );
173 22         51 my $op = $2;
174              
175             #add current key
176 22 100       82 if ( $op =~ /\+=/ ) {
    100          
177 2         6 push @{ $result{$section}->{$key} }, @value;
  2         7  
178 2         7 next;
179             }
180              
181             # skip if already defined key
182             elsif ( $op =~ /\?=/ ) {
183 4 100       15 next if defined $result{$section}->{$key};
184             }
185              
186             # set current value to result hash
187 18         56 $result{$section}->{$key} = \@value;
188 18         50 next;
189             }
190              
191             # if we came this far, the syntax couldn't be validated:
192 0         0 warn "syntax error on line $line_num: '$line'";
193 0         0 return {};
194             }
195              
196             #strip values
197 3         20 while ( my ( $sect_name, $sect_hash ) = each %result ) {
198 6         23 while ( my ( $key, $val ) = each %$sect_hash ) {
199 13 100       40 if ( scalar(@$val) < 2 ) {
200 8         39 $result{$sect_name}->{$key} = shift @$val;
201             }
202             }
203             }
204 3         40 return \%result;
205             }
206              
207             sub get_full_path_for {
208 1     1 0 4 my $root_file = shift;
209              
210             # my $file_to = shift;
211 1         5 my @req_path = @_;
212 1         5 my $req_path = join "/", @req_path;
213 1 50       5 return $req_path if $req_path =~ /^\//;
214 1         7 my @ini_path = split( "/", $root_file );
215              
216             #strip file name
217 1         3 pop @ini_path;
218 1         5 my $path = join "/" => @ini_path, $req_path;
219              
220             # _log1 $self "File $path not exists" unless -e $path;
221 1         7 return $path;
222             }
223              
224             sub process_includes {
225 3     3 0 8 my $file = shift;
226 3   50     21 my $fh = ( new IO::File:: "< $file" ) || die "$file: $!";
227 3         245 my $str = '';
228 3         51 while ( defined( my $line = <$fh> ) ) {
229              
230 28 100       121 $str .=
231             $line =~ /#%INCLUDE\s*(.*)\s*%/
232             ? &process_includes( &get_full_path_for( $file, $1 ) )
233             : $line;
234             }
235 3         28 close $fh;
236 3         18 return $str;
237             }
238              
239             sub new {
240 1     1 1 76 my $class = shift;
241 1         3 my $self = {};
242 1         2 my $stat;
243 1         2 bless( $self, $class );
244 1         5 $self->_init(@_);
245 1         6 return $self;
246             }
247              
248             sub _init {
249 1     1   2 my $self = shift;
250 1         2 my $file_path = shift;
251              
252             #process inludes in in data
253 1         16 my $inc = &process_includes($file_path);
254 1         4 $self->__conf( &convert_ini2hash(\$inc) );
255 1         19 $self->_path($file_path);
256 1         2 return 1;
257             }
258              
259             sub get_full_path {
260 0     0 0 0 my $self = shift;
261 0         0 my @req_path = @_;
262 0         0 my $req_path = join "/", @req_path;
263 0 0       0 return $req_path if $req_path =~ /^\//;
264 0         0 my @ini_path = split( "/", $self->_path );
265 0         0 pop @ini_path;
266 0         0 my $path = join "/" => @ini_path, $req_path;
267 0 0       0 _log1 "File $path not exists" unless -e $path;
268 0         0 return $path;
269             }
270              
271             sub AUTOLOAD {
272 2     2   1873 my $self = shift;
273 2 100       71 return if $AUTOLOAD =~ /::DESTROY$/;
274 1         6 ( my $auto_sub ) = $AUTOLOAD =~ /.*::(.*)/;
275 1         25 return $self->__conf->{$auto_sub};
276             }
277             1;
278             __END__
279              
280             =head1 SEE ALSO
281              
282             WebDAO, README
283              
284             =head1 AUTHOR
285              
286             Zahatski Aliaksandr, E<lt>zag@cpan.orgE<gt>
287              
288             =head1 COPYRIGHT AND LICENSE
289              
290             Copyright (C) 2006-2011 by Zahatski Aliaksandr
291              
292             This library is free software; you can redistribute it and/or modify
293             it under the same terms as Perl itself, either Perl version 5.8.8 or,
294             at your option, any later version of Perl 5 you may have available.
295              
296             =cut
297