bumped version
[catagits/Catalyst-Plugin-ConfigLoader.git] / lib / Catalyst / Plugin / ConfigLoader.pm
index ef29444..f5c416b 100644 (file)
@@ -8,8 +8,9 @@ use Module::Pluggable::Fast
     name    => '_config_loaders',\r
     search  => [ __PACKAGE__ ],\r
     require => 1;\r
+use Data::Visitor::Callback;\r
 \r
-our $VERSION = '0.01';\r
+our $VERSION = '0.1';\r
 \r
 =head1 NAME\r
 \r
@@ -17,46 +18,159 @@ Catalyst::Plugin::ConfigLoader - Load config files of various types
 \r
 =head1 SYNOPSIS\r
 \r
-       package MyApp;\r
-       \r
-       use Catalyst( ConfigLoader );\r
-       \r
+    package MyApp;\r
+    \r
+    # ConfigLoader should be first in your list so\r
+    # other plugins can get the config information\r
+    use Catalyst qw( ConfigLoader ... );\r
+    \r
     # by default myapp.* will be loaded\r
     # you can specify a file if you'd like\r
-    __PACKAGE__->config( file = > 'config.yaml' );\r
-    \r
+    __PACKAGE__->config( file => 'config.yaml' );    \r
 \r
 =head1 DESCRIPTION\r
 \r
-This mdoule will attempt to load find and load a configuration\r
+This module will attempt to load find and load a configuration\r
 file of various types. Currently it supports YAML, JSON, XML,\r
 INI and Perl formats.\r
 \r
+To support the distinction between development and production environments,\r
+this module will also attemp to load a local config (e.g. myapp_local.yaml)\r
+which will override any duplicate settings.\r
+\r
 =head1 METHODS\r
 \r
 =head2 setup( )\r
 \r
 This method is automatically called by Catalyst's setup routine. It will\r
-attempt to use each plugin and set the C<config()> section once a file has been\r
-successfully loaded.\r
+attempt to use each plugin and, once a file has been successfully\r
+loaded, set the C<config()> section. \r
 \r
 =cut\r
 \r
 sub setup {\r
-    my $c        = shift;\r
-    my $confpath = $c->config->{ file } || $c->path_to( Catalyst::Utils::appprefix( ref $c || $c ) );\r
+    my $c = shift;\r
+    my( $path, $extension ) = $c->get_config_path;\r
     \r
     for my $loader ( $c->_config_loaders ) {\r
-        my $config = $loader->load( $confpath );\r
-        if( $config ) {\r
+        my @files;\r
+        my @extensions = $loader->extensions;\r
+        if( $extension ) {\r
+            next unless grep { $_ eq $extension } @extensions;\r
+            push @files, $path;\r
+        }\r
+        else {\r
+            @files = map { ( "$path.$_", "${path}_local.$_" ) } @extensions;\r
+        }\r
+\r
+        for( @files ) {\r
+            next unless -f $_;\r
+            my $config = $loader->load( $_ );\r
+\r
+            $c->log->debug( "Loaded Config $_" ) if $c->debug;\r
+            \r
+            next if !$config;\r
+\r
+            _fix_syntax( $config );\r
+            \r
             $c->config( $config );\r
-            last;\r
         }\r
     }\r
 \r
+    $c->finalize_config;\r
+\r
     $c->NEXT::setup( @_ );\r
 }\r
 \r
+=head2 finalize_config\r
+\r
+This method is called after the config file is loaded. It can be\r
+used to implement tuning of config values that can only be done\r
+at runtime. If you need to do this to properly configure any\r
+plugins, it's important to load ConfigLoader before them.\r
+ConfigLoader provides a default finalize_config method which\r
+walks through the loaded config hash and replaces any strings\r
+beginning containing C<__HOME__> with the full path to\r
+app's home directory (i.e. C<$c-E<gt>path_to('')> ).\r
+You can also use C<__path_to('foo/bar')__> which translates to\r
+C<$c-E<gt>path_to('foo', 'bar')> \r
+\r
+=cut\r
+\r
+sub finalize_config {\r
+    my $c = shift;\r
+    my $v = Data::Visitor::Callback->new(\r
+        plain_value => sub {\r
+            return unless defined $_;\r
+            s[__HOME__][ $c->path_to( '' ) ]e;\r
+            s[__path_to\((.+)\)__][ $c->path_to( split( '/', $1 ) ) ]e;\r
+        }\r
+    );\r
+    $v->visit( $c->config );\r
+}\r
+\r
+=head2 get_config_path\r
+\r
+This method determines the path, filename prefix and file extension to be used\r
+for config loading. It returns the path (up to the filename less the\r
+extension) to check and the specific extension to use (if it was specified).\r
+\r
+The order of preference is specified as:\r
+\r
+=over 4\r
+\r
+=item * C<$ENV{ MYAPP_CONFIG }>\r
+\r
+=item * C<$c->config->{ file }>\r
+\r
+=item * C<$c->path_to( $application_prefix )>\r
+\r
+=back\r
+\r
+If either of the first two user-specified options are directories, the\r
+application prefix will be added on to the end of the path.\r
+\r
+=cut\r
+\r
+sub get_config_path {\r
+    my $c       = shift;\r
+    my $appname = ref $c || $c;\r
+    my $prefix  = Catalyst::Utils::appprefix( $appname );\r
+    my $path    = $ENV{ Catalyst::Utils::class2env( $appname ) . '_CONFIG' }\r
+        || $c->config->{ file }\r
+        || $c->path_to( $prefix );\r
+\r
+    my( $extension ) = ( $path =~ /\.(.{1,4})$/ );\r
+    \r
+    if( -d $path ) {\r
+        $path  =~ s/[\/\\]$//;\r
+        $path .= "/$prefix";\r
+    }\r
+    \r
+    return( $path, $extension );\r
+}\r
+\r
+sub _fix_syntax {\r
+    my $config     = shift;\r
+    my @components = (\r
+        map +{\r
+            prefix => $_ eq 'Component' ? '' : $_ . '::',\r
+            values => delete $config->{ lc $_ } || delete $config->{ $_ }\r
+        },\r
+        grep {\r
+            ref $config->{ lc $_ } || ref $config->{ $_ }\r
+        }\r
+        qw( Component Model M View V Controller C )\r
+    );\r
+\r
+    foreach my $comp ( @components ) {\r
+        my $prefix = $comp->{ prefix };\r
+        foreach my $element ( keys %{ $comp->{ values } } ) {\r
+            $config->{ "$prefix$element" } = $comp->{ values }->{ $element };\r
+        }\r
+    }\r
+}\r
+\r
 =head1 AUTHOR\r
 \r
 =over 4 \r
@@ -65,6 +179,17 @@ sub setup {
 \r
 =back\r
 \r
+=head1 CONTRIBUTORS\r
+\r
+The following people have generously donated their time to the\r
+development of this module:\r
+\r
+=over 4\r
+\r
+=item * David Kamholz E<lt>dkamholz@cpan.orgE<gt>\r
+\r
+=back\r
+\r
 =head1 COPYRIGHT AND LICENSE\r
 \r
 Copyright 2006 by Brian Cassidy\r
@@ -82,4 +207,4 @@ it under the same terms as Perl itself.
 \r
 =cut\r
 \r
-1;
\ No newline at end of file
+1;\r