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