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 8use English qw(-no_match_vars);
9
e0c0c283 10our $VERSION = '0.08';
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
ef87b7dc 54 Config::Any->load_files({files => \@files});
59a80452 55 Config::Any->load_files({files => \@files, filter => \&filter});
56 Config::Any->load_files({files => \@files, use_ext => 1});
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 ) = @_;
59a80452 89 return unless defined $args;
92a04e78 90 unless ( exists $args->{ files } ) {
59a80452 91 warn "no files specified";
92 return;
93 }
94
92a04e78 95 my %load_args
96 = map { $_ => defined $args->{ $_ } ? $args->{ $_ } : undef }
e0c0c283 97 qw(filter use_ext force_plugins driver_args);
92a04e78 98 $load_args{ files } = [ grep { -f $_ } @{ $args->{ files } } ];
99 return $class->_load( \%load_args );
c80a0905 100}
101
59a80452 102=head2 load_stems( )
103
104 Config::Any->load_stems({stems => \@stems]});
105 Config::Any->load_stems({stems => \@stems, filter => \&filter});
106 Config::Any->load_stems({stems => \@stems, use_ext => 1});
107
108C<load_stems()> attempts to load configuration from a list of files which it generates
109by combining the filename stems list passed in the C<stems> parameter with the
110potential filename extensions from each loader, which you can check with the
111C<extensions()> classmethod described below. Once this list of possible filenames is
112built it is treated exactly as in C<load_files()> above, as which it takes the same
113parameters. Please read the C<load_files()> documentation before using this method.
114
115=cut
116
c80a0905 117sub load_stems {
92a04e78 118 my ( $class, $args ) = @_;
59a80452 119 return unless defined $args;
92a04e78 120 unless ( exists $args->{ stems } ) {
59a80452 121 warn "no stems specified";
122 return;
123 }
92a04e78 124
125 my %load_args
126 = map { $_ => defined $args->{ $_ } ? $args->{ $_ } : undef }
ef87b7dc 127 qw(filter use_ext force_plugins driver_args);
41f47406 128
92a04e78 129 my $filenames = $class->_stems_to_files( $args->{ stems } );
130 $load_args{ files } = [ grep { -f $_ } @{ $filenames } ];
131 return $class->_load( \%load_args );
41f47406 132}
133
134sub _stems_to_files {
92a04e78 135 my ( $class, $stems ) = @_;
41f47406 136 return unless defined $stems;
137
c80a0905 138 my @files;
92a04e78 139STEM:
140 for my $s ( @$stems ) {
141 EXT:
142 for my $ext ( $class->extensions ) {
c80a0905 143 my $file = "$s.$ext";
144 next EXT unless -f $file;
145 push @files, $file;
146 last EXT;
147 }
148 }
41f47406 149 \@files;
c80a0905 150}
151
92a04e78 152sub _maphash (@) {
153 map { $_ => 1 } @_;
154} # sugar
41f47406 155
59a80452 156# this is where we do the real work
157# it's a private class-method because users should use the interface described
158# in the POD.
c80a0905 159sub _load {
92a04e78 160 my ( $class, $args ) = @_;
161 my ( $files_ref, $filter_cb, $use_ext, $force_plugins_ref )
162 = @{ $args }{ qw(files filter use_ext force_plugins) };
c80a0905 163 croak "_load requires a arrayref of file paths" unless defined $files_ref;
164
92a04e78 165 my %files = _maphash @$files_ref;
166 my %force_plugins = _maphash @$force_plugins_ref;
167 my $enforcing = keys %force_plugins ? 1 : 0;
41f47406 168
92a04e78 169 my $final_configs = [];
170 my $originally_loaded = {};
c80a0905 171
41f47406 172 # perform a separate file loop for each loader
c80a0905 173 for my $loader ( $class->plugins ) {
92a04e78 174 next if $enforcing && not defined $force_plugins{ $loader };
f0e3c221 175 last unless keys %files;
41f47406 176 my %ext = _maphash $loader->extensions;
177
92a04e78 178 my ( $loader_class ) = $loader =~ /::([^:]+)$/;
179 my $driver_args = $args->{ driver_args }{ $loader_class } || {};
180
181 FILE:
182 for my $filename ( keys %files ) {
183
184 # use file extension to decide whether this loader should try this file
185 # use_ext => 1 hits this block
186 if ( defined $use_ext && !$enforcing ) {
f0e3c221 187 my $matched_ext = 0;
92a04e78 188 EXT:
189 for my $e ( keys %ext ) {
190 next EXT unless $filename =~ m{ \. $e \z }xms;
191 next FILE unless exists $ext{ $e };
f0e3c221 192 $matched_ext = 1;
59a80452 193 }
41f47406 194
f0e3c221 195 next FILE unless $matched_ext;
59a80452 196 }
197
198 my $config;
92a04e78 199 eval { $config = $loader->load( $filename, $driver_args ); };
41f47406 200
92a04e78 201 next if $EVAL_ERROR; # if it croaked or warned, we can't use it
c80a0905 202 next if !$config;
92a04e78 203 delete $files{ $filename };
41f47406 204
205 # post-process config with a filter callback, if we got one
c80a0905 206 $filter_cb->( $config ) if defined $filter_cb;
41f47406 207
c80a0905 208 push @$final_configs, { $filename => $config };
209 }
210 }
211 $final_configs;
212}
213
59a80452 214=head2 finder( )
215
216The C<finder()> classmethod returns the
217L<Module::Pluggable::Object|Module::Pluggable::Object>
218object which is used to load the plugins. See the documentation for that module for
219more information.
220
221=cut
222
c80a0905 223sub finder {
92a04e78 224 my $class = shift;
c80a0905 225 my $finder = Module::Pluggable::Object->new(
226 search_path => [ __PACKAGE__ ],
227 require => 1
228 );
229 $finder;
230}
231
59a80452 232=head2 plugins( )
233
234The C<plugins()> classmethod returns the names of configuration loading plugins as
235found by L<Module::Pluggable::Object|Module::Pluggable::Object>.
236
237=cut
238
c80a0905 239sub plugins {
240 my $class = shift;
241 return $class->finder->plugins;
242}
243
59a80452 244=head2 extensions( )
c80a0905 245
59a80452 246The C<extensions()> classmethod returns the possible file extensions which can be loaded
247by C<load_stems()> and C<load_files()>. This may be useful if you set the C<use_ext>
248parameter to those methods.
c80a0905 249
59a80452 250=cut
c80a0905 251
59a80452 252sub extensions {
253 my $class = shift;
254 my @ext = map { $_->extensions } $class->plugins;
92a04e78 255 return wantarray ? @ext : [ @ext ];
59a80452 256}
c80a0905 257
258=head1 DIAGNOSTICS
259
c80a0905 260=over
261
59a80452 262=item C<no files specified> or C<no stems specified>
c80a0905 263
59a80452 264The C<load_files()> and C<load_stems()> methods will issue this warning if
265called with an empty list of files/stems to load.
c80a0905 266
59a80452 267=item C<_load requires a arrayref of file paths>
c80a0905 268
59a80452 269This fatal error will be thrown by the internal C<_load> method. It should not occur
270but is specified here for completeness. If your code dies with this error, please
271email a failing test case to the authors below.
c80a0905 272
273=back
274
c80a0905 275=head1 CONFIGURATION AND ENVIRONMENT
276
c80a0905 277Config::Any requires no configuration files or environment variables.
278
c80a0905 279=head1 DEPENDENCIES
280
59a80452 281L<Module::Pluggable|Module::Pluggable>
c80a0905 282
59a80452 283And at least one of the following:
284L<Config::General|Config::General>
285L<Config::Tiny|Config::Tiny>
286L<JSON|JSON>
287L<YAML|YAML>
288L<JSON::Syck|JSON::Syck>
289L<YAML::Syck|YAML::Syck>
290L<XML::Simple|XML::Simple>
c80a0905 291
292=head1 INCOMPATIBILITIES
293
c80a0905 294None reported.
295
c80a0905 296=head1 BUGS AND LIMITATIONS
297
c80a0905 298No bugs have been reported.
299
300Please report any bugs or feature requests to
301C<bug-config-any@rt.cpan.org>, or through the web interface at
302L<http://rt.cpan.org>.
303
c80a0905 304=head1 AUTHOR
305
f0e3c221 306Joel Bernstein E<lt>rataxis@cpan.orgE<gt>
c80a0905 307
59a80452 308=head1 CONTRIBUTORS
309
310This module was based on the original
311L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader>
312module by Brian Cassidy C<< <bricas@cpan.org> >>.
313
314With ideas and support from Matt S Trout C<< <mst@shadowcatsystems.co.uk> >>.
c80a0905 315
41f47406 316Further enhancements suggested by Evan Kaufman C<< <evank@cpan.org> >>.
317
c80a0905 318=head1 LICENCE AND COPYRIGHT
319
320Copyright (c) 2006, Portugal Telecom C<< http://www.sapo.pt/ >>. All rights reserved.
41f47406 321Portions copyright 2007, Joel Bernstein C<< <rataxis@cpan.org> >>.
c80a0905 322
323This module is free software; you can redistribute it and/or
324modify it under the same terms as Perl itself. See L<perlartistic>.
325
c80a0905 326=head1 DISCLAIMER OF WARRANTY
327
328BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
329FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
330OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
331PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
332EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
333WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
334ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
335YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
336NECESSARY SERVICING, REPAIR, OR CORRECTION.
337
338IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
339WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
340REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
341LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
342OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
343THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
344RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
345FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
346SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
347SUCH DAMAGES.
59a80452 348
349=head1 SEE ALSO
350
351L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader>
352-- now a wrapper around this module.
353
354=cut
355
41f47406 356"Drink more beer";