File Coverage

blib/lib/Config/Tiny.pm
Criterion Covered Total %
statement 69 74 93.2
branch 32 46 69.5
condition 5 10 50.0
subroutine 11 11 100.0
pod 6 6 100.0
total 123 147 83.6


line stmt bran cond sub pod time code
1             package Config::Tiny;
2              
3             # If you thought Config::Simple was small...
4              
5 9     9   4625 use strict;
  9         51  
  9         243  
6 9     9   208 use 5.008001; # For the utf8 stuff.
  9         32  
7              
8             # Warning: There is another version line, in t/02.main.t.
9              
10             our $VERSION = '2.30';
11              
12             BEGIN {
13 9     9   10710 $Config::Tiny::errstr = '';
14             }
15              
16             # Create an object.
17              
18 6 100   6 1 5678 sub new { return bless defined $_[1] ? $_[1] : {}, $_[0] }
19              
20             # Create an object from a file.
21              
22             sub read
23             {
24 6 50   6 1 2400 my($class) = ref $_[0] ? ref shift : shift;
25 6         23 my($file, $encoding) = @_;
26              
27 6 50 33     41 return $class -> _error('No file name provided') if (! defined $file || ($file eq '') );
28              
29             # Slurp in the file.
30              
31 6 100       19 $encoding = $encoding ? "<:$encoding" : '<';
32 6         32 local $/ = undef;
33              
34 6 50   1   292 open(my $CFG, $encoding, $file) or return $class -> _error( "Failed to open file '$file' for reading: $!" );
  1         11  
  1         3  
  1         7  
35 6         17082 my $contents = <$CFG>;
36 6         99 close($CFG );
37              
38 6 50       28 return $class -> _error("Reading from '$file' returned undef") if (! defined $contents);
39              
40 6         41 return $class -> read_string( $contents );
41              
42             } # End of read.
43              
44             # Create an object from a string.
45              
46             sub read_string
47             {
48 14 50   14 1 31571 my($class) = ref $_[0] ? ref shift : shift;
49 14         41 my($self) = bless {}, $class;
50              
51 14 50       43 return undef unless defined $_[0];
52              
53             # Parse the file.
54              
55 14         29 my $ns = '_';
56 14         22 my $counter = 0;
57              
58 14         243 foreach ( split /(?:\015{1,2}\012|\015|\012)/, shift )
59             {
60 63         87 $counter++;
61              
62             # Skip comments and empty lines.
63              
64 63 100       175 next if /^\s*(?:\#|\;|$)/;
65              
66             # Remove inline comments.
67              
68 53         113 s/\s\;\s.+$//g;
69              
70             # Handle section headers.
71              
72 53 100       155 if ( /^\s*\[\s*(.+?)\s*\]\s*$/ )
73             {
74             # Create the sub-hash if it doesn't exist.
75             # Without this sections without keys will not
76             # appear at all in the completed struct.
77              
78 15   50     176 $self->{$ns = $1} ||= {};
79              
80 15         30 next;
81             }
82              
83             # Handle properties.
84              
85 38 50       195 if ( /^\s*([^=]+?)\s*=\s*(.*?)\s*$/ )
86             {
87 38 100       132 if ( substr($1, -2) eq '[]' )
88             {
89 2         13 my $k = substr $1, 0, -2;
90 2   100     14 $self->{$ns}->{$k} ||= [];
91 2 50       7 return $self -> _error ("Can't mix arrays and scalars at line $counter" ) unless ref $self->{$ns}->{$k} eq 'ARRAY';
92 2         3 push @{$self->{$ns}->{$k}}, $2;
  2         7  
93 2         5 next;
94             }
95 36         121 $self->{$ns}->{$1} = $2;
96              
97 36         72 next;
98             }
99              
100 0         0 return $self -> _error( "Syntax error at line $counter: '$_'" );
101             }
102              
103 14         85 return $self;
104             }
105              
106             # Save an object to a file.
107              
108             sub write
109             {
110 3     3 1 4481 my($self) = shift;
111 3         46 my($file, $encoding) = @_;
112              
113 3 50 33     39 return $self -> _error('No file name provided') if (! defined $file or ($file eq '') );
114              
115 3 100       13 $encoding = $encoding ? ">:$encoding" : '>';
116              
117             # Write it to the file.
118              
119 3         15 my($string) = $self->write_string;
120              
121 3 50       15 return undef unless defined $string;
122              
123 3 50       287 open(my $CFG, $encoding, $file) or return $self->_error("Failed to open file '$file' for writing: $!");
124 3         85 print $CFG $string;
125 3         184 close($CFG);
126              
127 3         29 return 1;
128              
129             } # End of write.
130              
131             # Save an object to a string.
132              
133             sub write_string
134             {
135 9     9 1 1455 my($self) = shift;
136 9         20 my($contents) = '';
137              
138 9 50       60 for my $section ( sort { (($b eq '_') <=> ($a eq '_')) || ($a cmp $b) } keys %$self )
  9         40  
139             {
140             # Check for several known-bad situations with the section
141             # 1. Leading whitespace
142             # 2. Trailing whitespace
143             # 3. Newlines in section name.
144              
145 16 50       79 return $self->_error("Illegal whitespace in section name '$section'") if $section =~ /(?:^\s|\n|\s$)/s;
146              
147 16         33 my $block = $self->{$section};
148 16 100       39 $contents .= "\n" if length $contents;
149 16 100       56 $contents .= "[$section]\n" unless $section eq '_';
150              
151 16         48 for my $property ( sort keys %$block )
152             {
153 25 100       91 return $self->_error("Illegal newlines in property '$section.$property'") if $block->{$property} =~ /(?:\012|\015)/s;
154              
155 24 50       64 if (ref $block->{$property} eq 'ARRAY') {
156 0         0 for my $element ( @{$block->{$property}} )
  0         0  
157             {
158 0         0 $contents .= "${property}[]=$element\n";
159             }
160 0         0 next;
161             }
162 24         72 $contents .= "$property=$block->{$property}\n";
163             }
164             }
165              
166 8         29 return $contents;
167              
168             } # End of write_string.
169              
170             # Error handling.
171              
172 1     1 1 360 sub errstr { $Config::Tiny::errstr }
173 1     1   3 sub _error { $Config::Tiny::errstr = $_[1]; undef }
  1         4  
174              
175             1;
176              
177             __END__