日向夏特殊応援部隊

俺様向けメモ

Catalyst::Plugin::ConfigLoaderでconfigの切り替えを行う

はじめに

Catalyst::Plugin::ConfigLoaderを使うと異なる環境の為の設定を環境変数のみで制御出来ます。

下準備

ちょっと癖物なのが下記のように設定しなきゃいけないって事です。

package MyApp;

use Catalyst qw/ConfigLoader/;

__PACKAGE__->config(
    file => __PACKAGE__->path_to(conf)
);

で実際にconfディレクトリに下記のようにファイルを作ります。

$ ls conf/
myapp.json myapp_local.json myapp_service.json

こうして例えば、

$ cat conf/myapp.json
{
    name: 'MyApp',
    foo: 'foo'
}
$ cat conf/myapp_local.json
{
    foo: 'bar',
    baz: 'hoge'
}

のように設定したとすると、実際のconfigの中身には、myapp_local.jsonで指定した内容がmyapp.jsonの設定に上書きされます。

環境変数での切り替え

Catalyst::Plugin::ConfigLoaderのソースを見てみます。

Catalyst::Plugin::ConfigLoader->setup()
sub setup {
    my $c     = shift;
    my @files = $c->find_files;
    my $cfg   = Config::Any->load_files( {
        files   => \@files, 
        filter  => \&_fix_syntax,
        use_ext => 1
    } );

    # snip
}

Config::Anyに@filesを渡すまでが重要なようです。
find_filesメソッドを見てみましょう。

Catalyst::Plugin::ConfigLoader->find_files()
sub find_files {
    my $c = shift;
    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 {
        @files = map { ( "$path.$_", "${path}_${suffix}.$_" ) } @extensions;
    }

    @files;
}

get_config_path, get_config_local_suffixでそれぞれ値を取得していて、
その後の@filesの設定にはこれらの値の如何に応じて、設定が行われます。

Catalyst::Plugin::ConfigLoader->get_config_path()
sub get_config_path {
    my $c       = shift;
    my $appname = ref $c || $c;
    my $prefix  = Catalyst::Utils::appprefix( $appname );
    my $path    = $ENV{ Catalyst::Utils::class2env( $appname ) . '_CONFIG' }
        || $c->config->{ file }
        || $c->path_to( $prefix );

    my( $extension ) = ( $path =~ m{\.(.{1,4})$} );
    
    if( -d $path ) {
        $path  =~ s{[\/\\]$}{};
        $path .= "/$prefix";
    }
    
    return( $path, $extension );
}

設定値系の処理には必ずと言ってよいほどCatalyst::Utilsの関数が利用されてますね。
この手の処理とかPlugin, Componentだとかを作る場合は、
設定値だとかtmpディレクトリだとかはCatalyst::Utilsのお世話になるように作るべきですね。

  • $ENV{MYAPP_CONFIG}
  • $c->config->{file}
  • $c->path_to('myapp')

の順番でpathが設定されて、さらにpathがディレクトリの場合は、myappと言うsuffixを持つファイルを設定ファイルとみなすようになります。

Catalyst::Plugin::ConfigLoader->get_config_local_suffix()

ここが切り替えの為の答えです。

sub get_config_local_suffix {
    my $c       = shift;
    my $appname = ref $c || $c;
    my $suffix  = $ENV{ CATALYST_CONFIG_LOCAL_SUFFIX }
        || $ENV{ Catalyst::Utils::class2env( $appname ) . '_CONFIG_LOCAL_SUFFIX' }
        || $c->config->{ config_local_suffix }
        || 'local';

    return $suffix;
}
  • $ENV{CATALYST_CONFIG_LOCAL_SUFFIX}
  • $ENV{MYAPP_CONFIG_LOCAL_SUFFIX}
  • $c->config->{config_local_suffix}
  • local

の順番で、suffixが決まります。
具体的にはもう一度find_filesに戻ります。

my( $path, $extension ) = $c->get_config_path; # $path = 'conf/myapp', $extension = ''
my $suffix     = $c->get_config_local_suffix; # defaultはlocal
my @extensions = @{ Config::Any->extensions };
    
my @files;
if ($extension) { # $c->config->{file}をdirectory指定した場合はここはfalse
    next unless grep { $_ eq $extension } @extensions;
    push @files, $path, "${path}_${suffix}";
} else { # こっちが実行される
    @files = map { ( "$path.$_", "${path}_${suffix}.$_" ) } @extensions;
}

@files;

コメントを敢えて振りました。
設定ファイルとしてこの場合認められるのは、

  • conf/myapp.*
  • conf/myapp_suffix.*

となります。*はConfig::Anyでサポートしている拡張子全てです。

このsuffixを切り替えると上書き用の設定ファイルを切り替える事が出来るので、

$ evn MYAPP_CONFIG_LOCAL_SUFFIX=service ./script/myapp_server.pl -d -r

などとやると、設定ファイルを切り替える事が出来ます。
これでだいぶフレキシブルな開発が出来ますね。