Commit | Line | Data |
b2d85594 |
1 | package Catalyst::Plugin::ConfigLoader;\r |
2 | \r |
3 | use strict;\r |
4 | use warnings;\r |
5 | \r |
6 | use NEXT;\r |
3b6330a4 |
7 | use Module::Pluggable::Object ();\r |
9742697f |
8 | use Data::Visitor::Callback;\r |
b2d85594 |
9 | \r |
6015f463 |
10 | our $VERSION = '0.12';\r |
b2d85594 |
11 | \r |
12 | =head1 NAME\r |
13 | \r |
14 | Catalyst::Plugin::ConfigLoader - Load config files of various types\r |
15 | \r |
16 | =head1 SYNOPSIS\r |
17 | \r |
c7413665 |
18 | package MyApp;\r |
19 | \r |
d2afa071 |
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 |
135fd39b |
23 | \r |
b2d85594 |
24 | # by default myapp.* will be loaded\r |
25 | # you can specify a file if you'd like\r |
87dd9ae0 |
26 | __PACKAGE__->config( file => 'config.yaml' ); \r |
b2d85594 |
27 | \r |
28 | =head1 DESCRIPTION\r |
29 | \r |
04ea923f |
30 | This module will attempt to load find and load a configuration\r |
b2d85594 |
31 | file of various types. Currently it supports YAML, JSON, XML,\r |
32 | INI and Perl formats.\r |
33 | \r |
04ea923f |
34 | To support the distinction between development and production environments,\r |
35 | this module will also attemp to load a local config (e.g. myapp_local.yaml)\r |
36 | which will override any duplicate settings.\r |
37 | \r |
b2d85594 |
38 | =head1 METHODS\r |
39 | \r |
40 | =head2 setup( )\r |
41 | \r |
42 | This method is automatically called by Catalyst's setup routine. It will\r |
d6277728 |
43 | attempt to use each plugin and, once a file has been successfully\r |
04ea923f |
44 | loaded, set the C<config()> section. \r |
b2d85594 |
45 | \r |
46 | =cut\r |
47 | \r |
48 | sub setup {\r |
87dd9ae0 |
49 | my $c = shift;\r |
50 | my( $path, $extension ) = $c->get_config_path;\r |
b773e078 |
51 | my $suffix = $c->get_config_local_suffix;\r |
3b6330a4 |
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 |
c7413665 |
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 |
b773e078 |
66 | @files = map { ( "$path.$_", "${path}_${suffix}.$_" ) } @extensions;\r |
c7413665 |
67 | }\r |
68 | \r |
69 | for( @files ) {\r |
70 | next unless -f $_;\r |
71 | my $config = $loader->load( $_ );\r |
87dd9ae0 |
72 | \r |
172de8aa |
73 | $c->log->debug( qq(Loaded Config "$_") ) if $c->debug;\r |
7b57af98 |
74 | \r |
75 | next if !$config;\r |
76 | \r |
77 | _fix_syntax( $config );\r |
78 | \r |
7b57af98 |
79 | $c->config( $config );\r |
b2d85594 |
80 | }\r |
81 | }\r |
82 | \r |
9742697f |
83 | $c->finalize_config;\r |
84 | \r |
b2d85594 |
85 | $c->NEXT::setup( @_ );\r |
86 | }\r |
87 | \r |
9742697f |
88 | =head2 finalize_config\r |
89 | \r |
90 | This method is called after the config file is loaded. It can be\r |
91 | used to implement tuning of config values that can only be done\r |
92 | at runtime. If you need to do this to properly configure any\r |
93 | plugins, it's important to load ConfigLoader before them.\r |
94 | ConfigLoader provides a default finalize_config method which\r |
95 | walks through the loaded config hash and replaces any strings\r |
04ea923f |
96 | beginning containing C<__HOME__> with the full path to\r |
97 | app's home directory (i.e. C<$c-E<gt>path_to('')> ).\r |
6015f463 |
98 | You can also use C<__path_to(foo/bar)__> which translates to\r |
04ea923f |
99 | C<$c-E<gt>path_to('foo', 'bar')> \r |
9742697f |
100 | \r |
101 | =cut\r |
102 | \r |
103 | sub finalize_config {\r |
104 | my $c = shift;\r |
105 | my $v = Data::Visitor::Callback->new(\r |
da18b925 |
106 | plain_value => sub {\r |
107 | return unless defined $_;\r |
3b6330a4 |
108 | s{__HOME__}{ $c->path_to( '' ) }e;\r |
109 | s{__path_to\((.+)\)__}{ $c->path_to( split( '/', $1 ) ) }e;\r |
da18b925 |
110 | }\r |
9742697f |
111 | );\r |
dc4daea5 |
112 | $v->visit( $c->config );\r |
9742697f |
113 | }\r |
114 | \r |
87dd9ae0 |
115 | =head2 get_config_path\r |
116 | \r |
117 | This method determines the path, filename prefix and file extension to be used\r |
118 | for config loading. It returns the path (up to the filename less the\r |
119 | extension) to check and the specific extension to use (if it was specified).\r |
120 | \r |
121 | The order of preference is specified as:\r |
122 | \r |
123 | =over 4\r |
124 | \r |
125 | =item * C<$ENV{ MYAPP_CONFIG }>\r |
126 | \r |
b773e078 |
127 | =item * C<$c-E<gt>config-E<gt>{ file }>\r |
87dd9ae0 |
128 | \r |
b773e078 |
129 | =item * C<$c-E<gt>path_to( $application_prefix )>\r |
87dd9ae0 |
130 | \r |
131 | =back\r |
132 | \r |
133 | If either of the first two user-specified options are directories, the\r |
134 | application prefix will be added on to the end of the path.\r |
135 | \r |
136 | =cut\r |
137 | \r |
138 | sub 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 |
3b6330a4 |
146 | my( $extension ) = ( $path =~ m{\.(.{1,4})$} );\r |
87dd9ae0 |
147 | \r |
148 | if( -d $path ) {\r |
3b6330a4 |
149 | $path =~ s{[\/\\]$}{};\r |
87dd9ae0 |
150 | $path .= "/$prefix";\r |
151 | }\r |
152 | \r |
153 | return( $path, $extension );\r |
154 | }\r |
155 | \r |
b773e078 |
156 | =head2 get_config_local_suffix\r |
157 | \r |
158 | Determines the suffix of files used to override the main config. By default\r |
159 | this 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 |
174 | sub 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 |
1502c009 |
185 | sub _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 |
d1b52b01 |
191 | },\r |
192 | grep {\r |
193 | ref $config->{ lc $_ } || ref $config->{ $_ }\r |
194 | }\r |
6e7f9907 |
195 | qw( Component Model M View V Controller C )\r |
1502c009 |
196 | );\r |
197 | \r |
135fd39b |
198 | foreach my $comp ( @components ) {\r |
1502c009 |
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 |
b2d85594 |
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 |
a8288af5 |
214 | =head1 CONTRIBUTORS\r |
215 | \r |
216 | The following people have generously donated their time to the\r |
217 | development 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 |
b2d85594 |
225 | =head1 COPYRIGHT AND LICENSE\r |
226 | \r |
227 | Copyright 2006 by Brian Cassidy\r |
228 | \r |
229 | This library is free software; you can redistribute it and/or modify\r |
230 | it 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 |
a07967b8 |
242 | 1;\r |