Configプラグインで使う実用的な外部設定ファイルの一例

# Configプラグインで使う実用的な外部設定ファイルの一例

この前[Liteでないアプリの実用的なディレクトリ構造一例](http://qiita.com/azumakuniyuki/items/49106ec3891af32af88d)を書いた[@azumakuniyuki](https://twitter.com/azumakuniyuki)です。

Advent Calendarの例示用にリポジトリを作ったので、もう少しネタが絞り出せそうな気がするので、構築するWebアプリケーションで外部設定ファイルを使えるようにする話を書きます。

## Mojolicious::Plugin::Config

Mojoliciousをインストールすると、それに含まれる[Mojolicious::Plugin::Config](https://metacpan.org/pod/Mojolicious::Plugin::Config)を使うと簡単に外部設定ファイルを使う事が出来ます。勿論、プラグインを使わず自前で読込むコードを書いても外部設定ファイルは使えますが、Mojoliciousに標準で含まれているので、このプラグインを使う方が便利で合理的です。

## 設定ファイルを準備する

12月6日に書いた[ネコ管理アプリケーション](http://qiita.com/azumakuniyuki/items/49106ec3891af32af88d)の話の続きで、設定ファイルを追加する事にします。万が一手元で動かしてみたいとか、この記事に書いてる内容がほんまに動くのか確認したいって方は適当なディレクトリで `git clone` して `morbo script/noraneko_web` して下さい。

### サンプルのソースを動かす
```
% git clone https://github.com/azumakuniyuki/Noraneko-Mojo-Sample.git
Cloning into 'Noraneko-Mojo-Sample'...
remote: Counting objects: 69, done.
remote: Compressing objects: 100% (54/54), done.
remote: Total 69 (delta 2), reused 69 (delta 2)
Unpacking objects: 100% (69/69), done.
Checking connectivity... done

% cd ./Noraneko-Mojo-Sample
% morbo script/norabeko_web
Server available at http://127.0.0.1:3000.
```

### 設定ファイルのディレクトリと空ファイル作成

以降、リポジトリの中に居るという前提で話が進みます。

```
% ls .
README.md
etc/
lib/
log/
public/
script/
t/
templates/

% mkdir ./etc
% touch ./etc/neko.conf
% touch ./etc/auth.conf

adminがデプロイするユーザ、appserverがサーバを動かすユーザ
% chown admin:appserver ./etc/auth.conf
% chmod o-rwx ./etc/auth.conf
```

設定ファイルを入れるディレクトリは`etc`でも`conf`でも何でも良いですが、僕はいつも`etc`にしています。また設定ファイルの名前も何でも良いですし、拡張子も何でも良いです。

## 設定ファイルの内容
設定ファイルは **Perlのハッシュリファレンス** で記述します。書く内容は滅多に変化しないであろう値や固定値を入れておくと良さそうです。

```perl:etc/neko.conf
{
    # サイト全体の設定
    site => {
        maintenance => 0,   # 1にするとメンテモードに移行するとか?
        copyright => 'Copyright (C) 京都市役所ネコ課',
        ja  => {
            name => 'ネコ管理システム',
        },
        en => {
            name => 'Stray Cats Administration System',
        },
    },
}
```

データベース接続情報とか認証関係の設定値はパーミッションを厳しくした別のファイルに書きます。

```perl:etc/auth.conf
{
    # データベースに関する設定
    'database' => {
        'dbtype' => 'PostgreSQL',
        'dbname' => 'nekochan',
        'hostname' => '192.0.2.22',
        'port' => 5432,
        'username' => 'dbadmin',
        'passowrd' => 'nyanko',
    },
}
```

この記事ではDB接続情報や外部サービスとの連携APIキー等、秘匿すべき情報はソースコードから分離して設定ファイルに入れる、またはそういった認証情報のみを保存した設定ファイルをもう一個作って、ファイルのパーミッションで制限する事にしています。


## Noraneko::Webで設定ファイルを読込む

```perl:lib/Noraneko/Web.pm
package Noraneko::Web;
use Mojo::Base 'Mojolicious';
use Path::Class;

sub startup {
    my $self = shift;
    my $home = new Path::Class::File(__FILE__);
    my $root = $home->dir->resolve->absolute->parent->parent();

    for my $e ( 'neko', 'auth' ) {
        my $f = $root->stringify.'/etc/'.$e.'.conf';
        $self->plugin( 'Config', { 'file' => $f } );
        # 適当な変数に受けとってもよい
        # $conf = $self->plugin( 'Config', { 'file' => $f } );
    }

    my $r = $self->routes;
    # この前書いたルーティングの設定とかいろいろ続く...

```

作った設定ファイル`etc/neko.conf`と`etc/auth.conf`を`Config`プラグインのメソッドで読込んでいます。読込んだ設定ファイルの内容は`$self->defaults->{'config'}`でアクセス出来ます。

```perl
warn Dumper $self->defaults->{'config'};

$VAR1 = {
  'database' => {
    'dbtype' => 'PostgreSQL',
    'hostname' => '192.0.2.22',
    'port' => 5432,
    'passowrd' => 'nyanko',
    'username' => 'dbadmin',
    'dbname' => 'nekochan'
  },
  'site' => {
    'maintenance' => 0,
    'en' => {
      'name' => 'Stray Cats Administration System',
    },
    'ja' => {
      'name' => "\x{30cd}\x{30b3}\x{7ba1}\x{7406}\x{30b7}\x{30b9}\x{30c6}\x{30e0}"
    },
    'copyright' => "Copyright (C) \x{4eac}\x{90fd}\x{5e02}\x{5f79}\x{6240}\x{30cd}\x{30b3}\x{8ab2}"
  }
};
```

## コントローラで設定値を使う

ここで例示しているのは、言語別にサイト名を定義している部分とDBへの接続情報です。

サイト名なんかは設定ファイルに書かず、直接テンプレートファイルや`templates/layout`にある(というか自分で作る)レイアウトに埋め込んでしまえば良いのですが、例えばテンプレートに出さない処理結果にサイト名を入れたい(外部サービスに送るデータにサイト名を入れるとか)用途があるなら、コントローラ側でサイト名が参照出来る方が好都合です。

```perl:lib/Noraneko/Web/User/Root.pm
sub index {
    my $self = shift;
    my $lang = $self->stash('lang');

    $self->stash( 'v' => $self->app->config->{'site'}->{ $lang } );
    return $self->render( 'template' => $lang.'/user/root/index' );
}
```

テンプレートに渡す変数を`v`という名前で`stash`に入れています。ここで`v`に入れた内容はテンプレート側で`$v`として参照出来ますので、例えば以下で例示するテンプレートファイルでは`ページ名 | サイト名`のような形でHTMLの`title`要素を作っています。

```perl:templates/ja/user/root/index.html.ep
% layout 'ja/user/default';
% title '市民のページ | '.$v->{'name'};
<h1>ニャー?</h1>
```

レイアウトについては本Advent Calendarの12月4日の記事、[Mojoliciousのテンプレートでレイアウトを自在に操る](http://qiita.com/yusukebe/items/b1dffe22bd870b128aa9)に詳しく載っています。

## github

一応、ちゃんと動くか心配なのでここで説明している箇所だけ実装したネコの管理アプリを更新してgithubに置いてきました。

[github.com/azumakuniyuki/Noraneko-Mojo-Sample](https://github.com/azumakuniyuki/Noraneko-Mojo-Sample)

## まとめ

滅多に変更しないような固定値は外部設定ファイルに分離しておく方が管理がしやすいと思います。DBに設定テーブルを作るのもありやと思いますが、サーバが一台だけの小規模な構成であるなら、複数のサーバ間で設定値を共有する事も特に考えなくて良いですし設定ファイルを設置するという手段も充分有効です。

また、サーバを触るエンジニアだけがDB接続情報を参照出来るようにする、という運用を行う場合は、設定ファイルを複数用意してUNIXのユーザでのパーミッションを設定するという古典的な手法も充分有効です。

設定ファイルに分離する利点としては、lib/以下のソースコードに直接値を埋め込まないので、例えばデザイナーさんとかサイト管理者がソースコードを触らずともある程度の設定変更が出来るという点でしょうか。

この記事で作る事にしているシステムでは、例えばネコが欲しい人向けに管理しているネコの写真を表示するページがあったとして、一ページに表示するネコの数を設定ファイルで定義しておけばページ上のネコ表示数増減も容易です。

## 参考

- [Mojolicious::Plugin::Config](https://github.com/yuki-kimoto/mojolicious-guides-japanese/wiki/Mojolicious%3A%3APlugin%3A%3AConfig)
- [Mojoliciousのテンプレートでレイアウトを自在に操る](http://qiita.com/yusukebe/items/b1dffe22bd870b128aa9)
- 今日は参考ネコ画像はありません