Dupler: テンプレートエンジンを使って設定ファイルやテストデータを簡単に作成する

みなさん、設定ファイル書いてますか？　私は書いてます。アプリケーションやミドルウェアの設定ファイルから各種クラウドリソースのプロビジョニング用のyml/jsonファイルまで、ITやってると設定ファイル書いて一日終わることもあるのではないでしょうか。というか現代的にはKubernetesですかね。毎日ymlと格闘してないですか？

しかし設定ファイルは環境ごとに大体同じようなものを複数書いたり、あるいは同じ種類（例えばアプリケーションの設定）のものだけどほんの一部だけ違うものがたくさん必要になったり、そういうことありますよね。もちろんAnsibleやらTerraformといったCI/CDツールを導入して自動化する、というのが正攻法ではありますが、プロダクション用途ではないサーバーをとりあえず５台立てて色々検証したいとか、そういう時はとりあえず設定ファイルだけ簡単にテンプレートエンジンで作りたい、とまあ平均的な日本人は考えているに違いありません。

しかし世にテンプレートエンジンは数多あれど、それってライブラリじゃないですか？　つまりわざわざプログラミングしなきゃいけないわけ？　俺は別にプログラミングしたいんじゃない、単に設定ファイルをパッとたくさん作りたいだけなんだ！　という皆さんのために、Dupler、こちら作りました。

https://github.com/daixque/dupler

## これは何？

Duplerはテンプレートエンジンを動かすためのCLIツールです。内部的には[Tilt](https://github.com/rtomayko/tilt)というテンプレートエンジンが動いていますが、基本的にはRubyの[ERB](https://github.com/ruby/erb)のテンプレートを動かすものとして作っています。

単にテンプレートエンジンを使ってテキストファイルを生成するツールなので、もちろん設定ファイルを書くだけではなく、例えばテストデータを簡単に作るとか、その他のどんな用途にも利用可能です。

## 使い方

まずはgemでインストールします。

```sh
$ gem install dupler
```

任意のディレクトリに移動したら、`dupler new`コマンドでduplerのプロジェクトディレクトリの雛形を作成します。

```sh
$ dupler new myproject
$ cd myproject
$ ls           
README.md	templates	values.yaml
```

ここでvalues.yamlファイルは、テンプレートエンジン内で可変になるような値を設定する設定ファイルです。Duplerはこの値をtemplatesフォルダ内のテンプレートにあてて最終的な出力ファイルを作成します。

出力されたサンプルのvalues.yamlを見てみましょう。

```yaml
# Dupler conf file

# Dupler special element is used for configuration of Dupler itself.
# Uncomment below to enable configuration.
# Dupler:
#   time:
#     format:
#       default: "%Y-%m-%d %H:%M"

sample:
  name: Dupler
  records: 5
```

前半のコメントはとりあえず置いておいて、後半を見るとsampleというオブジェクトの中にnameとrecordsという値が設定されていますね。テンプレートからはこれらの値が参照できるようになります。

templates/sample.md.erbを見てみましょう。

```
# Dupler Example

This file is generated by <%= sample.name %> <%= Dupler::VERSION %>.
Generated at: <%= Time.now %>

## List sample

Iteration example with ActiveSupport time expression.

<% sample.records.times do |i| %>
 - <%= i %>, <%= i.days.after.iso8601 %>
<% end %>
```

ERBの記法である`<%=　Rubyの式 %>`を使って色々書いてありますね。また後半では繰り返し処理のサンプル書かれています。とりあえずこの状態でこのテンプレートをレンダリングしてみましょう。

テンプレートのレンダリングには`dupler`コマンドを使います。

```
$ dupler
render: ./output/sample.md
```

これでoutputディレクトリが作成され、その中にsample.mdファイルがレンダリングされました。

```
# Dupler Example

This file is generated by Dupler 0.2.1.
Generated at: 2023-11-03 22:46:19 +0900

## List sample

Iteration example with ActiveSupport time expression.

 - 0, 2023-11-03T22:46:19+09:00
 - 1, 2023-11-04T22:46:19+09:00
 - 2, 2023-11-05T22:46:19+09:00
 - 3, 2023-11-06T22:46:19+09:00
 - 4, 2023-11-07T22:46:19+09:00
```

ちなみにtemplatesフォルダには複数のテンプレートファイルを保存することができ、その場合は全てのテンプレートが自動的に処理されてoutputフォルダにレンダリング結果が保存されます。

## オプション

デフォルトでは設定ファイルはvalues.yaml、出力ディレクトリはoutput、テンプレートファイルはtemplates/*ですが、これらは以下のように変更可能です。

```
$ dupler -c other-values.yaml other-output other-templates/*
```

## ユースケース

見ていただいた通り、Duplerは超シンプルなテンプレートエンジンを使ってテキストファイルを出力するだけのツールです。ただ例えば、以下のような時にすごい便利なのでぜひ使ってみてください。

### 複数環境がある設定ファイルの自動生成

よくある開発環境、テスト環境（SIT/UAT/etc.)、ステージング環境、本番環境のような構成の時、設定ファイルの構造は同じだが接続情報や名前などだけが違う、ということがよくありますよね。こういうパターンの場合は同一のテンプレートに対して、環境に合わせた複数のvaluesファイルを使ってDuplerを使うと便利です。

```
$ dupler -c staging-values.yaml staging_output
```

### テストデータ作成

上記のサンプルのように、ループ処理も普通にできますのでテストデータを簡単に作成する際にも便利です。あとこの場合、デフォルトでRailsでお馴染みのライブラリ[ActiveSupport](https://github.com/rails/rails/tree/main/activesupport)の機能が利用できます。特に日付関連の処理は劇的に便利です。

例えばサンプルを少しだけ変更した以下のようなテンプレートを考えてみます。

```erb
<% 5.times do |i| %>
 - <%= i %>, <%= i.days.after.iso8601 %>, <%= ['a', 'b', 'c'].sample %>
<% end %>
```

こちらを簡単に説明すると、
- 5.timesで5回ループ
- i.days.after.iso8601は現時点からi日後の時刻をiso8601形式で出力
- 配列.sampleは配列の中からランダムに要素を取り出すRubyのメソッド

なので、例えばこのテンプレートは、

```
 - 0, 2023-11-03T23:17:59+09:00, a
 - 1, 2023-11-04T23:17:59+09:00, b
 - 2, 2023-11-05T23:17:59+09:00, c
 - 3, 2023-11-06T23:17:59+09:00, c
 - 4, 2023-11-07T23:17:59+09:00, b
```
こういうふうにレンダリングされます。

あとは少し複雑な使い方として、例えばJSON lineのファイルをレンダリングすることを考えてみましょう。JSON形式で出力する場合、JSONとして正しいフォーマットになっていることが重要です。ここの例でやりたいことは以下です。
- JSON line形式のログファイルを書き出す
- 時刻`@timestamp`は１秒刻み
- `message`はa, b, cからランダムに一つ選ぶ

この実装ではERB内でRubyのHashオブジェクトを作成して、JSONに変換しています。こうすることでJSONとしてのフォーマット間違いを防ぐことができますし、１行で書かなくて良いぶん読み書きも楽です。

```erb
<% sample.records.times do |i| %>
<%= {
  '@timestamp': i.seconds.after.iso8601,
  message: ['a', 'b', 'c'].sample
}.to_json %>
<% end %>
```

このテンプレートの出力結果は以下のようになります。

```json
{"@timestamp":"2023-11-03T23:32:32+09:00","message":"a"}
{"@timestamp":"2023-11-03T23:32:33+09:00","message":"b"}
{"@timestamp":"2023-11-03T23:32:34+09:00","message":"c"}
{"@timestamp":"2023-11-03T23:32:35+09:00","message":"b"}
{"@timestamp":"2023-11-03T23:32:36+09:00","message":"c"}
```

こうなるともはやテンプレートエンジンと呼んでいいのかわからない使い方ですが、便利は便利です。

## まとめ

というわけで、超シンプルなツールの割にすごい便利なので、Dupler使ってみてください。