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