removing old dir
[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;
7use NEXT;
8use Data::Visitor::Callback;
9
10our $VERSION = '0.13';
11
12=head1 NAME
13
14Catalyst::Plugin::ConfigLoader - Load config files of various types
15
16=head1 SYNOPSIS
17
18 package MyApp;
19
20 # ConfigLoader should be first in your list so
21 # other plugins can get the config information
22 use Catalyst qw( ConfigLoader ... );
23
24 # by default myapp.* will be loaded
25 # you can specify a file if you'd like
26 __PACKAGE__->config( file => 'config.yaml' );
27
28=head1 DESCRIPTION
29
30This module will attempt to load find and load a configuration
31file of various types. Currently it supports YAML, JSON, XML,
32INI and Perl formats.
33
34To support the distinction between development and production environments,
35this module will also attemp to load a local config (e.g. myapp_local.yaml)
36which will override any duplicate settings.
37
38=head1 METHODS
39
40=head2 setup( )
41
42This method is automatically called by Catalyst's setup routine. It will
43attempt to use each plugin and, once a file has been successfully
44loaded, set the C<config()> section.
45
46=cut
47
48sub setup {
49 my $c = shift;
50 my @files = $c->find_files;
51 my $cfg = Config::Any->load_files( {
52 files => \@files,
53 filter => \&_fix_syntax,
54 use_ext => 1
55 } );
56
57 # split the responses into normal and local cfg
58 my $local_suffix = $c->get_config_local_suffix;
59 my( @cfg, @localcfg );
60 for( @$cfg ) {
61 if( ( keys %$_ )[ 0 ] =~ m{ $local_suffix \. }xms ) {
62 push @localcfg, $_;
63 } else {
64 push @cfg, $_;
65 }
66 }
67
68 # load all the normal cfgs, then the local cfgs last so they can override
69 # normal cfgs
70 $c->load_config( $_ ) for @cfg, @localcfg;
71
72 $c->finalize_config;
73 $c->NEXT::setup( @_ );
74}
75
76=head2 load_config
77
78This method handles loading the configuration data into the Catalyst
79context object. It does not return a value.
80
81=cut
82
83sub load_config {
84 my $c = shift;
85 my $ref = shift;
86
87 my( $file, $config ) = each %$ref;
88
89 $c->config( $config );
90 $c->log->debug( qq(Loaded Config "$file") )
91 if $c->debug;
92
93 return;
94}
95
96=head2 find_files
97
98This method determines the potential file paths to be used for config loading.
99It returns an array of paths (up to the filename less the extension) to pass to
100L<Config::Any|Config::Any> for loading.
101
102=cut
103
104sub find_files {
105 my $c = shift;
106 my( $path, $extension ) = $c->get_config_path;
107 my $suffix = $c->get_config_local_suffix;
108 my @extensions = @{ Config::Any->extensions };
109
110 my @files;
111 if ($extension) {
112 next unless grep { $_ eq $extension } @extensions;
113 push @files, $path, "${path}_${suffix}";
114 } else {
115 @files = map { ( "$path.$_", "${path}_${suffix}.$_" ) } @extensions;
116 }
117
118 @files;
119}
120
121=head2 get_config_path
122
123This method determines the path, filename prefix and file extension to be used
124for config loading. It returns the path (up to the filename less the
125extension) to check and the specific extension to use (if it was specified).
126
127The order of preference is specified as:
128
129=over 4
130
131=item * C<$ENV{ MYAPP_CONFIG }>
132
133=item * C<$c-E<gt>config-E<gt>{ file }>
134
135=item * C<$c-E<gt>path_to( $application_prefix )>
136
137=back
138
139If either of the first two user-specified options are directories, the
140application prefix will be added on to the end of the path.
141
142=cut
143
144sub get_config_path {
145 my $c = shift;
146 my $appname = ref $c || $c;
147 my $prefix = Catalyst::Utils::appprefix( $appname );
148 my $path = $ENV{ Catalyst::Utils::class2env( $appname ) . '_CONFIG' }
149 || $c->config->{ file }
150 || $c->path_to( $prefix );
151
152 my( $extension ) = ( $path =~ m{\.(.{1,4})$} );
153
154 if( -d $path ) {
155 $path =~ s{[\/\\]$}{};
156 $path .= "/$prefix";
157 }
158
159 return( $path, $extension );
160}
161
162=head2 get_config_local_suffix
163
164Determines the suffix of files used to override the main config. By default
165this value is C<local>, but it can be specified in the following order of preference:
166
167=over 4
168
169=item * C<$ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }>
170
171=item * C<$ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }>
172
173=item * C<$c-E<gt>config-E<gt>{ config_local_suffix }>
174
175=back
176
177=cut
178
179sub get_config_local_suffix {
180 my $c = shift;
181 my $appname = ref $c || $c;
182 my $suffix = $ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }
183 || $ENV{ Catalyst::Utils::class2env( $appname ) . '_CONFIG_LOCAL_SUFFIX' }
184 || $c->config->{ config_local_suffix }
185 || 'local';
186
187 return $suffix;
188}
189
190sub _fix_syntax {
191 my $config = shift;
192 my @components = (
193 map +{
194 prefix => $_ eq 'Component' ? '' : $_ . '::',
195 values => delete $config->{ lc $_ } || delete $config->{ $_ }
196 },
197 grep {
198 ref $config->{ lc $_ } || ref $config->{ $_ }
199 }
200 qw( Component Model M View V Controller C )
201 );
202
203 foreach my $comp ( @components ) {
204 my $prefix = $comp->{ prefix };
205 foreach my $element ( keys %{ $comp->{ values } } ) {
206 $config->{ "$prefix$element" } = $comp->{ values }->{ $element };
207 }
208 }
209}
210
211=head2 finalize_config
212
213This method is called after the config file is loaded. It can be
214used to implement tuning of config values that can only be done
215at runtime. If you need to do this to properly configure any
216plugins, it's important to load ConfigLoader before them.
217ConfigLoader provides a default finalize_config method which
218walks through the loaded config hash and replaces any strings
219beginning containing C<__HOME__> with the full path to
220app's home directory (i.e. C<$c-E<gt>path_to('')> ).
221You can also use C<__path_to(foo/bar)__> which translates to
222C<$c-E<gt>path_to('foo', 'bar')>
223
224=cut
225
226sub finalize_config {
227 my $c = shift;
228 my $v = Data::Visitor::Callback->new(
229 plain_value => sub {
230 return unless defined $_;
231 s{__HOME__}{ $c->path_to( '' ) }e;
232 s{__path_to\((.+)\)__}{ $c->path_to( split( '/', $1 ) ) }e;
233 }
234 );
235 $v->visit( $c->config );
236}
237
238=head1 AUTHOR
239
240=over 4
241
242=item * Brian Cassidy E<lt>bricas@cpan.orgE<gt>
243
244=back
245
246=head1 CONTRIBUTORS
247
248The following people have generously donated their time to the
249development of this module:
250
251=over 4
252
253=item * Joel Bernstein E<lt>rataxis@cpan.orgE<gt> - Rewrite to use L<Config::Any>
254
255=item * David Kamholz E<lt>dkamholz@cpan.orgE<gt> - L<Data::Visitor> integration
256
257=back
258
259Work to this module has been generously sponsored by:
260
261=over 4
262
263=item * Portugal Telecom L<http://www.sapo.pt/> - Work done by Joel Bernstein
264
265=back
266
267=head1 COPYRIGHT AND LICENSE
268
269Copyright 2006 by Brian Cassidy
270
271This library is free software; you can redistribute it and/or modify
272it under the same terms as Perl itself.
273
274=head1 SEE ALSO
275
276=over 4
277
278=item * L<Catalyst>
279
280=item * L<Config::Any>
281
282=back
283
284=cut
285
2861;