YAML生成を工夫してembulkの設定ファイルを使い易く

embulk meetupの後にsonots先輩に何か書いてくださいよーって言われたので、
embulkに限った話ではないのですが、関連する話題なのでEmbulk Advent Calendarに書かせてもらおうと思います。

embulkは設定ファイルがYAMLです。
現時点ではテンプレート機能はまだ限定的な機能しかなくて、それ程表現力がありません。
それならもう、YAMLの生成を別に任せようと思って簡単なgemを作りました。

[joker1007/yaml_master](https://github.com/joker1007/yaml_master "joker1007/yaml_master")

YAMLの設定ファイルの大本になるマスターファイルを作っておいて、そこから特定の範囲だけを別のYAMLとして出力するだけのgemです。
その際にerbでRubyを埋め込めるようにしているので、Rubyで表現できることは何でもできます。
環境変数から日付を取ってそれをRangeにしてループしたり、ActiveRecordからSQL作ったりとか。

まあ、そもそものモチベーションとしては、RailsアプリにもDBの認証情報とかAPIトークンをyamlで書いていて、その他のSQLバッチを実行するフレームワークにも同じこと書いていて、更にembulkの設定も別ファイルで書いていて、という状態が嫌だったから作ったものです。
アプリ毎に微妙にキー名違ってて互換性無いし……。
なので、こっちで一回ラップして、そっから吐くという形にしたものです。

簡単な例だと以下の様な`master.yml`を書いておきます。

```yaml
yaml_master:
  database_yml: <%= ENV["CONFIG_DIR"] %>/database.yml
  embulk_yml: <%= ENV["CONFIG_DIR"] %>/embulk.yml

database_config: &database_config
  development: &database_development
    adapter: mysql2
    encoding: utf8
    database: development
    pool: 5
    host: &database_development_host
    username: &database_development_username root
    password: &database_development_password
    socket: /tmp/mysql.sock

  test: &database_test
    adapter: mysql2
    encoding: utf8
    database: test
    host: &database_test_host
    username: &database_test_username root
    password: &database_test_password

  production: &database_production
    adapter: mysql2
    encoding: utf8
    database: production
    pool: 5
    host: &database_production_host "192.168.1.100"
    username: &database_production_username root
    password: &database_production_password
    socket: /tmp/mysql.sock


data:
  database_yml:
    <<: *database_config

  embulk_yml:
    in:
      type: file
      path_prefix: example.csv
      parser:
        type: csv
        skip_header_lines: 1
        columns:
          - {name: key_name, type: string}
          - {name: day, type: timestamp, format: '%Y-%m-%d'}
          - {name: new_clients, type: long}

    out:
      type: mysql
      host: *database_<%= ENV["RAILS_ENV"] %>_host
      user: *database_<%= ENV["RAILS_ENV"] %>_username
      password: *database_<%= ENV["RAILS_ENV"] %>_password
      database: my_database
      table: my_table
      mode: insert
```

その後、以下のようにコマンドを打ちます。

```sh
$ RAILS_ENV=production CONFIG_DIR="config" yaml_master -m master.yml --all
```

こうすると、`yaml_master`というブロックにあるキー名と一致するyamlの中身を`data`ブロックから引っ張ってきて、それをvalueをファイル名として出力します。
上記の例だと`config/database.yml`と`config/embulk.yml`が出力されます。
その際、まずERBを評価してYAMLにした後、YAMLのエイリアス機能を使って記述内容を再利用します。

また、特定の設定だけをファイル名をして出力することもできます。

```sh
$ RAILS_ENV=production CONFIG_DIR="config" yaml_master -m master.yml -k embulk_yml -o embulk_config.yml 
# -o オプションが無い場合は標準出力
```

### その他の機能
#### 組み込みのヘルパーメソッド
その他の機能としては、ERBで使える組み込みのヘルパーメソッドがあって、masterファイルのフルパスが取れたりHOMEディレクトリのパスが取れたり、ファイルが存在する時だけ中身を読み取って文字列を出力する、といった処理が書けます。
詳しくはテストコードを見てください。

#### 実行時変数として使えるプロパティを指定する
```sh
$ RAILS_ENV=production CONFIG_DIR="config" yaml_master -m master.yml -k embulk_yml -p "foo=bar,hoge=fuga"
```

という感じで`-p`もしくは`--properties`オプションでプロパティを渡せます。
このプロパティはERB内で`properties["foo"]`という形で参照できます。

#### 別のyamlファイルをincludeして読み込む
一つのマスターファイルに何でも書くと、とても長くなってしまいそうなので、別のyamlファイルをincludeして埋め込めるようにしました。
ただ、現時点ではYAMLのエイリアス機能が共通化できてないので、イマイチな感じです。

これぐらいの表現力があれば、cronなりその他のワークフローエンジンに組み込む際にも色々と工夫できるかと思います。