line endings, tabs, perltidy.
[p5sagit/Config-Any.git] / lib / Config / Any.pm
CommitLineData
c80a0905 1package Config::Any;
92a04e78 2
c80a0905 3use strict;
92a04e78 4use warnings;
5
c80a0905 6use Carp;
7use Module::Pluggable::Object ();
59a80452 8
5770ffc0 9our $VERSION = '0.13';
c80a0905 10
59a80452 11=head1 NAME
12
13Config::Any - Load configuration from different file formats, transparently
14
59a80452 15=head1 SYNOPSIS
16
17 use Config::Any;
18
f0e3c221 19 my $cfg = Config::Any->load_stems({stems => \@filepath_stems, ... });
20 # or
21 my $cfg = Config::Any->load_files({files => \@filepaths, ... });
59a80452 22
f0e3c221 23 for (@$cfg) {
0c6989c6 24 my ($filename, $config) = %$_;
f0e3c221 25 $class->config($config);
26 warn "loaded config from file: $filename";
27 }
59a80452 28
29=head1 DESCRIPTION
30
31L<Config::Any|Config::Any> provides a facility for Perl applications and libraries
32to load configuration data from multiple different file formats. It supports XML, YAML,
33JSON, Apache-style configuration, Windows INI files, and even Perl code.
34
35The rationale for this module is as follows: Perl programs are deployed on many different
36platforms and integrated with many different systems. Systems administrators and end
37users may prefer different configuration formats than the developers. The flexibility
38inherent in a multiple format configuration loader allows different users to make
39different choices, without generating extra work for the developers. As a developer
40you only need to learn a single interface to be able to use the power of different
41configuration formats.
42
43=head1 INTERFACE
44
45=cut
46
7c218182 47=head2 load_files( \%args )
59a80452 48
4efab558 49 Config::Any->load_files( { files => \@files } );
50 Config::Any->load_files( { files => \@files, filter => \&filter } );
51 Config::Any->load_files( { files => \@files, use_ext => 1 } );
aa7bd7c3 52 Config::Any->load_files( { files => \@files, flatten_to_hash => 1 } );
59a80452 53
54C<load_files()> attempts to load configuration from the list of files passed in
55the C<files> parameter, if the file exists.
56
57If the C<filter> parameter is set, it is used as a callback to modify the configuration
58data before it is returned. It will be passed a single hash-reference parameter which
59it should modify in-place.
60
61If the C<use_ext> parameter is defined, the loader will attempt to parse the file
62extension from each filename and will skip the file unless it matches a standard
63extension for the loading plugins. Only plugins whose standard extensions match the
64file extension will be used. For efficiency reasons, its use is encouraged, but
65be aware that you will lose flexibility -- for example, a file called C<myapp.cfg>
66containing YAML data will not be offered to the YAML plugin, whereas C<myapp.yml>
67or C<myapp.yaml> would be.
68
aa7bd7c3 69When the C<flatten_to_hash> parameter is defined, the loader will return a hash
70keyed on the file names, as opposed to the usual list of single-key hashes.
71
41f47406 72C<load_files()> also supports a 'force_plugins' parameter, whose value should be an
73arrayref of plugin names like C<Config::Any::INI>. Its intended use is to allow the use
74of a non-standard file extension while forcing it to be offered to a particular parser.
75It is not compatible with 'use_ext'.
76
ef87b7dc 77You can supply a C<driver_args> hashref to pass special options to a particular
78parser object. Example:
79
80 Config::Any->load_files( { files => \@files, driver_args => {
81 General => { -LowerCaseNames => 1 }
82 } )
83
59a80452 84=cut
85
c80a0905 86sub load_files {
92a04e78 87 my ( $class, $args ) = @_;
4efab558 88
89 unless ( $args && exists $args->{ files } ) {
90 warn "No files specified!";
59a80452 91 return;
92 }
93
4efab558 94 return $class->_load( $args );
c80a0905 95}
96
7c218182 97=head2 load_stems( \%args )
59a80452 98
bef9e9a5 99 Config::Any->load_stems( { stems => \@stems } );
100 Config::Any->load_stems( { stems => \@stems, filter => \&filter } );
101 Config::Any->load_stems( { stems => \@stems, use_ext => 1 } );
aa7bd7c3 102 Config::Any->load_stems( { stems => \@stems, flatten_to_hash => 1 } );
59a80452 103
104C<load_stems()> attempts to load configuration from a list of files which it generates
105by combining the filename stems list passed in the C<stems> parameter with the
106potential filename extensions from each loader, which you can check with the
107C<extensions()> classmethod described below. Once this list of possible filenames is
108built it is treated exactly as in C<load_files()> above, as which it takes the same
109parameters. Please read the C<load_files()> documentation before using this method.
110
111=cut
112
c80a0905 113sub load_stems {
92a04e78 114 my ( $class, $args ) = @_;
bef9e9a5 115
116 unless ( $args && exists $args->{ stems } ) {
117 warn "No stems specified!";
59a80452 118 return;
119 }
92a04e78 120
bef9e9a5 121 my $stems = delete $args->{ stems };
c80a0905 122 my @files;
92a04e78 123 for my $s ( @$stems ) {
92a04e78 124 for my $ext ( $class->extensions ) {
4efab558 125 push @files, "$s.$ext";
c80a0905 126 }
127 }
c80a0905 128
bef9e9a5 129 $args->{ files } = \@files;
130 return $class->_load( $args );
131}
41f47406 132
c80a0905 133sub _load {
92a04e78 134 my ( $class, $args ) = @_;
a918b0b8 135 croak "_load requires a arrayref of file paths" unless $args->{ files };
136
c84f1613 137 my $force = defined $args->{ force_plugins };
138 if ( !$force and !defined $args->{ use_ext } ) {
72628dc7 139 warn
140 "use_ext argument was not explicitly set, as of 0.09, this is true by default";
a918b0b8 141 $args->{ use_ext } = 1;
142 }
143
144 # figure out what plugins we're using
ade8c46e 145 my @plugins = $force ? @{ $args->{ force_plugins } } : $class->plugins;
a918b0b8 146
147 # map extensions if we have to
72628dc7 148 my ( %extension_lut, $extension_re );
a918b0b8 149 my $use_ext_lut = !$force && $args->{ use_ext };
72628dc7 150 if ( $use_ext_lut ) {
a918b0b8 151 for my $plugin ( @plugins ) {
152 $extension_lut{ $_ } = $plugin for $plugin->extensions;
153 }
154
155 $extension_re = join( '|', keys %extension_lut );
156 }
59a80452 157
a918b0b8 158 # map args to plugins
159 my $base_class = __PACKAGE__;
160 my %loader_args;
161 for my $plugin ( @plugins ) {
162 $plugin =~ m{^$base_class\::(.+)};
163 $loader_args{ $plugin } = $args->{ driver_args }->{ $1 } || {};
164 }
165
166 my @results;
167
168 for my $filename ( @{ $args->{ files } } ) {
72628dc7 169
a918b0b8 170 # don't even bother if it's not there
171 next unless -f $filename;
172
173 my @try_plugins = @plugins;
41f47406 174
72628dc7 175 if ( $use_ext_lut ) {
a918b0b8 176 $filename =~ m{\.($extension_re)\z};
177 next unless $1;
178 @try_plugins = $extension_lut{ $1 };
179 }
180
181 for my $loader ( @try_plugins ) {
ade8c46e 182 next unless $loader->is_supported;
72628dc7 183 my @configs
184 = eval { $loader->load( $filename, $loader_args{ $loader } ); };
41f47406 185
a918b0b8 186 # fatal error if we used extension matching
92c29326 187 croak "Error parsing $filename: $@" if $@ and $use_ext_lut;
a918b0b8 188 next if $@ or !@configs;
189
190 # post-process config with a filter callback
191 if ( $args->{ filter } ) {
192 $args->{ filter }->( $_ ) for @configs;
193 }
41f47406 194
72628dc7 195 push @results,
196 { $filename => @configs == 1 ? $configs[ 0 ] : \@configs };
a918b0b8 197 last;
c80a0905 198 }
199 }
7c218182 200
aa7bd7c3 201 if ( defined $args->{ flatten_to_hash } ) {
202 my %flattened = map { %$_ } @results;
203 return \%flattened;
204 }
205
a918b0b8 206 return \@results;
c80a0905 207}
208
59a80452 209=head2 finder( )
210
211The C<finder()> classmethod returns the
212L<Module::Pluggable::Object|Module::Pluggable::Object>
213object which is used to load the plugins. See the documentation for that module for
214more information.
215
216=cut
217
c80a0905 218sub finder {
92a04e78 219 my $class = shift;
c80a0905 220 my $finder = Module::Pluggable::Object->new(
221 search_path => [ __PACKAGE__ ],
222 require => 1
223 );
bef9e9a5 224 return $finder;
c80a0905 225}
226
59a80452 227=head2 plugins( )
228
229The C<plugins()> classmethod returns the names of configuration loading plugins as
230found by L<Module::Pluggable::Object|Module::Pluggable::Object>.
231
232=cut
233
c80a0905 234sub plugins {
235 my $class = shift;
236 return $class->finder->plugins;
237}
238
59a80452 239=head2 extensions( )
c80a0905 240
59a80452 241The C<extensions()> classmethod returns the possible file extensions which can be loaded
242by C<load_stems()> and C<load_files()>. This may be useful if you set the C<use_ext>
243parameter to those methods.
c80a0905 244
59a80452 245=cut
c80a0905 246
59a80452 247sub extensions {
248 my $class = shift;
249 my @ext = map { $_->extensions } $class->plugins;
4efab558 250 return wantarray ? @ext : \@ext;
59a80452 251}
c80a0905 252
253=head1 DIAGNOSTICS
254
c80a0905 255=over
256
7c218182 257=item C<No files specified!> or C<No stems specified!>
c80a0905 258
59a80452 259The C<load_files()> and C<load_stems()> methods will issue this warning if
260called with an empty list of files/stems to load.
c80a0905 261
59a80452 262=item C<_load requires a arrayref of file paths>
c80a0905 263
59a80452 264This fatal error will be thrown by the internal C<_load> method. It should not occur
265but is specified here for completeness. If your code dies with this error, please
266email a failing test case to the authors below.
c80a0905 267
268=back
269
c80a0905 270=head1 CONFIGURATION AND ENVIRONMENT
271
c80a0905 272Config::Any requires no configuration files or environment variables.
273
c80a0905 274=head1 DEPENDENCIES
275
59a80452 276L<Module::Pluggable|Module::Pluggable>
c80a0905 277
59a80452 278And at least one of the following:
279L<Config::General|Config::General>
280L<Config::Tiny|Config::Tiny>
281L<JSON|JSON>
282L<YAML|YAML>
283L<JSON::Syck|JSON::Syck>
284L<YAML::Syck|YAML::Syck>
285L<XML::Simple|XML::Simple>
c80a0905 286
287=head1 INCOMPATIBILITIES
288
c80a0905 289None reported.
290
c80a0905 291=head1 BUGS AND LIMITATIONS
292
c80a0905 293No bugs have been reported.
294
295Please report any bugs or feature requests to
296C<bug-config-any@rt.cpan.org>, or through the web interface at
297L<http://rt.cpan.org>.
298
c80a0905 299=head1 AUTHOR
300
f0e3c221 301Joel Bernstein E<lt>rataxis@cpan.orgE<gt>
c80a0905 302
59a80452 303=head1 CONTRIBUTORS
304
305This module was based on the original
306L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader>
307module by Brian Cassidy C<< <bricas@cpan.org> >>.
308
309With ideas and support from Matt S Trout C<< <mst@shadowcatsystems.co.uk> >>.
c80a0905 310
41f47406 311Further enhancements suggested by Evan Kaufman C<< <evank@cpan.org> >>.
312
c80a0905 313=head1 LICENCE AND COPYRIGHT
314
315Copyright (c) 2006, Portugal Telecom C<< http://www.sapo.pt/ >>. All rights reserved.
41f47406 316Portions copyright 2007, Joel Bernstein C<< <rataxis@cpan.org> >>.
c80a0905 317
318This module is free software; you can redistribute it and/or
319modify it under the same terms as Perl itself. See L<perlartistic>.
320
c80a0905 321=head1 DISCLAIMER OF WARRANTY
322
323BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
324FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
325OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
326PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
327EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
328WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
329ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
330YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
331NECESSARY SERVICING, REPAIR, OR CORRECTION.
332
333IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
334WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
335REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
336LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
337OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
338THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
339RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
340FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
341SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
342SUCH DAMAGES.
59a80452 343
344=head1 SEE ALSO
345
346L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader>
347-- now a wrapper around this module.
348
349=cut
350
41f47406 351"Drink more beer";