File Coverage

blib/lib/Config/Tiny.pm
Criterion Covered Total %
statement 61 62 98.3
branch 28 40 70.0
condition 3 8 37.5
subroutine 11 11 100.0
pod 6 6 100.0
total 109 127 85.8


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   4434 use strict;
  9         51  
  9         237  
6 9     9   172 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.29';
11              
12             BEGIN {
13 9     9   8995 $Config::Tiny::errstr = '';
14             }
15              
16             # Create an object.
17              
18 6 100   6 1 5688 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 2233 my($class) = ref $_[0] ? ref shift : shift;
25 6         18 my($file, $encoding) = @_;
26              
27 6 50 33     37 return $class -> _error('No file name provided') if (! defined $file || ($file eq '') );
28              
29             # Slurp in the file.
30              
31 6 100       18 $encoding = $encoding ? "<:$encoding" : '<';
32 6         24 local $/ = undef;
33              
34 6 50   1   253 open(my $CFG, $encoding, $file) or return $class -> _error( "Failed to open file '$file' for reading: $!" );
  1         8  
  1         1  
  1         13  
35 6         12498 my $contents = <$CFG>;
36 6         88 close($CFG );
37              
38 6 50       34 return $class -> _error("Reading from '$file' returned undef") if (! defined $contents);
39              
40 6         39 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 8823 my($class) = ref $_[0] ? ref shift : shift;
49 14         40 my($self) = bless {}, $class;
50              
51 14 50       43 return undef unless defined $_[0];
52              
53             # Parse the file.
54              
55 14         28 my $ns = '_';
56 14         22 my $counter = 0;
57              
58 14         230 foreach ( split /(?:\015{1,2}\012|\015|\012)/, shift )
59             {
60 61         92 $counter++;
61              
62             # Skip comments and empty lines.
63              
64 61 100       162 next if /^\s*(?:\#|\;|$)/;
65              
66             # Remove inline comments.
67              
68 51         100 s/\s\;\s.+$//g;
69              
70             # Handle section headers.
71              
72 51 100       163 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     104 $self->{$ns = $1} ||= {};
79              
80 15         26 next;
81             }
82              
83             # Handle properties.
84              
85 36 50       182 if ( /^\s*([^=]+?)\s*=\s*(.*?)\s*$/ )
86             {
87 36         132 $self->{$ns}->{$1} = $2;
88              
89 36         69 next;
90             }
91              
92 0         0 return $self -> _error( "Syntax error at line $counter: '$_'" );
93             }
94              
95 14         87 return $self;
96             }
97              
98             # Save an object to a file.
99              
100             sub write
101             {
102 3     3 1 3731 my($self) = shift;
103 3         52 my($file, $encoding) = @_;
104              
105 3 50 33     40 return $self -> _error('No file name provided') if (! defined $file or ($file eq '') );
106              
107 3 100       17 $encoding = $encoding ? ">:$encoding" : '>';
108              
109             # Write it to the file.
110              
111 3         12 my($string) = $self->write_string;
112              
113 3 50       21 return undef unless defined $string;
114              
115 3 50       249 open(my $CFG, $encoding, $file) or return $self->_error("Failed to open file '$file' for writing: $!");
116 3         61 print $CFG $string;
117 3         213 close($CFG);
118              
119 3         27 return 1;
120              
121             } # End of write.
122              
123             # Save an object to a string.
124              
125             sub write_string
126             {
127 9     9 1 1401 my($self) = shift;
128 9         40 my($contents) = '';
129              
130 9 50       45 for my $section ( sort { (($b eq '_') <=> ($a eq '_')) || ($a cmp $b) } keys %$self )
  7         36  
131             {
132             # Check for several known-bad situations with the section
133             # 1. Leading whitespace
134             # 2. Trailing whitespace
135             # 3. Newlines in section name.
136              
137 16 50       73 return $self->_error("Illegal whitespace in section name '$section'") if $section =~ /(?:^\s|\n|\s$)/s;
138              
139 16         31 my $block = $self->{$section};
140 16 100       37 $contents .= "\n" if length $contents;
141 16 100       61 $contents .= "[$section]\n" unless $section eq '_';
142              
143 16         47 for my $property ( sort keys %$block )
144             {
145 25 100       97 return $self->_error("Illegal newlines in property '$section.$property'") if $block->{$property} =~ /(?:\012|\015)/s;
146              
147 24         70 $contents .= "$property=$block->{$property}\n";
148             }
149             }
150              
151 8         28 return $contents;
152              
153             } # End of write_string.
154              
155             # Error handling.
156              
157 1     1 1 377 sub errstr { $Config::Tiny::errstr }
158 1     1   2 sub _error { $Config::Tiny::errstr = $_[1]; undef }
  1         5  
159              
160             1;
161              
162             __END__