| 1 | package Config::Multi; |
|---|
| 2 | |
|---|
| 3 | use strict; |
|---|
| 4 | use warnings; |
|---|
| 5 | use DirHandle; |
|---|
| 6 | use File::Spec; |
|---|
| 7 | use FindBin; |
|---|
| 8 | use Config::Any; |
|---|
| 9 | use Carp; |
|---|
| 10 | |
|---|
| 11 | use base qw/Class::Accessor/; |
|---|
| 12 | |
|---|
| 13 | our $VERSION = '0.03'; |
|---|
| 14 | |
|---|
| 15 | __PACKAGE__->mk_accessors(qw/app_name prefix dir files extension/); |
|---|
| 16 | |
|---|
| 17 | sub load { |
|---|
| 18 | my $self = shift; |
|---|
| 19 | my @files = (); |
|---|
| 20 | $self->{extension} ||= 'yml'; |
|---|
| 21 | croak('you must set dir') unless $self->{dir}; |
|---|
| 22 | croak('you must set app_name') unless $self->{app_name}; |
|---|
| 23 | |
|---|
| 24 | my $config = {}; |
|---|
| 25 | |
|---|
| 26 | my $app_files = $self->_find_files( $self->{app_name} ); |
|---|
| 27 | my $app = Config::Any->load_files( { files => $app_files } ); |
|---|
| 28 | for ( @{$app} ) { |
|---|
| 29 | my ( $filename, $data ) = %$_; |
|---|
| 30 | push @files, $filename; |
|---|
| 31 | $config = { %{$config}, %{$data} }; |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | if ( $self->{prefix} ) { |
|---|
| 35 | my $prefix_files |
|---|
| 36 | = $self->_find_files( $self->{prefix} . '_' . $self->{app_name} ); |
|---|
| 37 | my $prefix = Config::Any->load_files( { files => $prefix_files } ); |
|---|
| 38 | for ( @{$prefix} ) { |
|---|
| 39 | my ( $filename, $data ) = %$_; |
|---|
| 40 | push @files, $filename; |
|---|
| 41 | $config = { %{$config}, %{$data} }; |
|---|
| 42 | } |
|---|
| 43 | } |
|---|
| 44 | |
|---|
| 45 | my $local_files = $self->_local_files; |
|---|
| 46 | my $local = Config::Any->load_files( { files => $local_files } ); |
|---|
| 47 | my $local_config = {} ; |
|---|
| 48 | for ( @{$local} ) { |
|---|
| 49 | my ( $filename, $data ) = %$_; |
|---|
| 50 | push @files, $filename; |
|---|
| 51 | $local_config->{$filename} = $data; |
|---|
| 52 | } |
|---|
| 53 | |
|---|
| 54 | for (@{$local_files} ) { |
|---|
| 55 | $config = { %{$config}, %{ $local_config->{$_} } }; |
|---|
| 56 | } |
|---|
| 57 | |
|---|
| 58 | $self->{files} = \@files; |
|---|
| 59 | |
|---|
| 60 | return $config; |
|---|
| 61 | } |
|---|
| 62 | |
|---|
| 63 | sub _local_files { |
|---|
| 64 | my $self = shift; |
|---|
| 65 | my $env_app_key = 'CONFIG_MULTI_' . uc( $self->{app_name} ); |
|---|
| 66 | my $env_prefix_key |
|---|
| 67 | = 'CONFIG_MULTI_' |
|---|
| 68 | . uc( $self->{prefix} ) . '_' |
|---|
| 69 | . uc( $self->{app_name} ); |
|---|
| 70 | my @files = (); |
|---|
| 71 | my $app_lcoal = $ENV{$env_app_key} |
|---|
| 72 | || File::Spec->catfile( $self->dir, |
|---|
| 73 | $self->{app_name} . '_local.' . $self->extension ); |
|---|
| 74 | push @files, $app_lcoal if -e $app_lcoal; |
|---|
| 75 | |
|---|
| 76 | if ( $self->{prefix} ) { |
|---|
| 77 | my $prefix_local = $ENV{$env_prefix_key} || File::Spec->catfile( |
|---|
| 78 | $self->dir, |
|---|
| 79 | $self->{prefix} . '_' |
|---|
| 80 | . $self->{app_name} |
|---|
| 81 | . '_local.' |
|---|
| 82 | . $self->extension |
|---|
| 83 | ); |
|---|
| 84 | push @files, $prefix_local if -e $prefix_local; |
|---|
| 85 | } |
|---|
| 86 | |
|---|
| 87 | return \@files; |
|---|
| 88 | } |
|---|
| 89 | |
|---|
| 90 | sub _find_files { |
|---|
| 91 | my $self = shift; |
|---|
| 92 | my $path = $self->dir; |
|---|
| 93 | my $label = shift; |
|---|
| 94 | my $extension = $self->extension; |
|---|
| 95 | |
|---|
| 96 | my @files; |
|---|
| 97 | my $dh = DirHandle->new($path) or croak "Could not Open " . $path; |
|---|
| 98 | |
|---|
| 99 | while ( my $file = $dh->read() ) { |
|---|
| 100 | next if $file =~ /local\.$extension$/; |
|---|
| 101 | if ( $file =~ /^$label\.yml$/ |
|---|
| 102 | || $file =~ /^$label\_(\w+)\.$extension$/ ) |
|---|
| 103 | { |
|---|
| 104 | push @files, File::Spec->catfile( $path, $file ); |
|---|
| 105 | } |
|---|
| 106 | } |
|---|
| 107 | |
|---|
| 108 | return \@files; |
|---|
| 109 | } |
|---|
| 110 | |
|---|
| 111 | 1; |
|---|
| 112 | |
|---|
| 113 | =head1 NAME |
|---|
| 114 | |
|---|
| 115 | Config::Multi - load multiple config files. |
|---|
| 116 | |
|---|
| 117 | =head1 SYNOPSIS |
|---|
| 118 | |
|---|
| 119 | use Config::Multi; |
|---|
| 120 | use File::Spec; |
|---|
| 121 | use FindBin; |
|---|
| 122 | |
|---|
| 123 | my $dir = File::Spec->catfile( $FindBin::Bin , 'conf' ); |
|---|
| 124 | |
|---|
| 125 | # prefix and extension is optional. |
|---|
| 126 | my $cm |
|---|
| 127 | = Config::Multi->new({ |
|---|
| 128 | dir => $dir , |
|---|
| 129 | app_name => 'myapp' , |
|---|
| 130 | prefix => 'web' , |
|---|
| 131 | extension => 'yml' |
|---|
| 132 | }); |
|---|
| 133 | my $config = $cm->load(); |
|---|
| 134 | my $loaded_config_files = $cm->files; |
|---|
| 135 | |
|---|
| 136 | =head1 DESCRIPTION |
|---|
| 137 | |
|---|
| 138 | This module load multiple config files using L<Config::Any>. You can specify directory and put into your config files! |
|---|
| 139 | |
|---|
| 140 | I create this module because I want to load not only loading multiple config files but also switch config files depend on interface I am using. like, I want to load web.yml only for web interface configuration and cli.yml for only for client interface configuration. let me explain step by step at EXAMPLE section. |
|---|
| 141 | |
|---|
| 142 | =head1 EXAMPLE |
|---|
| 143 | |
|---|
| 144 | =head2 your configuration files |
|---|
| 145 | |
|---|
| 146 | This is under your ~/myapp/conf/ and have yaml configuration in each files. you can specify the directory using dir option. |
|---|
| 147 | |
|---|
| 148 | . |
|---|
| 149 | |-- env-prefix.yml |
|---|
| 150 | |-- env.yml |
|---|
| 151 | |-- myapp.yml |
|---|
| 152 | |-- myapp_boin.yml |
|---|
| 153 | |-- myapp_local.yml |
|---|
| 154 | |-- myapp_oppai.yml |
|---|
| 155 | |-- never_load.yml |
|---|
| 156 | |-- web_myapp.yml |
|---|
| 157 | |-- web_myapp_cat.yml |
|---|
| 158 | |-- web_myapp_dog.yml |
|---|
| 159 | `-- web_myapp_local.yml |
|---|
| 160 | |
|---|
| 161 | =head2 switchable |
|---|
| 162 | |
|---|
| 163 | when you set app_name as 'myapp' and prefix as 'jobqueue' then below files are loaded |
|---|
| 164 | |
|---|
| 165 | |-- myapp.yml |
|---|
| 166 | |-- myapp_boin.yml |
|---|
| 167 | |-- myapp_local.yml |
|---|
| 168 | |-- myapp_oppai.yml |
|---|
| 169 | |
|---|
| 170 | ${app_name}.yml or ${app_name}_*.yml |
|---|
| 171 | |
|---|
| 172 | when you set app_name as 'myapp' and prefix as 'web' then below files are loaded |
|---|
| 173 | |
|---|
| 174 | |-- myapp.yml |
|---|
| 175 | |-- myapp_boin.yml |
|---|
| 176 | |-- myapp_local.yml |
|---|
| 177 | |-- myapp_oppai.yml |
|---|
| 178 | |-- web_myapp.yml |
|---|
| 179 | |-- web_myapp_cat.yml |
|---|
| 180 | |-- web_myapp_dog.yml |
|---|
| 181 | `-- web_myapp_local.yml |
|---|
| 182 | |
|---|
| 183 | ${prefix}_${myapp}.yml ${prefix}_${myapp}_*.yml |
|---|
| 184 | |
|---|
| 185 | YES! you can switch config files depend on what you set for app_name and prefix. |
|---|
| 186 | |
|---|
| 187 | =head2 overwrite rule. |
|---|
| 188 | |
|---|
| 189 | there is also overwriting rule. there are three steps for this. |
|---|
| 190 | |
|---|
| 191 | |
|---|
| 192 | _local.yml file overwrite the other config setting |
|---|
| 193 | |
|---|
| 194 | ${prefix}_${app_name}_local.yml |
|---|
| 195 | ${app_name}_local.yml |
|---|
| 196 | |
|---|
| 197 | ${prefix}_ files overwrite ${app_name} config setting |
|---|
| 198 | |
|---|
| 199 | ${app_name}.yml, ${app_name}_*.yml (not include ${app_name}_local.yml) |
|---|
| 200 | |
|---|
| 201 | app config. |
|---|
| 202 | |
|---|
| 203 | ${prefix}_${myapp}.yml ${prefix}_${myapp}_*.yml |
|---|
| 204 | |
|---|
| 205 | =head2 $ENV setting |
|---|
| 206 | |
|---|
| 207 | instead of ${prefix}_${app_name}_local.yml , you can specify the path with $ENV{CONFIG_MULTI_PREFIX_MYAPP} |
|---|
| 208 | |
|---|
| 209 | instead of ${app_name}_local.yml , you can specify the path with $ENV{CONFIG_MULTI_MYAPP} |
|---|
| 210 | |
|---|
| 211 | note. PREFIX = uc($prefix); MYAPP = uc($app_name) |
|---|
| 212 | |
|---|
| 213 | =head1 METHODS |
|---|
| 214 | |
|---|
| 215 | =head2 new |
|---|
| 216 | |
|---|
| 217 | constructor SEE CONSTRUCTOR ARGUMENT section. |
|---|
| 218 | |
|---|
| 219 | =head2 load |
|---|
| 220 | |
|---|
| 221 | load config files and return config data. |
|---|
| 222 | |
|---|
| 223 | =head2 files |
|---|
| 224 | |
|---|
| 225 | get array references of loaded config files. You can use this method after call load() method. |
|---|
| 226 | |
|---|
| 227 | =head1 CONSTRUCTOR ARGUMENT |
|---|
| 228 | |
|---|
| 229 | =head2 app_name |
|---|
| 230 | |
|---|
| 231 | your application name. use [a-z]+ for format. |
|---|
| 232 | |
|---|
| 233 | =head2 prefix |
|---|
| 234 | |
|---|
| 235 | prefix name . use [a-z]+ for format. this is optional. if you did not set. only application config is loaded(include appname_local.yml if you have. ) |
|---|
| 236 | |
|---|
| 237 | =head2 dir |
|---|
| 238 | |
|---|
| 239 | specify directory where your config files are located. |
|---|
| 240 | |
|---|
| 241 | =head2 extension |
|---|
| 242 | |
|---|
| 243 | you must specify extension for your config files. default is yml. |
|---|
| 244 | |
|---|
| 245 | =head1 SEE ALSO |
|---|
| 246 | |
|---|
| 247 | L<Config::Any> |
|---|
| 248 | |
|---|
| 249 | =head1 AUTHOR |
|---|
| 250 | |
|---|
| 251 | Tomohiro Teranishi <tomohiro.teranishi@gmail.com> |
|---|
| 252 | |
|---|
| 253 | =head1 COPYRIGHT |
|---|
| 254 | |
|---|
| 255 | This module is copyright 2008 Tomohiro Teranishi. |
|---|
| 256 | |
|---|
| 257 | =head1 LICENSE |
|---|
| 258 | |
|---|
| 259 | This library is free software; you can redistribute it and/or modify |
|---|
| 260 | it under the same terms as Perl itself. |
|---|
| 261 | |
|---|
| 262 | =cut |
|---|
| 263 | |
|---|