use warnings;
use Config::Any;
-use NEXT;
+use MRO::Compat;
use Data::Visitor::Callback;
use Catalyst::Utils ();
-our $VERSION = '0.16';
+our $VERSION = '0.25';
=head1 NAME
=head1 SYNOPSIS
package MyApp;
-
+
# ConfigLoader should be first in your list so
# other plugins can get the config information
use Catalyst qw( ConfigLoader ... );
-
+
# by default myapp.* will be loaded
# you can specify a file if you'd like
- __PACKAGE__->config( file => 'config.yaml' );
+ __PACKAGE__->config( 'Plugin::ConfigLoader' => { file => 'config.yaml' } );
+
+ In the file, assuming it's in YAML format:
+
+ foo: bar
+
+ Accessible through the context object, or the class itself
+
+ $c->config->{foo} # bar
+ MyApp->config->{foo} # bar
=head1 DESCRIPTION
This module will attempt to load find and load a configuration
file of various types. Currently it supports YAML, JSON, XML,
-INI and Perl formats.
+INI and Perl formats. Special configuration for a particular driver format can
+be stored in C<MyApp-E<gt>config-E<gt>{ 'Plugin::ConfigLoader' }-E<gt>{ driver }>.
+For example, to pass arguments to L<Config::General>, use the following:
+
+ __PACKAGE__->config( 'Plugin::ConfigLoader' => {
+ driver => {
+ 'General' => { -LowerCaseNames => 1 }
+ }
+ } );
+
+See L<Config::Any>'s C<driver_args> parameter for more information.
To support the distinction between development and production environments,
this module will also attemp to load a local config (e.g. myapp_local.yaml)
-which will override any duplicate settings.
+which will override any duplicate settings. See
+L<get_config_local_suffix|/get_config_local_suffix>
+for details on how this is configured.
=head1 METHODS
This method is automatically called by Catalyst's setup routine. It will
attempt to use each plugin and, once a file has been successfully
-loaded, set the C<config()> section.
+loaded, set the C<config()> section.
=cut
sub setup {
my $c = shift;
my @files = $c->find_files;
- my $cfg = Config::Any->load_files( {
- files => \@files,
- filter => \&_fix_syntax,
- use_ext => 1
- } );
+ my $cfg = Config::Any->load_files(
+ { files => \@files,
+ filter => \&_fix_syntax,
+ use_ext => 1,
+ driver_args => $c->config->{ 'Plugin::ConfigLoader' }->{ driver }
+ || {},
+ }
+ );
+ # map the array of hashrefs to a simple hash
+ my %configs = map { %$_ } @$cfg;
# split the responses into normal and local cfg
my $local_suffix = $c->get_config_local_suffix;
- my( @cfg, @localcfg );
- for( @$cfg ) {
- if( ( keys %$_ )[ 0 ] =~ m{ $local_suffix \. }xms ) {
- push @localcfg, $_;
- } else {
- push @cfg, $_;
+ my ( @main, @locals );
+ for ( sort keys %configs ) {
+ if ( m{$local_suffix\.}ms ) {
+ push @locals, $_;
+ }
+ else {
+ push @main, $_;
}
}
-
+
# load all the normal cfgs, then the local cfgs last so they can override
# normal cfgs
- $c->load_config( $_ ) for @cfg, @localcfg;
+ $c->load_config( { $_ => $configs{ $_ } } ) for @main, @locals;
$c->finalize_config;
- $c->NEXT::setup( @_ );
+ $c->next::method( @_ );
}
=head2 load_config
sub load_config {
my $c = shift;
my $ref = shift;
-
- my( $file, $config ) = each %$ref;
-
+
+ my ( $file, $config ) = %$ref;
+
$c->config( $config );
$c->log->debug( qq(Loaded Config "$file") )
if $c->debug;
sub find_files {
my $c = shift;
- my( $path, $extension ) = $c->get_config_path;
+ my ( $path, $extension ) = $c->get_config_path;
my $suffix = $c->get_config_local_suffix;
my @extensions = @{ Config::Any->extensions };
-
+
my @files;
- if ($extension) {
- next unless grep { $_ eq $extension } @extensions;
- push @files, $path, "${path}_${suffix}";
- } else {
+ if ( $extension ) {
+ die "Unable to handle files with the extension '${extension}'"
+ unless grep { $_ eq $extension } @extensions;
+ ( my $local = $path ) =~ s{\.$extension}{_$suffix.$extension};
+ push @files, $path, $local;
+ }
+ else {
@files = map { ( "$path.$_", "${path}_${suffix}.$_" ) } @extensions;
}
-
@files;
}
=item * C<$ENV{ CATALYST_CONFIG }>
-=item * C<$c-E<gt>config-E<gt>{ 'Plugin::ConfigLoader' }-E>gt>{ file }>
+=item * C<$c-E<gt>config-E<gt>{ 'Plugin::ConfigLoader' }-E<gt>{ file }>
=item * C<$c-E<gt>path_to( $application_prefix )>
If either of the first two user-specified options are directories, the
application prefix will be added on to the end of the path.
-DEPRECATION NOTICE: C<$c-E<gt>config-E<gt>{ file }> is deprecated
-and will be removed in the next release.
-
=cut
sub get_config_path {
- my $c = shift;
+ my $c = shift;
+
+
my $appname = ref $c || $c;
my $prefix = Catalyst::Utils::appprefix( $appname );
- my $path = Catalyst::Utils::env_value( $c, 'CONFIG' )
+ my $path = Catalyst::Utils::env_value( $appname, 'CONFIG' )
|| $c->config->{ 'Plugin::ConfigLoader' }->{ file }
- || $c->config->{ file } # to be removed next release
|| $c->path_to( $prefix );
- my( $extension ) = ( $path =~ m{\.(.{1,4})$} );
-
- if( -d $path ) {
- $path =~ s{[\/\\]$}{};
+ my ( $extension ) = ( $path =~ m{\.(.{1,4})$} );
+
+ if ( -d $path ) {
+ $path =~ s{[\/\\]$}{};
$path .= "/$prefix";
}
-
- return( $path, $extension );
+
+ return ( $path, $extension );
}
=head2 get_config_local_suffix
Determines the suffix of files used to override the main config. By default
-this value is C<local>, but it can be specified in the following order of preference:
+this value is C<local>, which will load C<myapp_local.conf>. The suffix can
+be specified in the following order of preference:
=over 4
=back
-DEPRECATION NOTICE: C<$c-E<gt>config-E<gt>{ config_local_suffix }> is deprecated
-and will be removed in the next release.
+The first one of these values found replaces the default of C<local> in the
+name of the local config file to be loaded.
+
+For example, if C< $ENV{ MYAPP_CONFIG_LOCAL_SUFFIX }> is set to C<testing>,
+ConfigLoader will try and load C<myapp_testing.conf> instead of
+C<myapp_local.conf>.
=cut
sub get_config_local_suffix {
- my $c = shift;
+ my $c = shift;
+
my $appname = ref $c || $c;
- my $suffix = Catalyst::Utils::env_value( $c, 'CONFIG_LOCAL_SUFFIX' )
+ my $suffix = Catalyst::Utils::env_value( $appname, 'CONFIG_LOCAL_SUFFIX' )
|| $c->config->{ 'Plugin::ConfigLoader' }->{ config_local_suffix }
- || $c->config->{ config_local_suffix } # to be remove in the next release
|| 'local';
return $suffix;
prefix => $_ eq 'Component' ? '' : $_ . '::',
values => delete $config->{ lc $_ } || delete $config->{ $_ }
},
- grep {
- ref $config->{ lc $_ } || ref $config->{ $_ }
- }
- qw( Component Model M View V Controller C )
+ grep { ref $config->{ lc $_ } || ref $config->{ $_ } }
+ qw( Component Model M View V Controller C Plugin )
);
foreach my $comp ( @components ) {
=item * C<__HOME__> - replaced with C<$c-E<gt>path_to('')>
+=item * C<__ENV(foo)__> - replaced with the value of C<$ENV{foo}>
+
=item * C<__path_to(foo/bar)__> - replaced with C<$c-E<gt>path_to('foo/bar')>
=item * C<__literal(__FOO__)__> - leaves __FOO__ alone (allows you to use
=cut
sub config_substitutions {
- my $c = shift;
- my $subs = $c->config->{ 'Plugin::ConfigLoader' }->{ substitutions } || {};
- $subs->{ HOME } ||= sub { shift->path_to( '' ); };
+ my $c = shift;
+ my $subs = $c->config->{ 'Plugin::ConfigLoader' }->{ substitutions }
+ || {};
+ $subs->{ HOME } ||= sub { shift->path_to( '' ); };
+ $subs->{ ENV } ||=
+ sub {
+ my ( $c, $v ) = @_;
+ if (! defined($ENV{$v})) {
+ Catalyst::Exception->throw( message =>
+ "Missing environment variable: $v" );
+ return "";
+ } else {
+ return $ENV{ $v };
+ }
+ };
$subs->{ path_to } ||= sub { shift->path_to( @_ ); };
$subs->{ literal } ||= sub { return $_[ 1 ]; };
my $subsre = join( '|', keys %$subs );
}
}
-
=head1 AUTHOR
Brian Cassidy E<lt>bricas@cpan.orgE<gt>
=item * David Kamholz E<lt>dkamholz@cpan.orgE<gt> - L<Data::Visitor> integration
+=item * Stuart Watt - Addition of ENV macro.
+
=back
-Work to this module has been generously sponsored by:
+Work to this module has been generously sponsored by:
=over 4
=head1 COPYRIGHT AND LICENSE
-Copyright 2007 by Brian Cassidy
+Copyright 2006-2009 by Brian Cassidy
This library is free software; you can redistribute it and/or modify
-it under the same terms as Perl itself.
+it under the same terms as Perl itself.
=head1 SEE ALSO
-=over 4
+=over 4
=item * L<Catalyst>