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