Explain MYAPP_LOCAL_CONFIG_SUFFIX etc much more clearly, patch from mailing list
[catagits/Catalyst-Plugin-ConfigLoader.git] / lib / Catalyst / Plugin / ConfigLoader.pm
CommitLineData
f004a98a 1package Catalyst::Plugin::ConfigLoader;
2
3use strict;
4use warnings;
5
6use Config::Any;
123852c9 7use MRO::Compat;
f004a98a 8use Data::Visitor::Callback;
7f0397f8 9use Catalyst::Utils ();
f004a98a 10
f10899f5 11our $VERSION = '0.24';
f004a98a 12
13=head1 NAME
14
15Catalyst::Plugin::ConfigLoader - Load config files of various types
16
17=head1 SYNOPSIS
18
19 package MyApp;
20
21 # ConfigLoader should be first in your list so
22 # other plugins can get the config information
23 use Catalyst qw( ConfigLoader ... );
24
25 # by default myapp.* will be loaded
26 # you can specify a file if you'd like
3231a8d0 27 __PACKAGE__->config( 'Plugin::ConfigLoader' => { file => 'config.yaml' } );
f004a98a 28
7c53420c 29 In the file, assuming it's in YAML format:
30
31 foo: bar
32
33 Accessible through the context object, or the class itself
34
35 $c->config->{foo} # bar
36 MyApp->config->{foo} # bar
37
f004a98a 38=head1 DESCRIPTION
39
40This module will attempt to load find and load a configuration
41file of various types. Currently it supports YAML, JSON, XML,
3231a8d0 42INI and Perl formats. Special configuration for a particular driver format can
43be stored in C<MyApp-E<gt>config-E<gt>{ 'Plugin::ConfigLoader' }-E<gt>{ driver }>.
48b5d20d 44For example, to pass arguments to L<Config::General>, use the following:
45
46 __PACKAGE__->config( 'Plugin::ConfigLoader' => {
47 driver => {
48 'General' => { -LowerCaseNames => 1 }
49 }
50 } );
51
52See L<Config::Any>'s C<driver_args> parameter for more information.
f004a98a 53
54To support the distinction between development and production environments,
55this module will also attemp to load a local config (e.g. myapp_local.yaml)
6a5c6f19 56which will override any duplicate settings. See
57L<get_config_local_suffix|/get_config_local_suffix>
58for details on how this is configured.
f004a98a 59
60=head1 METHODS
61
62=head2 setup( )
63
64This method is automatically called by Catalyst's setup routine. It will
65attempt to use each plugin and, once a file has been successfully
66loaded, set the C<config()> section.
67
68=cut
69
70sub setup {
71 my $c = shift;
72 my @files = $c->find_files;
587d381b 73 my $cfg = Config::Any->load_files(
74 { files => \@files,
75 filter => \&_fix_syntax,
76 use_ext => 1,
77 driver_args => $c->config->{ 'Plugin::ConfigLoader' }->{ driver }
78 || {},
79 }
80 );
90c108e6 81 # map the array of hashrefs to a simple hash
82 my %configs = map { %$_ } @$cfg;
83
f004a98a 84 # split the responses into normal and local cfg
85 my $local_suffix = $c->get_config_local_suffix;
90c108e6 86 my ( @main, @locals );
87 for ( sort keys %configs ) {
88 if ( m{$local_suffix\.}ms ) {
89 push @locals, $_;
587d381b 90 }
91 else {
90c108e6 92 push @main, $_;
f004a98a 93 }
94 }
587d381b 95
f004a98a 96 # load all the normal cfgs, then the local cfgs last so they can override
97 # normal cfgs
90c108e6 98 $c->load_config( { $_ => $configs{ $_ } } ) for @main, @locals;
f004a98a 99
100 $c->finalize_config;
123852c9 101 $c->next::method( @_ );
f004a98a 102}
103
104=head2 load_config
105
106This method handles loading the configuration data into the Catalyst
107context object. It does not return a value.
108
109=cut
110
111sub load_config {
112 my $c = shift;
113 my $ref = shift;
587d381b 114
e538c6f7 115 my ( $file, $config ) = %$ref;
587d381b 116
f004a98a 117 $c->config( $config );
118 $c->log->debug( qq(Loaded Config "$file") )
119 if $c->debug;
120
121 return;
122}
123
124=head2 find_files
125
126This method determines the potential file paths to be used for config loading.
127It returns an array of paths (up to the filename less the extension) to pass to
128L<Config::Any|Config::Any> for loading.
129
130=cut
131
132sub find_files {
133 my $c = shift;
587d381b 134 my ( $path, $extension ) = $c->get_config_path;
f004a98a 135 my $suffix = $c->get_config_local_suffix;
136 my @extensions = @{ Config::Any->extensions };
587d381b 137
f004a98a 138 my @files;
587d381b 139 if ( $extension ) {
1da5b36d 140 die "Unable to handle files with the extension '${extension}'"
141 unless grep { $_ eq $extension } @extensions;
4f63af80 142 ( my $local = $path ) =~ s{\.$extension}{_$suffix.$extension};
143 push @files, $path, $local;
587d381b 144 }
145 else {
f004a98a 146 @files = map { ( "$path.$_", "${path}_${suffix}.$_" ) } @extensions;
147 }
f004a98a 148 @files;
149}
150
151=head2 get_config_path
152
153This method determines the path, filename prefix and file extension to be used
154for config loading. It returns the path (up to the filename less the
155extension) to check and the specific extension to use (if it was specified).
156
157The order of preference is specified as:
158
159=over 4
160
161=item * C<$ENV{ MYAPP_CONFIG }>
162
7f0397f8 163=item * C<$ENV{ CATALYST_CONFIG }>
164
eb05f0bf 165=item * C<$c-E<gt>config-E<gt>{ 'Plugin::ConfigLoader' }-E<gt>{ file }>
f004a98a 166
167=item * C<$c-E<gt>path_to( $application_prefix )>
168
169=back
170
171If either of the first two user-specified options are directories, the
172application prefix will be added on to the end of the path.
173
174=cut
175
176sub get_config_path {
587d381b 177 my $c = shift;
afce197f 178
afce197f 179
f004a98a 180 my $appname = ref $c || $c;
181 my $prefix = Catalyst::Utils::appprefix( $appname );
7f0397f8 182 my $path = Catalyst::Utils::env_value( $c, 'CONFIG' )
af391898 183 || $c->config->{ 'Plugin::ConfigLoader' }->{ file }
f004a98a 184 || $c->path_to( $prefix );
185
587d381b 186 my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
187
188 if ( -d $path ) {
189 $path =~ s{[\/\\]$}{};
f004a98a 190 $path .= "/$prefix";
191 }
4f63af80 192
587d381b 193 return ( $path, $extension );
f004a98a 194}
195
196=head2 get_config_local_suffix
197
198Determines the suffix of files used to override the main config. By default
6a5c6f19 199this value is C<local>, which will load C<myapp_local.conf>. The suffix can
200be specified in the following order of preference:
f004a98a 201
202=over 4
203
f004a98a 204=item * C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }>
205
7f0397f8 206=item * C<$ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }>
207
af391898 208=item * C<$c-E<gt>config-E<gt>{ 'Plugin::ConfigLoader' }-E<gt>{ config_local_suffix }>
f004a98a 209
210=back
211
6a5c6f19 212The first one of these values found replaces the default of C<local> in the
213name of the local config file to be loaded.
214
215For example, if C< $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> is set to C<testing>,
216ConfigLoader will try and load C<myapp_testing.conf> instead of
217C<myapp_local.conf>.
218
f004a98a 219=cut
220
221sub get_config_local_suffix {
587d381b 222 my $c = shift;
afce197f 223
f004a98a 224 my $appname = ref $c || $c;
587d381b 225 my $suffix = Catalyst::Utils::env_value( $c, 'CONFIG_LOCAL_SUFFIX' )
af391898 226 || $c->config->{ 'Plugin::ConfigLoader' }->{ config_local_suffix }
f004a98a 227 || 'local';
228
229 return $suffix;
230}
231
232sub _fix_syntax {
233 my $config = shift;
234 my @components = (
235 map +{
236 prefix => $_ eq 'Component' ? '' : $_ . '::',
237 values => delete $config->{ lc $_ } || delete $config->{ $_ }
238 },
587d381b 239 grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
25cd1420 240 qw( Component Model M View V Controller C Plugin )
f004a98a 241 );
242
243 foreach my $comp ( @components ) {
244 my $prefix = $comp->{ prefix };
245 foreach my $element ( keys %{ $comp->{ values } } ) {
246 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
247 }
248 }
249}
250
251=head2 finalize_config
252
253This method is called after the config file is loaded. It can be
254used to implement tuning of config values that can only be done
255at runtime. If you need to do this to properly configure any
256plugins, it's important to load ConfigLoader before them.
257ConfigLoader provides a default finalize_config method which
d392c48d 258walks through the loaded config hash and calls the C<config_substitutions>
259sub on any string.
f004a98a 260
261=cut
262
263sub finalize_config {
264 my $c = shift;
265 my $v = Data::Visitor::Callback->new(
266 plain_value => sub {
267 return unless defined $_;
d392c48d 268 $c->config_substitutions( $_ );
f004a98a 269 }
270 );
271 $v->visit( $c->config );
272}
273
d392c48d 274=head2 config_substitutions( $value )
275
276This method substitutes macros found with calls to a function. There are three
277default macros:
278
279=over 4
280
281=item * C<__HOME__> - replaced with C<$c-E<gt>path_to('')>
282
283=item * C<__path_to(foo/bar)__> - replaced with C<$c-E<gt>path_to('foo/bar')>
284
285=item * C<__literal(__FOO__)__> - leaves __FOO__ alone (allows you to use
286C<__DATA__> as a config value, for example)
287
288=back
289
290The parameter list is split on comma (C<,>). You can override this method to
291do your own string munging, or you can define your own macros in
af391898 292C<MyApp-E<gt>config-E<gt>{ 'Plugin::ConfigLoader' }-E<gt>{ substitutions }>.
293Example:
d392c48d 294
af391898 295 MyApp->config->{ 'Plugin::ConfigLoader' }->{ substitutions } = {
d392c48d 296 baz => sub { my $c = shift; qux( @_ ); }
297 }
298
299The above will respond to C<__baz(x,y)__> in config strings.
300
301=cut
302
303sub config_substitutions {
587d381b 304 my $c = shift;
305 my $subs = $c->config->{ 'Plugin::ConfigLoader' }->{ substitutions }
306 || {};
307 $subs->{ HOME } ||= sub { shift->path_to( '' ); };
d392c48d 308 $subs->{ path_to } ||= sub { shift->path_to( @_ ); };
309 $subs->{ literal } ||= sub { return $_[ 1 ]; };
310 my $subsre = join( '|', keys %$subs );
311
312 for ( @_ ) {
313 s{__($subsre)(?:\((.+?)\))?__}{ $subs->{ $1 }->( $c, $2 ? split( /,/, $2 ) : () ) }eg;
314 }
315}
316
f004a98a 317=head1 AUTHOR
318
01be879a 319Brian Cassidy E<lt>bricas@cpan.orgE<gt>
f004a98a 320
321=head1 CONTRIBUTORS
322
323The following people have generously donated their time to the
324development of this module:
325
326=over 4
327
328=item * Joel Bernstein E<lt>rataxis@cpan.orgE<gt> - Rewrite to use L<Config::Any>
329
330=item * David Kamholz E<lt>dkamholz@cpan.orgE<gt> - L<Data::Visitor> integration
331
332=back
333
334Work to this module has been generously sponsored by:
335
336=over 4
337
338=item * Portugal Telecom L<http://www.sapo.pt/> - Work done by Joel Bernstein
339
340=back
341
342=head1 COPYRIGHT AND LICENSE
343
c098c4a2 344Copyright 2006-2009 by Brian Cassidy
f004a98a 345
346This library is free software; you can redistribute it and/or modify
347it under the same terms as Perl itself.
348
349=head1 SEE ALSO
350
351=over 4
352
353=item * L<Catalyst>
354
affbca23 355=item * L<Catalyst::Plugin::ConfigLoader::Manual>
356
f004a98a 357=item * L<Config::Any>
358
359=back
360
361=cut
362
3631;