6 use Module::Pluggable::Object ();
7 use English qw(-no_match_vars);
13 Config::Any - Load configuration from different file formats, transparently
17 This document describes Config::Any version 0.0.8
23 my $cfg = Config::Any->load_stems({stems => \@filepath_stems, ... });
25 my $cfg = Config::Any->load_files({files => \@filepaths, ... });
28 my ($filename, $config) = each %$_;
29 $class->config($config);
30 warn "loaded config from file: $filename";
35 L<Config::Any|Config::Any> provides a facility for Perl applications and libraries
36 to load configuration data from multiple different file formats. It supports XML, YAML,
37 JSON, Apache-style configuration, Windows INI files, and even Perl code.
39 The rationale for this module is as follows: Perl programs are deployed on many different
40 platforms and integrated with many different systems. Systems administrators and end
41 users may prefer different configuration formats than the developers. The flexibility
42 inherent in a multiple format configuration loader allows different users to make
43 different choices, without generating extra work for the developers. As a developer
44 you only need to learn a single interface to be able to use the power of different
45 configuration formats.
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});
57 C<load_files()> attempts to load configuration from the list of files passed in
58 the C<files> parameter, if the file exists.
60 If the C<filter> parameter is set, it is used as a callback to modify the configuration
61 data before it is returned. It will be passed a single hash-reference parameter which
62 it should modify in-place.
64 If the C<use_ext> parameter is defined, the loader will attempt to parse the file
65 extension from each filename and will skip the file unless it matches a standard
66 extension for the loading plugins. Only plugins whose standard extensions match the
67 file extension will be used. For efficiency reasons, its use is encouraged, but
68 be aware that you will lose flexibility -- for example, a file called C<myapp.cfg>
69 containing YAML data will not be offered to the YAML plugin, whereas C<myapp.yml>
70 or C<myapp.yaml> would be.
72 C<load_files()> also supports a 'force_plugins' parameter, whose value should be an
73 arrayref of plugin names like C<Config::Any::INI>. Its intended use is to allow the use
74 of a non-standard file extension while forcing it to be offered to a particular parser.
75 It is not compatible with 'use_ext'.
77 You can supply a C<driver_args> hashref to pass special options to a particular
78 parser object. Example:
80 Config::Any->load_files( { files => \@files, driver_args => {
81 General => { -LowerCaseNames => 1 }
87 my ($class, $args) = @_;
88 return unless defined $args;
89 unless (exists $args->{files}) {
90 warn "no files specified";
94 my %load_args = map { $_ => defined $args->{$_} ? $args->{$_} : undef }
95 qw(filter use_ext force_plugins driver_args);
96 $load_args{files} = [ grep { -f $_ } @{$args->{files}} ];
97 return $class->_load(\%load_args);
102 Config::Any->load_stems({stems => \@stems]});
103 Config::Any->load_stems({stems => \@stems, filter => \&filter});
104 Config::Any->load_stems({stems => \@stems, use_ext => 1});
106 C<load_stems()> attempts to load configuration from a list of files which it generates
107 by combining the filename stems list passed in the C<stems> parameter with the
108 potential filename extensions from each loader, which you can check with the
109 C<extensions()> classmethod described below. Once this list of possible filenames is
110 built it is treated exactly as in C<load_files()> above, as which it takes the same
111 parameters. Please read the C<load_files()> documentation before using this method.
116 my ($class, $args) = @_;
117 return unless defined $args;
118 unless (exists $args->{stems}) {
119 warn "no stems specified";
123 my %load_args = map { $_ => defined $args->{$_} ? $args->{$_} : undef }
124 qw(filter use_ext force_plugins driver_args);
126 my $filenames = $class->_stems_to_files($args->{stems});
127 $load_args{files} = [ grep { -f $_ } @{$filenames} ];
128 return $class->_load(\%load_args);
131 sub _stems_to_files {
132 my ($class, $stems) = @_;
133 return unless defined $stems;
137 for my $s (@$stems) {
139 for my $ext ($class->extensions) {
140 my $file = "$s.$ext";
141 next EXT unless -f $file;
149 sub _maphash (@) { map { $_ => 1 } @_ } # sugar
151 # this is where we do the real work
152 # it's a private class-method because users should use the interface described
155 my ($class, $args) = @_;
156 my ($files_ref, $filter_cb, $use_ext, $force_plugins_ref) =
157 @{$args}{qw(files filter use_ext force_plugins)};
158 croak "_load requires a arrayref of file paths" unless defined $files_ref;
160 my %files = _maphash @$files_ref;
161 my %force_plugins = _maphash @$force_plugins_ref;
162 my $enforcing = keys %force_plugins ? 1 : 0;
164 my $final_configs = [];
165 my $originally_loaded = {};
167 # perform a separate file loop for each loader
168 for my $loader ( $class->plugins ) {
169 next if $enforcing && not defined $force_plugins{$loader};
170 last unless keys %files;
171 my %ext = _maphash $loader->extensions;
173 my ($loader_class) = $loader =~ /::([^:]+)$/;
174 my $driver_args = $args->{driver_args}{$loader_class} || {};
177 for my $filename (keys %files) {
178 # use file extension to decide whether this loader should try this file
179 # use_ext => 1 hits this block
180 if (defined $use_ext && !$enforcing) {
183 for my $e (keys %ext) {
184 next EXT unless $filename =~ m{ \. $e \z }xms;
185 next FILE unless exists $ext{$e};
189 next FILE unless $matched_ext;
194 $config = $loader->load( $filename, $driver_args );
197 next if $EVAL_ERROR; # if it croaked or warned, we can't use it
199 delete $files{$filename};
201 # post-process config with a filter callback, if we got one
202 $filter_cb->( $config ) if defined $filter_cb;
204 push @$final_configs, { $filename => $config };
212 The C<finder()> classmethod returns the
213 L<Module::Pluggable::Object|Module::Pluggable::Object>
214 object which is used to load the plugins. See the documentation for that module for
221 my $finder = Module::Pluggable::Object->new(
222 search_path => [ __PACKAGE__ ],
230 The C<plugins()> classmethod returns the names of configuration loading plugins as
231 found by L<Module::Pluggable::Object|Module::Pluggable::Object>.
237 return $class->finder->plugins;
242 The C<extensions()> classmethod returns the possible file extensions which can be loaded
243 by C<load_stems()> and C<load_files()>. This may be useful if you set the C<use_ext>
244 parameter to those methods.
250 my @ext = map { $_->extensions } $class->plugins;
251 return wantarray ? @ext : [@ext];
258 =item C<no files specified> or C<no stems specified>
260 The C<load_files()> and C<load_stems()> methods will issue this warning if
261 called with an empty list of files/stems to load.
263 =item C<_load requires a arrayref of file paths>
265 This fatal error will be thrown by the internal C<_load> method. It should not occur
266 but is specified here for completeness. If your code dies with this error, please
267 email a failing test case to the authors below.
271 =head1 CONFIGURATION AND ENVIRONMENT
273 Config::Any requires no configuration files or environment variables.
277 L<Module::Pluggable|Module::Pluggable>
279 And at least one of the following:
280 L<Config::General|Config::General>
281 L<Config::Tiny|Config::Tiny>
284 L<JSON::Syck|JSON::Syck>
285 L<YAML::Syck|YAML::Syck>
286 L<XML::Simple|XML::Simple>
288 =head1 INCOMPATIBILITIES
292 =head1 BUGS AND LIMITATIONS
294 No bugs have been reported.
296 Please report any bugs or feature requests to
297 C<bug-config-any@rt.cpan.org>, or through the web interface at
298 L<http://rt.cpan.org>.
302 Joel Bernstein E<lt>rataxis@cpan.orgE<gt>
306 This module was based on the original
307 L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader>
308 module by Brian Cassidy C<< <bricas@cpan.org> >>.
310 With ideas and support from Matt S Trout C<< <mst@shadowcatsystems.co.uk> >>.
312 Further enhancements suggested by Evan Kaufman C<< <evank@cpan.org> >>.
314 =head1 LICENCE AND COPYRIGHT
316 Copyright (c) 2006, Portugal Telecom C<< http://www.sapo.pt/ >>. All rights reserved.
317 Portions copyright 2007, Joel Bernstein C<< <rataxis@cpan.org> >>.
319 This module is free software; you can redistribute it and/or
320 modify it under the same terms as Perl itself. See L<perlartistic>.
322 =head1 DISCLAIMER OF WARRANTY
324 BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
325 FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
326 OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
327 PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
328 EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
329 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
330 ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
331 YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
332 NECESSARY SERVICING, REPAIR, OR CORRECTION.
334 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
335 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
336 REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
337 LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
338 OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
339 THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
340 RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
341 FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
342 SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
347 L<Catalyst::Plugin::ConfigLoader|Catalyst::Plugin::ConfigLoader>
348 -- now a wrapper around this module.