File Coverage

blib/lib/Config/INI/Reader.pm
Criterion Covered Total %
statement 53 53 100.0
branch 18 18 100.0
condition 5 6 83.3
subroutine 17 17 100.0
pod 12 12 100.0
total 105 106 99.0


line stmt bran cond sub pod time code
1 3     3   1661 use v5.12.0;
  3         13  
2 3     3   14 use warnings;
  3         4  
  3         131  
3             package Config::INI::Reader 0.029;
4              
5 3     3   1064 use Mixin::Linewise::Readers 0.110;
  3         53410  
  3         14  
6             # ABSTRACT: a subclassable .ini-file parser
7              
8             #pod =head1 SYNOPSIS
9             #pod
10             #pod If F contains:
11             #pod
12             #pod admin = rjbs
13             #pod
14             #pod [rjbs]
15             #pod awesome = yes
16             #pod height = 5' 10"
17             #pod
18             #pod [mj]
19             #pod awesome = totally
20             #pod height = 23"
21             #pod
22             #pod Then when your program contains:
23             #pod
24             #pod my $hash = Config::INI::Reader->read_file('family.ini');
25             #pod
26             #pod C<$hash> will contain:
27             #pod
28             #pod {
29             #pod '_' => { admin => 'rjbs' },
30             #pod rjbs => {
31             #pod awesome => 'yes',
32             #pod height => q{5' 10"},
33             #pod },
34             #pod mj => {
35             #pod awesome => 'totally',
36             #pod height => '23"',
37             #pod },
38             #pod }
39             #pod
40             #pod =head1 DESCRIPTION
41             #pod
42             #pod Config::INI::Reader is I config module implementing I
43             #pod slightly different take on the undeniably easy to read L<".ini" file
44             #pod format|Config::INI>. Its default behavior is quite similar to that of
45             #pod L, on which it is based.
46             #pod
47             #pod The chief difference is that Config::INI::Reader is designed to be subclassed
48             #pod to allow for side-effects and self-reconfiguration to occur during the course
49             #pod of reading its input.
50             #pod
51             #pod =cut
52              
53 3     3   1038 use Carp ();
  3         7  
  3         2767  
54              
55             our @CARP_NOT = qw(Mixin::Linewise::Readers);
56              
57             #pod =head1 METHODS FOR READING CONFIG
58             #pod
59             #pod These methods are all that most users will need: they read configuration from a
60             #pod source of input, then they return the data extracted from that input. There
61             #pod are three reader methods, C, C, and C.
62             #pod The first two are implemented in terms of the third. It iterates over lines in
63             #pod a file, calling methods on the reader when events occur. Those events are
64             #pod detailed below in the L section.
65             #pod
66             #pod All of the reader methods return an unblessed reference to a hash.
67             #pod
68             #pod All throw an exception when they encounter an error.
69             #pod
70             #pod =head2 read_file
71             #pod
72             #pod my $hash_ref = Config::INI::Reader->read_file($filename);
73             #pod
74             #pod Given a filename, this method returns a hashref of the contents of that file.
75             #pod
76             #pod =head2 read_string
77             #pod
78             #pod my $hash_ref = Config::INI::Reader->read_string($string);
79             #pod
80             #pod Given a string, this method returns a hashref of the contents of that string.
81             #pod
82             #pod =head2 read_handle
83             #pod
84             #pod my $hash_ref = Config::INI::Reader->read_handle($io_handle);
85             #pod
86             #pod Given an IO::Handle, this method returns a hashref of the contents of that
87             #pod handle.
88             #pod
89             #pod =cut
90              
91             sub read_handle {
92 12     12 1 20457 my ($invocant, $handle) = @_;
93              
94 12 100       57 my $self = ref $invocant ? $invocant : $invocant->new;
95              
96             # parse the file
97 12         280 LINE: while (my $line = $handle->getline) {
98 66 100 100     1492 if ($handle->input_line_number == 1 && $line =~ /\A\x{FEFF}/) {
99 1         188 Carp::confess("input handle appears to start with a BOM");
100             }
101              
102 65         1018 $self->preprocess_line(\$line);
103              
104 65 100       111 next LINE if $self->can_ignore($line, $handle);
105              
106             # Handle section headers
107 54 100       89 if (defined (my $name = $self->parse_section_header($line, $handle))) {
108             # Create the sub-hash if it doesn't exist.
109             # Without this sections without keys will not
110             # appear at all in the completed struct.
111 13         30 $self->change_section($name);
112 13         191 next LINE;
113             }
114              
115 41 100       77 if (my ($name, $value) = $self->parse_value_assignment($line, $handle)) {
116 39         85 $self->set_value($name, $value);
117 39         593 next LINE;
118             }
119              
120 2         19 $self->handle_unparsed_line($line, $handle);
121             }
122              
123 9         221 $self->finalize;
124              
125 9         91 return $self->{data};
126             }
127              
128             #pod =head1 METHODS FOR SUBCLASSING
129             #pod
130             #pod These are the methods you need to understand and possibly change when
131             #pod subclassing Config::INI::Reader to handle a different format of input.
132             #pod
133             #pod =head2 current_section
134             #pod
135             #pod my $section_name = $reader->current_section;
136             #pod
137             #pod This method returns the name of the current section. If no section has yet
138             #pod been set, it returns the result of calling the C method.
139             #pod
140             #pod =cut
141              
142             sub current_section {
143 39   66 39 1 112 $_[0]->{section} // $_[0]->starting_section;
144             }
145              
146             #pod =head2 parse_section_header
147             #pod
148             #pod my $name = $reader->parse_section_header($line, $handle);
149             #pod
150             #pod Given a line of input, this method decides whether the line is a section-change
151             #pod declaration. If it is, it returns the name of the section to which to change.
152             #pod If the line is not a section-change, the method returns false.
153             #pod
154             #pod =cut
155              
156             sub parse_section_header {
157 54 100   54 1 162 return $1 if $_[1] =~ /^\s*\[\s*(.+?)\s*\]\s*$/;
158 41         78 return;
159             }
160              
161             #pod =head2 change_section
162             #pod
163             #pod $reader->change_section($section_name);
164             #pod
165             #pod This method is called whenever a section change occurs in the file.
166             #pod
167             #pod The default implementation is to change the current section into which data is
168             #pod being read and to initialize that section to an empty hashref.
169             #pod
170             #pod =cut
171              
172             sub change_section {
173 13     13 1 22 my ($self, $section) = @_;
174              
175 13         24 $self->{section} = $section;
176              
177 13 100       31 if (!exists $self->{data}{$section}) {
178 11         24 $self->{data}{$section} = {};
179             }
180             }
181              
182             #pod =head2 parse_value_assignment
183             #pod
184             #pod my ($name, $value) = $reader->parse_value_assignment($line, $handle);
185             #pod
186             #pod Given a line of input, this method decides whether the line is a property
187             #pod value assignment. If it is, it returns the name of the property and the value
188             #pod being assigned to it. If the line is not a property assignment, the method
189             #pod returns false.
190             #pod
191             #pod =cut
192              
193             sub parse_value_assignment {
194 3 100   3 1 1579 return ($1, $2) if $_[1] =~ /^\s*([^=\s\pC][^=\pC]*?)\s*=\s*(.*?)\s*$/;
  3     41   38  
  3         38  
  41         254  
195 2         6 return;
196             }
197              
198             #pod =head2 set_value
199             #pod
200             #pod $reader->set_value($name, $value);
201             #pod
202             #pod This method is called whenever an assignment occurs in the file. The default
203             #pod behavior is to change the value of the named property to the given value.
204             #pod
205             #pod =cut
206              
207             sub set_value {
208 39     39 1 60 my ($self, $name, $value) = @_;
209              
210 39         72 $self->{data}{ $self->current_section }{$name} = $value;
211             }
212              
213             #pod =head2 starting_section
214             #pod
215             #pod my $section = Config::INI::Reader->starting_section;
216             #pod
217             #pod This method returns the name of the starting section. The default is: C<_>
218             #pod
219             #pod =cut
220              
221 12     12 1 43 sub starting_section { q{_} }
222              
223             #pod =head2 can_ignore
224             #pod
225             #pod do_nothing if $reader->can_ignore($line, $handle)
226             #pod
227             #pod This method returns true if the given line of input is safe to ignore. The
228             #pod default implementation ignores lines that contain only whitespace or comments.
229             #pod
230             #pod This is run I L.
231             #pod
232             #pod =cut
233              
234             sub can_ignore {
235 65     65 1 94 my ($self, $line, $handle) = @_;
236              
237             # Skip comments and empty lines
238 65 100       347 return $line =~ /\A\s*(?:;|$)/ ? 1 : 0;
239             }
240              
241             #pod =head2 preprocess_line
242             #pod
243             #pod $reader->preprocess_line(\$line);
244             #pod
245             #pod This method is called to preprocess each line after it's read but before it's
246             #pod parsed. The default implementation just strips inline comments. Alterations
247             #pod to the line are made in place.
248             #pod
249             #pod =cut
250              
251             sub preprocess_line {
252 65     65 1 90 my ($self, $line) = @_;
253              
254             # Remove inline comments
255 65         68 ${$line} =~ s/\s+;.*$//g;
  65         121  
256             }
257              
258             #pod =head2 handle_unparsed_line
259             #pod
260             #pod $reader->handle_unparsed_line( $line, $handle );
261             #pod
262             #pod This method is called when the reader encounters a line that doesn't look like
263             #pod anything it recognizes. By default, it throws an exception.
264             #pod
265             #pod =cut
266              
267             sub handle_unparsed_line {
268 2     2 1 6 my ($self, $line, $handle) = @_;
269 2         5 my $lineno = $handle->input_line_number;
270 2         219 Carp::croak "Syntax error at line $lineno: '$line'";
271             }
272              
273             #pod =head2 finalize
274             #pod
275             #pod $reader->finalize;
276             #pod
277             #pod This method is called when the reader has finished reading in every line of the
278             #pod file.
279             #pod
280             #pod =cut
281              
282       9 1   sub finalize { }
283              
284             #pod =head2 new
285             #pod
286             #pod my $reader = Config::INI::Reader->new;
287             #pod
288             #pod This method returns a new reader. This generally does not need to be called by
289             #pod anything but the various C methods, which create a reader object only
290             #pod ephemerally.
291             #pod
292             #pod =cut
293              
294             sub new {
295 12     12 1 1312 my ($class) = @_;
296              
297 12         27 my $self = { data => {}, };
298              
299 12         89 bless $self => $class;
300             }
301              
302             #pod =head1 ORIGIN
303             #pod
304             #pod Originaly derived from L, by Adam Kennedy.
305             #pod
306             #pod =cut
307              
308             1;
309              
310             __END__