File Coverage

blib/lib/Config/Ant.pm
Criterion Covered Total %
statement 58 64 90.6
branch 12 22 54.5
condition 1 5 20.0
subroutine 12 12 100.0
pod 6 6 100.0
total 89 109 81.6


line stmt bran cond sub pod time code
1             package Config::Ant;
2             BEGIN {
3 2     2   45107 $Config::Ant::VERSION = '0.01';
4             }
5              
6             # ABSTRACT: Load Ant-style property files
7              
8 2     2   18 use strict;
  2         4  
  2         59  
9 2     2   10 use warnings;
  2         3  
  2         53  
10              
11 2     2   19 use Carp;
  2         11  
  2         317  
12 2     2   18 use Scalar::Util qw(openhandle);
  2         4  
  2         253  
13 2     2   1973 use File::Slurp qw(read_file);
  2         41129  
  2         1821  
14              
15             sub new {
16 2     2 1 27 my ($this, @initial) = @_;
17 2   33     16 my $class = ref($this) || $this;
18 2         7 my $self = {@initial};
19 2         5 bless $self, $class;
20 2         8 return $self;
21             }
22              
23             sub read {
24 4     4 1 3003 my ($self, $file) = @_;
25 4         18 my $contents = File::Slurp::read_file($file);
26 4         385 $self->read_string($contents);
27             }
28              
29             sub read_line {
30 14     14 1 38 my ($self, $section, $key, $value) = @_;
31            
32 14 100       40 return if (exists($self->{$section}->{$key}));
33              
34 12         55 $value =~ s/\$\{([^}]+)\}/
35 10 100       34 if (! exists($self->{$section}->{$1})) {
36 2         9 '${'.$1.'}';
37             } else {
38 8         33 $self->{$section}->{$1};
39             } /eg;
40              
41 12         42 $self->{$section}->{$key} = $value;
42             }
43              
44             # This has been cobbled from Config::Tiny. Most of the rest has been
45             # written directly using additional dependencies.
46              
47             sub read_string {
48 4     4 1 8 my ($self, $contents) = @_;
49              
50             # Parse the file
51 4         6 my $ns = '_';
52 4         6 my $counter = 0;
53 4         184 foreach ( split /(?:\015{1,2}\012|\015|\012)/, $contents ) {
54 34         35 $counter++;
55              
56             # Skip comments and empty lines
57 34 100       100 next if /^\s*(?:\#|\;|$)/;
58              
59             # Remove inline comments
60 14         21 s/\s\;\s.+$//g;
61              
62             # Handle section headers
63 14 50       34 if ( /^\s*\[\s*(.+?)\s*\]\s*$/ ) {
64             # Create the sub-hash if it doesn't exist.
65             # Without this sections without keys will not
66             # appear at all in the completed struct.
67 0   0     0 $self->{$ns = $1} ||= {};
68 0         0 next;
69             }
70              
71             # Handle properties
72 14 50       107 if ( /^\s*([^=]+?)\s*=\s*(.*?)\s*$/ ) {
73 14         33 $self->read_line($ns, $1, $2);
74 14         26 next;
75             }
76              
77 0         0 return $self->_error( "Syntax error at line $counter: '$_'" );
78             }
79              
80 4         17 $self;
81             }
82              
83             sub write {
84 1     1 1 885 my ($self, $file) = @_;
85              
86 1         2 my $opened = 0;
87 1 50       8 if (! openhandle($file)) {
88 0 0       0 open($file, '>', $file) or croak("Failed to open file '$file' for writing: $!");
89 0         0 $opened = 1;
90             }
91            
92 1         14 print $file $self->write_string();
93 1 50       5 close($file) if ($opened);
94 1         23 return;
95             }
96              
97             # Again, this bit was cobbled from Config::Tiny.
98              
99             sub write_string {
100 1     1 1 2 my ($self) = @_;
101              
102 1         2 my $contents = '';
103 1 0       5 foreach my $section ( sort { (($b eq '_') <=> ($a eq '_')) || ($a cmp $b) } keys %$self ) {
  0         0  
104 1         3 my $block = $self->{$section};
105 1 50       11 $contents .= "\n" if length $contents;
106 1 50       4 $contents .= "[$section]\n" unless $section eq '_';
107 1         7 foreach my $property ( sort keys %$block ) {
108 6         16 $contents .= "$property=$block->{$property}\n";
109             }
110             }
111            
112 1         14 $contents;
113             }
114              
115             1;
116              
117             =head1 NAME
118              
119             Config::Ant - load Ant-style property files
120              
121             =head1 SYNOPSIS
122              
123             # In your configuration file
124             root.directory = /usr/local
125              
126             lib = ${root.directory}/lib
127             bin = ${root.directory}/lib
128             perl = ${bin}/perl
129            
130             # In your program
131             use Config::Ant;
132            
133             # Create a config
134             my $config = Config::Ant->new();
135              
136             # Read the config
137             $config->read('file1.conf');
138            
139             # You can also read a second file, with properties substituted from the first
140             $config->read('file2.conf');
141            
142             my $rootdir = $config->{_}->{'root.directory'};
143             my $perl = $config->{_}->{perl};
144            
145             # Writing ignores substitutions
146             $config->write('files.conf');
147            
148             =head1 DESCRIPTION
149              
150             Apache Ant uses property files with substitutions in them, which are very helpful for maintaining
151             a complex set of related properties. This component is a subclass of L which includes
152             the Ant-style substitution systems.
153              
154             Ant properties are set by their first definition and are then immutable, so a second definition
155             will not affect anything, ever. This is handy, as you can override settings by putting local values
156             first, and the loading files of defaults.
157              
158             Note that the usage interface is I identical to L. This is because L
159             assumes that each file is self-contained, and constructs a new object for it. This does not make
160             sense for Ant-style files, which are often loaded from several files, allowing for local customization.
161              
162             Also not that the file format is I identical to Ant, in that like L,
163             Config::Ant allows "windows style" sections to be used. This can be handy, but it's an optional extra
164             that will only annoy you if you use property names containing [ or ], which would be a very
165             bad move.
166              
167             =head1 METHODS
168              
169             =over 4
170              
171             =item Config::Ant->new()
172              
173             Returns a new property file processing instance, which can then be used as a container for
174             properties read and written through the other methods.
175              
176             =item read($file)
177              
178             Reads a file (or file handle) into the property system. This reads the text and passes the string to
179             C. This method can be called many times for a single instance, and this is common
180             when you want to handle several property files. The first property sets always wins, and there is
181             no method defined to allow properties to be removed.
182              
183             =item read_string($text)
184              
185             Reads and processes the properties a line at a time. Comment lines and blanks are skipped, sections
186             are set, and property lines passed to C
187              
188             =item read_line($section, $property, $value)
189              
190             This sets the property, and can be overridden if required. The property will only be set if a value
191             doesn't exist. The default method also handles the substitution of existing values into the value.
192              
193             =item write($file)
194              
195             Opens the file for writing, if necessary (i.e., not a file handle) and then writes out all the
196             current properties, using C to obtain the stringified property file text.
197              
198             =item write_string()
199              
200             Returns the stringified text for all the properties currently registered.
201              
202             =back
203              
204             =head1 AUTHOR
205              
206             Stuart Watt Estuart@morungos.comE
207              
208             =head1 COPYRIGHT
209              
210             Copyright 2010 by the authors.
211              
212             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
213              
214             =head1 SEE ALSO
215              
216             Partly based on L.
217              
218             =cut