File Coverage

blib/lib/Config/INIPlus.pm
Criterion Covered Total %
statement 165 259 63.7
branch 97 158 61.3
condition 22 57 38.6
subroutine 16 25 64.0
pod 12 12 100.0
total 312 511 61.0


line stmt bran cond sub pod time code
1             package Config::INIPlus;
2              
3 2     2   145114 use warnings;
  2         7  
  2         88  
4 2     2   10 use strict;
  2         5  
  2         68  
5              
6 2     2   2275 use IO::File;
  2         16513  
  2         318  
7 2     2   1986 use IO::String;
  2         5752  
  2         76  
8 2     2   20 use Scalar::Util qw(blessed);
  2         4  
  2         171  
9 2     2   12 use Carp qw(croak confess);
  2         3  
  2         740  
10              
11             =head1 NAME
12              
13             Config::INIPlus - Read and write INI-style config files with structure extensions
14              
15             =cut
16              
17             our $VERSION = '1.0.3';
18              
19             # Some regexes we use for matching
20             my $sp = qr/(?:[ ]|\t)+/; # Space characters
21             my $osp = qr/(?:[ ]|\t)*/; # Optional space characters
22             my $eol = qr/(?:\r?\n)/x; # End of line
23             my $eolc = qr/(?:;.*)?$eol/x; # End of line with optional comment
24              
25             # Modes for calls to ->new
26 2     2   15 use constant TOP_MODE => 0; # When new is called non-recursively (i.e.,
  2         5  
  2         157  
27             # from the topmost context)
28 2     2   10 use constant HASH_MODE => 1; # When processing a sub-hash
  2         3  
  2         81  
29 2     2   10 use constant ARRAY_MODE => 2; # When processing a sub-array
  2         2  
  2         92  
30 2     2   19 use constant STRING_MODE => 3; # When processing a multi-line string
  2         4  
  2         7846  
31              
32             =head1 SYNOPSIS
33              
34             INIPlus is a configurtion file format based on INI which supports multi-line
35             strings and nesting of arrays and hashes. This is useful if you start a
36             project using INI files, but realize you need nested data in your
37             configurations and want to support extended configurations without
38             breaking backward compatibility with existing config files.
39              
40             =head2 The INIPlus Config File
41              
42             ; Comment
43             Key=Value ; End of line comment
44             Key2="Multi
45             Line
46             Value" ; Post-multi-line comment
47            
48             [Section]
49             Foo=This is a foo
50             Hash {
51             Bar=Hey it's a bar
52             Baz="Is Baz at the bar?"
53             }
54             Array (
55             Value One
56             "Value Two"
57             "Value
58             Three
59             Is multi-line!"
60             )
61              
62             The hashes and arrays can be nested any number of levels deep:
63              
64             Hash {
65             ArrayOfSubhashes (
66             {
67             Key1=Val1
68             Key2=Val2
69             }
70             {
71             HeyAnotherArray (
72             Value1
73             Value2
74             Value3
75             )
76             }
77             )
78             }
79            
80             =head2 Creating a config object
81              
82             use Config::INIPlus;
83            
84             # Create the config object from a file
85             $cfg = Config::INIPlus->new( file => 'foo.ini' );
86            
87             # Create the config object from a filehandle
88             $filehandle = IO::File->new('file.ini');
89             $cfg = Config::INIPlus->new( fh => $filehandle );
90            
91             # Create the config from a string
92             $string = <
93             Key1=Val1
94             Key2=Val2
95             ; ...
96             EOF
97            
98             $cfg = Config::INIPlus->new( string => $string );
99              
100             =head2 Extracting the contents of a config object
101              
102             # Gets a non-sectioned value (like "Key2" in the example INI above)
103             my $val = $cfg->get( 'KeyName' );
104            
105             # Gets a value from a section (e.g., "Foo" under "Section" in
106             # the example above)
107             my $val = $cfg->get( 'KeyName', 'SectionName' );
108            
109             # Gets the entire structure as a hash reference
110             my $hash = $cfg->as_hashref();
111            
112             # Get one section as a hash reference (e.g., "Section" in the
113             # exampe INI above)
114             my $sec = $cfg->section_as_hashref( 'SectionName' );
115              
116             =head2 Modifying a config object
117              
118             # Set a non-sectioned value
119             $cfg->set( 'KeyName', 'KeyValue' );
120            
121             # Set a value for a key within a section
122             $cfg->set( 'KeyName', 'KeyValue', 'SectionName' );
123            
124             # Remove a non-sectioned key (and respective value)
125             $cfg->del( 'KeyName' );
126            
127             # Remove a sectioned key
128             $cfg->del( 'KeyName', 'SectionName' );
129            
130             # Add a section
131             $cfg->add_section( 'SectionName' );
132            
133             # Remove a section
134             $cfg->del_section( 'SectionName' );
135            
136             =head2 Getting the config object as text / writing to a file
137              
138             # Get the configuration as a string
139             $string = $cfg->as_string;
140            
141             # Write the configuration back into the file it was originally
142             # read from
143             $cfg->write;
144              
145             # Write the configuration to a specific file
146             $cfg->write( 'filename.ini' );
147              
148             =head1 METHODS
149              
150             =head2 Config::INIPlus->new( file => 'filename' )
151              
152             =head2 Config::INIPlus->new( fh => $perl_filehandle )
153              
154             =head2 Config::INIPlus->new( string => $string_config )
155              
156             Creates a new config object. You can use a filename with the 'file' paramter,
157             a IO::Handle style filehandle using the 'fh', or pull from the entire INIPlus
158             configuration loaded into a string using the 'string' paramter.
159              
160             =cut
161              
162             sub new {
163              
164 20     20 1 3304 my $pkg = shift; # Package
165 20         160 my %p = @_; # Parameters
166              
167             # Default some of the parameters
168 20 100       60 unless ( defined $p{'debug'} ) { $p{'debug'} = 0; }
  4         9  
169 20 100       55 unless ( defined $p{'file'} ) { $p{'file'} = ''; }
  2         3  
170 20 100       99 unless ( defined $p{'_mode'} ) { $p{'_mode'} = TOP_MODE; }
  4         11  
171              
172             # Are we being called from the topmost context without a filehandle?
173 20 100 66     64 if ( ( $p{'_mode'} == TOP_MODE ) && ( not defined $p{'fh'} ) ) {
174 4 100       15 if ( defined $p{'string'} ) {
    50          
175              
176             # Turn the string we've been passed into a filehandle
177 2         17 $p{'fh'} = IO::String->new( $p{'string'}, 'r' );
178 2 50       98 if ( not defined $p{'fh'} ) {
179 0         0 croak "Error opening string $p{'string'}: $!";
180             }
181             }
182             elsif ( $p{'file'} ) {
183              
184             # Open the filename we've been passed to a new filehandle
185 2         41 $p{'fh'} = IO::File->new( $p{'file'}, 'r' );
186 2 50       265 if ( not defined $p{'fh'} ) {
187 0         0 croak "Error opening file $p{'file'}: $!";
188             }
189             }
190             }
191              
192             # Check that the filehandle we should have at this point looks like a
193             # filehandle (I used ->can instead of ->isa since many handle interfaces
194             # don't actually inherit from IO::Handle but all of the ones which work
195             # with this module will support the method 'getline'
196 20 50 33     51 unless ( defined( $p{'fh'} ) && eval { $p{'fh'}->can('getline') } ) {
  20         129  
197 0         0 croak "Must be called with a filename, string or filehandle";
198             }
199              
200 20         27 my $struct; # This contains whatever we're going to return up the
201             # chain: a hash, array or string
202 20         31 my @sections = (); # This is a list of INI file sections
203 20         26 my $section = ''; # This is the current section being processed
204 20         22 my $line = ''; # The current line being processed
205              
206 20         66 local $/ = "\012"; # Unix newline... will catch DOS newlines too.
207             # Local is necessary if the parent context has set
208             # $/ to something different
209              
210             # Keep processing the FH until we hit the end
211 20         68 while ( not $p{'fh'}->eof ) {
212              
213 136         2814 $line = $p{'fh'}->getline;
214              
215             # If debugging is enabled then show the line being processed
216 136 50       3338 $p{'debug'} && _debug( $p{'fh'}, $line, "line - $line" );
217              
218 136 100 100     553 if ( $p{'_mode'} == TOP_MODE || $p{'_mode'} == HASH_MODE ) {
    100          
    50          
219              
220             # Process in a HASH style context
221 88 100       195 unless ( defined $struct ) { $struct = {}; }
  12         25  
222              
223 88         102 my $name;
224             my $val;
225              
226 88 100       4601 if ( $line =~ m/ ^ $osp $eolc $ /x ) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
227 24 50       55 $p{'debug'}
228             && _debug( $p{'fh'}, "Skipping blank/comment line" );
229             }
230             elsif ( $line =~ m/ ^ $osp \[ $osp (.+) $osp \] $osp $eolc $ /x )
231             {
232              
233             # Process a [section] definition
234 8         17 $section = $1;
235 8 50       23 if ( $section =~ m/^_/ ) {
236 0         0 croak( $p{'fh'}, $p{'file'},
237             "Sections cannot begin with underscore" );
238             }
239 8 50       17 if ( $p{'_mode'} == TOP_MODE ) {
240 8         15 push @sections, $section;
241             }
242             else {
243 0         0 croak _error( $p{'fh'}, $line, $p{'file'},
244             "Unexpected section definition $section during subhash"
245             );
246             }
247             }
248             elsif ( $line =~ m/ ^ $osp (.*?) $osp \{ $osp $eolc $ /x ) {
249 4         9 $name = $1;
250 4         43 $val = $pkg->new( %p, '_mode' => HASH_MODE );
251             }
252             elsif ( $line =~ m/ ^ $osp (.*?) $osp \( $osp $eolc $ /x ) {
253 4         8 $name = $1;
254 4         30 $val = $pkg->new( %p, '_mode' => ARRAY_MODE );
255             }
256             elsif (
257             $line =~ m/ ^ $osp (.*?) $osp \= $osp \"( [^"]* $eol ) $ /x )
258             {
259 4         9 $name = $1;
260 4         18 $val = _fix_newlines(
261             $2 . $pkg->new( %p, '_mode' => STRING_MODE ) );
262             }
263             elsif (
264             $line =~ m/ ^ $osp (.*?) $osp \= $osp \"([^"]+)\" $eolc $ /x )
265             {
266 3         7 $name = $1;
267 3         6 $val = $2;
268             }
269             elsif (
270             $line =~ m/ ^ $osp (.*?) $osp \= $osp (.*?) $osp $eolc $ /x )
271             {
272 33         68 $name = $1;
273 33         51 $val = $2;
274             }
275             elsif ( $line =~ m/ ^ $osp ( \} | \) )$osp $eolc $ /x ) {
276 8         17 my $char = $1;
277 8 50 33     46 if ( ( $p{'_mode'} != TOP_MODE ) && ( $char eq '}' ) ) {
278              
279             # We should only get to this line if we're nested
280 8 50       29 $p{'debug'}
281             && _debug( $p{'fh'},
282             "Returning nested hash back up the chain" );
283 8         55 return $struct;
284             }
285              
286             # We saw a } or ) that doesn't belong here.
287 0         0 croak _error( $p{'fh'}, $line, $p{'file'},
288             "Unexpected $char" );
289             }
290             else {
291 0         0 croak _error( $p{'fh'}, $line, $p{'file'}, "Malformed line" );
292             }
293              
294 80 100 66     376 if ( defined($name) && defined($val) ) {
295 48 100 100     189 if ( ( $p{'_mode'} == TOP_MODE ) && $section ) {
296 16         92 $struct->{$section}{$name} = $val;
297             }
298             else {
299 32         185 $struct->{$name} = $val;
300             }
301             }
302              
303             } ## end if ( $p{'_mode'} == TOP_MODE || $p{'_mode'} == HASH_MODE )
304             elsif ( $p{'_mode'} == ARRAY_MODE ) {
305              
306             # Process in an ARRAY style context
307 36 100       75 unless ( defined $struct ) { $struct = []; }
  4         9  
308              
309 36         36 my $val;
310              
311 36 50       2033 if ( $line =~ m/ ^ $osp $eolc $/x ) {
    50          
    100          
    50          
    50          
    50          
    50          
    100          
    50          
312 0 0       0 $p{'debug'}
313             && _debug( $p{'fh'}, "Skipping blank/comment line" );
314             }
315             elsif ( $line =~ m/ ^ $osp \[ $osp (.+) $osp \] $osp $eolc $ /x )
316             {
317 0         0 croak _error( $p{'fh'}, $line, $p{'file'},
318             "Unexpected section definition $1 during subarray" );
319             }
320             elsif ( $line =~ m/ ^ $osp \{ $osp $eolc $ /x ) {
321 4         23 $val = $pkg->new( %p, '_mode' => HASH_MODE );
322             }
323             elsif ( $line =~ m/ ^ $osp \( $osp $eolc $ /x ) {
324 0         0 $val = $pkg->new( %p, '_mode' => ARRAY_MODE );
325             }
326             elsif ( $line =~ m/ ^ $osp \"( [^"]* $eol ) $ /x ) {
327 0         0 $val = _fix_newlines(
328             $1 . $pkg->new( %p, '_mode' => STRING_MODE ) );
329             }
330             elsif ( $line =~ m/ ^ $osp \"([^"]+)\" $osp $eolc $ /x ) {
331 0         0 $val = $1;
332             }
333             elsif ( $line =~ m/ ^ $osp \} $osp $eolc $ /x ) {
334              
335             # We saw a } that doesn't belong here.
336 0         0 croak _error( $p{'fh'}, $line, $p{'file'}, "Unexpected }" );
337             }
338             elsif ( $line =~ m/ ^ $osp \) $osp $eolc $ /x ) {
339              
340             # We should only get to this line if we're nested
341 4 50       11 $p{'debug'}
342             && _debug( $p{'fh'},
343             "Returning nested array back up the chain" );
344 4         22 return $struct;
345             }
346             elsif ( $line =~ m/ ^ $osp (.*?) $osp $eolc $ /x ) {
347 28         60 $val = $1;
348             }
349             else {
350 0         0 croak _error( $p{'fh'}, $line, $p{'file'}, "Malformed line" );
351             }
352              
353 32         224 push @$struct, $val;
354             }
355             elsif ( $p{'_mode'} == STRING_MODE ) {
356              
357             # Process in a multi-line string context
358 12 100       25 unless ( defined $struct ) { $struct = ''; }
  4         6  
359              
360 12 100       139 if ( $line =~ m/ ^ ([^"]*) " $osp $eolc $ /x ) {
    50          
361 4         118 return $struct . $1;
362             }
363             elsif ( $line =~ m/"/ ) {
364 0         0 croak _error( $p{'fh'}, $line, $p{'file'},
365             "Unexpected mid-string quote" );
366             }
367             else {
368 8         36 $struct .= $line;
369             }
370              
371             }
372             else {
373 0         0 croak _error( $p{'fh'}, $line, $p{'file'},
374             "Unknown mode: $p{'_mode'}" );
375             }
376             } ## end while ( not $p{'fh'}->eof )
377              
378             # If we got to the end of the file, but we weren't done processing a
379             # context other than top, then the file ended before we expected.
380 4 50       55 if ( $p{'_mode'} != TOP_MODE ) {
381 0         0 croak _error( $p{'fh'}, $line, $p{'file'}, "Premature end of file" );
382             }
383              
384             # Weed out any duplicate sections
385 4         10 my %sections_index = (); # Keeps an index of unique sections
386 4         5 my @sections_flattened = (); # Keeps the final list of sections in order
387 4         10 foreach my $section (@sections) {
388 8 50       18 next if ( exists $sections_index{$section} );
389 8         15 $sections_index{$section} = undef;
390 8         18 push @sections_flattened, $section;
391             }
392              
393             # Save metadata into the object
394 4         12 $struct->{'_file'} = $p{'file'}; # Filename, used for
395             # writing the file
396             # back out
397 4         9 $struct->{'_debug'} = $p{'debug'}; # Enable/disable
398             # debugging
399 4         11 $struct->{'_sections'} = \@sections_flattened; # List of sections in
400             # order
401 4         9 $struct->{'_sections_index'} = \%sections_index; # List of unique
402             # sections
403              
404             # We're done constructing the object, return it back up the chain
405 4         71 bless $struct, $pkg;
406              
407             } ## end sub new
408              
409             # Print debugging information to STDERR
410             sub _debug {
411              
412 0     0   0 my $fh = shift; # For the line number
413 0         0 my $message = shift; # What we're reporting
414              
415 0         0 print STDERR __PACKAGE__
416             . " Line "
417             . $fh->input_line_number . ' '
418             . $message . "\n";
419             }
420              
421             # Format an error message with context information about the line
422             # number and contents for passing to croak
423             sub _error {
424              
425 0     0   0 my $fh = shift; # For the line number
426 0         0 my $line = shift; # For the contents of the line
427 0         0 my $file = shift; # What file we're processing (if available)
428 0         0 my $message = shift; # What we're complaining about
429              
430 0         0 chomp $line;
431              
432 0         0 $message
433             .= " at input line " . $fh->input_line_number . " '" . $line . "'";
434 0 0       0 if ($file) { $message .= " in file $file"; }
  0         0  
435              
436 0         0 return $message;
437              
438             }
439              
440             =head2 $cfg->as_hashref
441              
442             Returns the entire INIPlus structure as a reference to a hash.
443              
444             =cut
445              
446             sub as_hashref {
447              
448 4     4 1 1165 my $self = shift;
449 4         6 my $out = shift;
450              
451 4         22 foreach my $key ( keys %$self ) {
452 36 100       85 next if ( $key =~ m/^_/ );
453 20         141 $out->{$key} = $self->{$key};
454             }
455              
456 4         25 return $out;
457             }
458              
459             =head2 $cfg->get( name [ , section ] )
460              
461             Gets the value of a particular entry. For entries within a section, the
462             section name must be provided.
463              
464             =cut
465              
466             sub get {
467              
468 0     0 1 0 my $self = shift;
469 0         0 my $name = shift;
470 0         0 my $section = shift;
471              
472 0 0 0     0 if ( defined($section) && ( $section ne '' ) ) {
473 0         0 return $self->{$section}{$name};
474             }
475             else {
476 0         0 return $self->{$name};
477             }
478             }
479              
480             =head2 $cfg->set( name, val [ , section ] )
481              
482             Sets the value of a particular entry. If an existing entry exists it will be
483             overwritten. For entries within a section, the section name must be provided.
484              
485             =cut
486              
487             sub set {
488              
489 0     0 1 0 my $self = shift;
490 0         0 my $name = shift;
491 0         0 my $val = shift;
492 0         0 my $section = shift;
493              
494 0 0 0     0 if ( ( not defined $name ) || ( $name eq '' ) ) {
495 0         0 croak "Name must be provided";
496             }
497 0 0       0 unless ( defined($val) ) {
498 0         0 croak "Value must be defined";
499             }
500 0 0       0 if ( $name =~ m/^_/ ) {
501 0         0 croak "Keys can not begin with underscore";
502             }
503              
504 0 0 0     0 if ( defined($section) && ( $section ne '' ) ) {
505 0         0 $self->{$section}{$name} = $val;
506             }
507             else {
508 0         0 $self->{$name} = $val;
509             }
510             }
511              
512             =head2 $cfg->del( name [ , section ] );
513              
514             Removes an entry. For entries within a section, the section name must be
515             provided.
516              
517             =cut
518              
519             sub del {
520              
521 0     0 1 0 my $self = shift;
522 0         0 my $name = shift;
523 0         0 my $section = shift;
524              
525 0 0 0     0 if ( ( not defined $name ) || ( $name eq '' ) ) {
526 0         0 croak "Name must be provided";
527             }
528              
529 0 0 0     0 if ( defined($section) && ( $section ne '' ) ) {
530 0         0 delete $self->{$section}{$name};
531             }
532             else {
533 0         0 delete $self->{$name};
534             }
535              
536             }
537              
538             =head2 $cfg->add_section( section )
539              
540             Adds a new section.
541              
542             =cut
543              
544             sub add_section {
545              
546 0     0 1 0 my $self = shift;
547 0         0 my $section = shift;
548              
549 0 0 0     0 if ( ( not defined $section ) || ( $section eq '' ) ) {
550 0         0 croak "Section must be provided";
551             }
552 0 0       0 if ( $section =~ m/^_/ ) {
553 0         0 croak "Sections cannot begin with underscore";
554             }
555              
556 0 0       0 if ( $self->section_exists($section) ) {
557 0         0 croak "Section $section already exists";
558             }
559 0 0       0 if ( defined $self->{$section} ) {
560 0         0 croak "Cannot create a conflicting top-level section when the same "
561             . "key name $section already exists";
562             }
563             else {
564 0         0 $self->{$section} = {};
565 0         0 push @{ $self->{'_sections'} }, $section;
  0         0  
566 0         0 $self->{'_sections_index'}{$section} = undef;
567             }
568             }
569              
570             =head2 $cfg->section_exists( section )
571              
572             Returns true if a section exists, false if it does not.
573              
574             =cut
575              
576             sub section_exists {
577              
578 5     5 1 7 my $self = shift;
579 5         8 my $section = shift;
580              
581 5 50 33     32 if ( ( not defined $section ) || ( $section eq '' ) ) {
582 0         0 croak "Section must be provided";
583             }
584              
585 5         26 return exists $self->{'_sections_index'}{$section};
586             }
587              
588             =head2 $cfg->sections()
589              
590             Returns a list of all of the sections in the file
591              
592             =cut
593              
594             sub sections {
595              
596 0     0 1 0 my $self = shift;
597              
598 0         0 return @$self->{'_sections'};
599             }
600              
601             =head2 $cfg->del_section( section )
602              
603             Removes a section.
604              
605             =cut
606              
607             sub del_section {
608              
609 0     0 1 0 my $self = shift;
610 0         0 my $section = shift;
611              
612 0 0 0     0 if ( ( not defined $section ) || ( $section eq '' ) ) {
613 0         0 croak "Section must be provided";
614             }
615              
616 0         0 delete $self->{$section};
617 0         0 delete $self->{'_sections_index'}{$section};
618 0         0 $self->{'_sections'}
619 0         0 = [ grep { !/^\Q$section\E$/ } @$self->{'_sections'} ];
620             }
621              
622             =head2 $cfg->section_as_hashref( section )
623              
624             Retrieves a section as a reference to a hash.
625              
626             =cut
627              
628             sub section_as_hashref {
629              
630 0     0 1 0 my $self = shift;
631 0         0 my $section = shift;
632              
633 0         0 return $self->{$section};
634              
635             }
636              
637             =head2 $cfg->write( [ $filename ] )
638              
639             Writes out the configuration to a file to disk. If a filename is provided,
640             the configuration is written to that file. If the object was read from a
641             source filename and no filename is provided to the write method, then the
642             original file is overwritten. The file written will not include the
643             formatting or comments of the original file read by this object.
644              
645             =cut
646              
647             sub write {
648 1     1 1 844 my $self = shift;
649 1         4 my $file = shift;
650              
651 1 50 33     10 unless ( defined($file) && $file ) {
652 0         0 $file = $self->{'file'};
653 0 0 0     0 unless ( defined($file) && $file ) {
654 0         0 croak "You must provide a filename to write if the read "
655             . "INIPlus file does not have an associated file name";
656             }
657             }
658              
659 1         11 my $fh = IO::File->new( $file, 'w' );
660              
661 1         138 $fh->print( $self->as_string );
662              
663             }
664              
665             =head2 $cfg->as_string()
666              
667             Retrieves the configuration as a string. This will not include the formatting
668             or comments of the original file read by this object.
669              
670             =cut
671              
672             sub as_string {
673 5     5 1 7 my $obj = shift;
674 5         12 my %p = @_;
675              
676 5 100       13 if ( not defined $p{indent_level} ) {
677 2         5 $p{indent_level} = 0;
678             }
679              
680 5 50       13 if ( not defined $p{indent_string} ) {
681 5         8 $p{indent_string} = ' ';
682             }
683              
684 5         15 my $indent = $p{indent_string} x $p{indent_level};
685              
686 5         6 my $out = '';
687              
688 5   66     112 my $at_root = blessed($obj) && $obj->isa('Config::INIPlus');
689              
690 5 100 100     28 if ( $at_root || ( ref $obj eq 'HASH' ) ) {
    50          
691 4         4 foreach my $key ( keys %{$obj} ) {
  4         12  
692 17 100       42 next if ( $key =~ m/^_/ );
693 13 100 100     44 next if ( $at_root && $obj->section_exists($key) );
694 12         19 my $value = $obj->{$key};
695 12 100       59 if ( ref $value eq 'ARRAY' ) {
    100          
    50          
    100          
696 1         3 $out .= $indent . $key . " (\n";
697 1         18 $out .= as_string( $value,
698             'indent_level' => $p{indent_level} + 1 );
699 1         14 $out .= $indent . ")\n";
700             }
701             elsif ( ref $value eq 'HASH' ) {
702 1         3 $out .= $indent . $key . " {\n";
703 1         14 $out .= as_string( $value,
704             'indent_level' => $p{indent_level} + 1 );
705 1         3 $out .= $indent . "}\n";
706             }
707             elsif ( $value =~ m/"/ ) {
708 0         0 croak "Strings with quotes cannot be serialized";
709             }
710             elsif ( $value =~ m/$eol/ ) {
711 1         4 $out .= $indent . "$key=\"$value\"\n";
712             }
713             else {
714 9         29 $out .= $indent . "$key=$value\n";
715             }
716             }
717 4         8 $out .= "\n";
718 4 100       10 if ($at_root) {
719 1         2 foreach my $section ( @{ $obj->{'_sections'} } ) {
  1         3  
720 2         9 $out .= "[$section]\n";
721 2 100       8 if ( defined $obj->{$section} ) {
722 1         8 $out .= as_string( $obj->{$section} );
723             }
724 2         5 $out .= "\n";
725             }
726             }
727             }
728             elsif ( ref $obj eq 'ARRAY' ) {
729 1         5 foreach my $value (@$obj) {
730 8 50       34 if ( ref $value eq 'ARRAY' ) {
    100          
    50          
    50          
731 0         0 $out .= $indent . "(\n";
732 0         0 $out .= as_string( $value,
733             'indent_level' => $p{indent_level} + 1 );
734 0         0 $out .= $indent . ")\n";
735             }
736             elsif ( ref $value eq 'HASH' ) {
737 1         2 $out .= $indent . "{\n";
738 1         8 $out .= as_string( $value,
739             'indent_level' => $p{indent_level} + 1 );
740 1         3 $out .= $indent . "}\n";
741             }
742             elsif ( $value =~ m/"/ ) {
743 0         0 croak "Strings with quotes cannot be serialized";
744             }
745             elsif ( $value =~ m/$eol/ ) {
746 0         0 $out .= $indent . "\"$value\"\n";
747             }
748             else {
749 7         15 $out .= $indent . "$value\n";
750             }
751             }
752             }
753             else {
754 0         0 confess "Only INIPlus objects consisting of perl native hashes and "
755             . "arrays can be serialized by as_string";
756             }
757              
758 5         23 return $out;
759              
760             }
761              
762             # Takes a string and translates any newlines into whatever the local system's
763             # newline is
764             sub _fix_newlines {
765 4     4   6 my $str = shift;
766 4         41 $str =~ s/$eol/\n/gs;
767 4         10 return $str;
768             }
769              
770             =head1 FAQ
771              
772             =over 4
773              
774             =item Why not use YAML/JSON/XML?
775              
776             There are times when you have existing INI files you need to maintain backward
777             compatibility with, but you need the ability to add richer syntax. If that's
778             the problem you're trying to solve, this module's for you. If you're not, then
779             you'll likely be better served by L.
780              
781             =back
782              
783             =head1 CAVEATS
784              
785             =over 4
786              
787             =item * Right now writing will preserve all data, but comments and formatting
788             will be lost
789              
790             =item * Since double quotes are used to contain multi-line strings, they are
791             not allowed in values. This behaviour is different than most other
792             INI parsers.
793              
794             =item * Obviously any of the formatting which allows for nested arrays and
795             hashes will not be compatible with existing INI parsers
796              
797             =item * Keys and section names cannot start with an underscore
798              
799             =back
800              
801             =head1 SEE ALSO
802              
803             =over 4
804              
805             =item * L - The most popular module for reading and writing INI
806             files
807              
808             =item * L - A non-INI way of reading and writing nested structures into
809             config files
810              
811             =back
812              
813             =head1 AUTHOR
814              
815             Anthony Kilna, C<< >> - L
816              
817             =head1 BUGS
818              
819             Please report any bugs or feature requests to C
820             rt.cpan.org>, or through the web interface at
821             L. I will be
822             notified, and then you'll automatically be notified of progress on your bug
823             as I make changes.
824              
825             =head1 SUPPORT
826              
827             You can find documentation for this module with the perldoc command.
828              
829             perldoc Config::INIPlus
830              
831             You can also look for information at:
832              
833             =over 4
834              
835             =item * RT: CPAN's request tracker
836              
837             L
838              
839             =item * AnnoCPAN: Annotated CPAN documentation
840              
841             L
842              
843             =item * CPAN Ratings
844              
845             L
846              
847             =item * Search CPAN
848              
849             L
850              
851             =back
852              
853             =head1 COPYRIGHT & LICENSE
854              
855             Copyright 2012 Kilna Companies.
856              
857             This program is free software; you can redistribute it and/or modify it
858             under the terms of either: the GNU General Public License as published
859             by the Free Software Foundation; or the Artistic License.
860              
861             See http://dev.perl.org/licenses/ for more information.
862              
863             =cut
864              
865             1; # End of Config::INIPlus