File Coverage

lib/Config/Basic.pm
Criterion Covered Total %
statement 86 129 66.6
branch 11 30 36.6
condition 1 6 16.6
subroutine 10 14 71.4
pod 6 7 85.7
total 114 186 61.2


line stmt bran cond sub pod time code
1             ###########################################################
2             # Tie::Countloop package
3             # Gnu GPL2 license
4             #
5             # $Id:: Basic.pm 85 2007-06-13 14:12:01Z fabrice $
6             # $Revision:: 85 $
7             #
8             # Fabrice Dulaunoy
9             ###########################################################
10             # ChangeLog:
11             #
12             ###########################################################=
13              
14             =head1 SYNOPSIS
15              
16             =over 3
17              
18             B
19              
20             A basic config parser
21             for file where a section start at first column and end when a new section appear
22              
23             =back
24              
25             =cut
26              
27             package Config::Basic;
28 1     1   61934 use strict;
  1         7  
  1         75  
29 1     1   12 use Carp;
  1         2  
  1         115  
30 1     1   1074 use IO::All;
  1         24681  
  1         10  
31              
32 1     1   1014 use fields qw{ sections target traillers headers };
  1         1731  
  1         6  
33 1     1   86 use vars qw($VERSION);
  1         2  
  1         78  
34              
35             #$VERSION = do { my @rev = ( q$Revision: 85 $ =~ /\d+/g ); sprintf "%d." . "%d" x $#rev, @rev };
36             $VERSION = do { my @rev = ( q$Revision: 85 $ =~ /\d+/g ); sprintf "1.%02d", @rev };
37              
38 1     1   1222 use Data::Dumper;
  1         8895  
  1         108  
39             ###########################################################################
40              
41             ###########################################################################
42             ### class creator ###
43             ###########################################################################
44              
45             =head1 METHODS
46            
47             OO interface
48              
49             =head2 new
50              
51             =over
52              
53             Create a new parser
54              
55             =over
56              
57             "-sections" = an ARRAY with all possible section
58             "-target" a file name to parse or a ref ARRAY with the data to parse
59             "-data" is a synonym of "-target"
60             "-file" is a synonym of "-target"
61              
62             t
63             "-traillers" is an ARRAY with all regular expresiion allowed for a trailler (to skip in the preceding section )
64             "-headers" is an ARRAY with all regular expresiion allowed for a headers (to add to the following section)
65              
66             the trailler and headers could be retrived into the parsed data
67             these 2 parameters are optionals
68              
69             my $a = B->new(
70             -data => \@data,
71             -sections => [ 'global', 'listen', 'defaults' ],
72             );
73              
74             =back
75              
76             =back
77              
78             =cut
79              
80             sub new
81             {
82 1     1 1 26035 my $self = shift;
83 1     1   9 no strict "refs";
  1         2  
  1         1084  
84 1         8 $self = fields::new( $self );
85 1         3777 $self->{ sections } = { @_ }->{ -sections };
86 1         7 $self->{ traillers } = { @_ }->{ -traillers };
87 1         7 $self->{ headers } = { @_ }->{ -headers };
88 1   33     19 $self->{ target } = { @_ }->{ -file } || { @_ }->{ -data } || { @_ }->{ -target };
89 1         6 return $self;
90             }
91             ###########################################################################
92              
93             ###########################################################################
94             ### get/set method for the target object ###
95             ###########################################################################
96              
97             =head2 target
98              
99             get/set the target in use
100              
101             my $target = $a->target( ) ; # return the current target
102             my $target = $a->target( "new.cfg" ) ; # change the target to the file "new.cfg"
103             # and return the new target (here the file name)
104             my $target = $a->target( \@data ); # change the target to the a ARRAY ref
105             # and return the new target (here the ARRAY ref)
106              
107             =cut
108              
109             sub target
110             {
111 0     0 1 0 my $self = shift;
112 0         0 my $object = shift;
113 0 0       0 if ( $object )
114             {
115 0         0 $self->{ target } = $object;
116             }
117 0         0 return $self->{ target };
118             }
119             ###########################################################################
120              
121             ###########################################################################
122             ### get/set method for the sections ###
123             ##" sections are a ARRAY of all possible section ###
124             ###########################################################################
125              
126             =head2 sections
127              
128             get/set the sections to use
129              
130              
131             my $sect = $a->sections( ) ; # return a ARRAY ref with the current sections
132             my $new_sect = $a->sections( [ 'all', 'server' ] ); # create a new set of sections and
133             # return a ARRAY ref with the current sections
134              
135             =cut
136              
137             sub sections
138             {
139 1     1 1 6 my $self = shift;
140 1         1 my $object = shift;
141 1 50       4 if ( $object )
142             {
143 0         0 $self->{ sections } = $object;
144             }
145 1         3 return $self->{ sections };
146             }
147             ###########################################################################
148              
149             ###########################################################################
150             ### get/set method for the traillers to skip ###
151             ##" traillers are a ARRAY of all possible section ###
152             ###########################################################################
153              
154             =head2 traillers
155              
156             get/set the traillers to skip
157             if at the end of a section, lines match one of these REGEX
158             these lines are not include in the section.
159             This allow to keep blank line and comment inside a section
160             and get the real ending of the section (e.g. to allow an insert)
161              
162             my $sect = $a->trailler( ) ; # return a ARRAY ref with the current traillers
163             my $new_sect = $a->trailler( [ '^\s*$', '^#' ] ) ; # create a new set of traillers and
164             # return a ARRAY ref with the current traillers
165              
166             =cut
167              
168             sub traillers
169             {
170 0     0 1 0 my $self = shift;
171 0         0 my $object = shift;
172 0 0       0 if ( $object )
173             {
174 0         0 $self->{ traillers } = $object;
175             }
176              
177 0         0 return $self->{ traillers };
178             }
179             ###########################################################################
180              
181             ###########################################################################
182             ### get/set method for the header to add ###
183             ### header with the following section section ###
184             ### this allow to keep comment with the section ###
185             ###########################################################################
186              
187             =head2 traillers
188              
189             get/set the headers to at
190             if before of a section, lines match one of these REGEX
191             these lines are add in the section (under the tag "start_headers").
192             This allow to keep comment to belong to a following section
193             and get the real starting of the section (e.g. to allow an insert)
194              
195             my $sect = $a->header( ); # return a ARRAY ref with the current headers
196             my $new_sect = $a->headers( [ '^\s*$', '^#' ] ) ; # create a new set of headers and
197             # return a ARRAY ref with the current headers
198              
199             =cut
200              
201             sub headers
202             {
203 0     0 0 0 my $self = shift;
204 0         0 my $object = shift;
205 0 0       0 if ( $object )
206             {
207 0         0 $self->{ headers } = $object;
208             }
209              
210 0         0 return $self->{ headers };
211             }
212             ###########################################################################
213              
214             ###########################################################################
215             ### method to retrieve a section ###
216             ### Param: a ref hash with start and end line ###
217             ### { ###
218             ### 'end' => 25, ###
219             ### start' => 8 ###
220             ### } ###
221             ### ###
222             ###########################################################################
223              
224             =head2 get_section
225              
226             method to retrieve a section.
227             the method expect a ref to a HASH with { start => "start_line" , end => "end_line" }
228              
229              
230             my $se = $a->get_section( $res->{ listen }[1] ); # return 3 elements:
231             start line (sithout headers)
232             end line (without traillers)
233             ARRAY ref with the content of the section
234              
235             =cut
236              
237             sub get_section
238             {
239 0     0 1 0 my $self = shift;
240 0         0 my $object = shift;
241 0         0 my @all;
242 0 0       0 if ( !( ref $self->{ target } ) )
243             {
244 0         0 @all = io( ( $self->{ target } ) )->chomp->slurp;
245             }
246             else
247             {
248 0         0 @all = @{ $self->{ target } };
  0         0  
249             }
250 0         0 my @section = splice @all, $object->{ start }, $object->{ end } - $object->{ start } + 1;
251              
252 0         0 return $object->{ start }, $object->{ end }, \@section;
253              
254             }
255             ###########################################################################
256              
257             ###########################################################################
258             ### parse the target and return a ref to a hash ###
259             ### where each section contain a array of hash ###
260             ### with start and end line (fisrt line = 0) ###
261             ### ###
262             ### { 'global' => [ ###
263             ### { ###
264             ### 'end' => 8, ###
265             ### 'start' => 0 ###
266             ### } ###
267             ### ] ###
268             ### } ###
269             ###########################################################################
270              
271             =head2 parse
272              
273             method to parse a target
274             the method return a ref to a HASH.
275             Each key are a section.
276             Each value contain a ref to an ARRAY with a ref to a HASH for each section seen in the target
277             There are for key in each section descrition
278             start = the line where the section start
279             end = the line where the section end without the traillers part if defined
280             start_header = the line where the section start included the header if defined
281             end_trailler = the line where the section end with the traillers part if defined
282            
283             my $se = $a->get_section( $res->{ listen }[1] ); # return ARRAY ref with the content of the second section 'listen'
284              
285             =cut
286              
287             sub parse
288             {
289 1     1 1 8 my $self = shift;
290 1         3 my %sect;
291 1         2 foreach ( @{ ( $self->{ sections } ) } )
  1         4  
292             {
293 4         13 $sect{ $_ } = [];
294             }
295              
296 1         3 my @all;
297 1 50       6 if ( !( ref $self->{ target } ) )
298             {
299 0         0 @all = io( ( $self->{ target } ) )->chomp->slurp;
300              
301             }
302             else
303             {
304 1         2 @all = @{ $self->{ target } };
  1         16  
305             }
306              
307 1         2 my $line_nbr = -1;
308 1         2 my $seen;
309             my $start;
310 0         0 my $end;
311 0         0 my $seen_regex;
312 1         2 my $traillers = 1;
313 1         2 my $headers = 0;
314 1         1 my $traillers_regex;
315             my $headers_regex;
316 0         0 my $head;
317 0         0 my $head_start;
318 0         0 my $head_end;
319              
320 1 50       8 if ( defined $self->{ traillers } )
321             {
322 0         0 $traillers_regex = "(" . ( join ")|(", @{ $self->{ traillers } } ) . ")";
  0         0  
323             }
324 1 50       4 if ( defined $self->{ headers } )
325             {
326 0         0 $headers_regex = "(" . ( join ")|(", @{ $self->{ headers } } ) . ")";
  0         0  
327             }
328 1         1 my $old_headers = 0;
329 1         3 foreach my $line ( @all )
330             {
331 111         102 $line_nbr++;
332 111         109 foreach my $regex ( @{ $self->{ sections } } )
  111         170  
333             {
334 444 100       3670 if ( $line =~ m/^($regex)/g )
335             {
336              
337 6 100       11 if ( $seen )
338             {
339 5         57 $end = $line_nbr;
340 5         5 my @tmp = @{ $sect{ $seen_regex } };
  5         11  
341 5         6 my %range;
342 5         16 $range{ start } = $start;
343 5         6 $range{ end } = $end - $traillers;
344 5         9 $range{ end_traillers } = $end - 1;
345 5         6 $range{ start_headers } = $start - $old_headers;
346 5         5 $traillers = 1;
347 5         7 push @tmp, \%range;
348 5         8 $sect{ $seen_regex } = \@tmp;
349 5         6 $start = $line_nbr;
350 5         6 $seen_regex = $regex;
351 5         11 $old_headers = $headers;
352             }
353             else
354             {
355 1         1 $seen_regex = $regex;
356 1         2 $start = $line_nbr;
357 1         3 $seen ^= 1;
358             }
359             }
360              
361             }
362              
363 111 50       257 if ( defined $self->{ headers } )
364             {
365 0         0 my $line_tmp = $line;
366 0 0       0 if ( ( $line_tmp =~ /($headers_regex)/g ) )
367             {
368 0         0 $headers++;
369             }
370             else
371             {
372 0         0 $headers = 0;
373             }
374             }
375              
376 111 50       220 if ( defined $self->{ traillers } )
377             {
378 0         0 my $line_tmp = $line;
379 0 0 0     0 if ( $seen && ( $line_tmp =~ m/($traillers_regex)/g ) )
380             {
381 0         0 $traillers++;
382             }
383             else
384             {
385 0         0 $traillers = 1;
386             }
387             }
388             }
389              
390 1 50       6 if ( $seen )
391             {
392 1         2 $line_nbr++;
393 1         1 my @tmp = @{ $sect{ $seen_regex } };
  1         3  
394 1         2 my %range;
395 1         3 $range{ start } = $start;
396 1         2 $range{ end } = $line_nbr - $traillers;
397 1         3 $range{ end_traillers } = $line_nbr - 1;
398 1         3 $range{ start_headers } = $start - $old_headers;
399 1         2 push @tmp, \%range;
400 1         3 $sect{ $seen_regex } = \@tmp;
401             }
402 1         11 return \%sect;
403             }
404             ###########################################################################
405              
406             1;
407              
408             __END__