Rubyによる内部DSLを用いた設定ファイルの作り方

2014.09.25.木
技術

takedaです。前回に引き続き今回もRubyです。Rubyを使い始めて3ヶ月ほどになりますが、Rubyとの接し方にも慣れてきました。
今回は、内部DSLを用いた、設定ファイルの読み込みについて記述します。

端的にまとめると、Rubyのプログラムを設定ファイルのように扱おうということです。

Ruby Logo

内部DSLとは

DSLとはドメイン特化言語と呼ばれるものです。ある事柄に対して、特化しているプログラミング言語のようなものです。
MySQLなどの設定ファイルも、設定項目に対して、代入を行うだけのプログラミング言語とみなすことができるかと思います。

内部というのは、Rubyという言語の内部で特化された言語ということです。
外部DSLは文法などが一から定義されるのに対して、内部DSLでは、Rubyの文法に則って記述します。

何ができるのか

通常のYAMLやJSONなどの設定ファイルの記述と違い何ができるのかを、以下に挙げます。
  • 関数を設定できる
  • 読み込み時にイベントを起こせる(検査など)
  • Rubyで許される記述方法がすべて許される
  • 間違った設定項目があった場合にエラーが出る(missing_methodでエラーを拾えば自由に対処できる)

実際に作ってみる

以下のようなコードを設定ファイルとします。

以下のように読み取り用のクラスを定義します。

以下は実際に設定ファイルから値を読み取るためのコードです。

このように、とても簡単に値を取得できます。値の代入がメソッドの呼び出しなので、値の加工やバリデーションなども分かりやすいです。

instance_evalとは何なのか

instance_evalは与えたコードの示すselfを、呼び出したインスタンスとして扱うことができるメソッドです。

簡単に言うと以下と同じようなことをしています。

selfというのはmethod定義時には省略することができますが、実際は「self.foo 14」のような呼び出しとなり、自身のメソッドが呼ばれます。
なので、self部分をinstance_evalによりインスタンスを束縛することで、そのインスタンスのメソッドで実行したこととなります。

代入にはイコールを使いたい

設定ファイルなので代入はイコールを使いたいと誰もが思うかと思います。
しかし以下の様な設定ファイルは作る事ができません。

前回の開発ブログでも出てきましたが、Rubyでは「def foo=( val )」というような定義により、「reader.foo = 14」で実行できるメソッドが定義できます。

しかし、このメソッドは、同じクラスのメソッド内では呼び出すことができません。
以下のように定義して「execute_code」を呼び出した場合は、fooというローカル変数への代入とみなされます。

なのでメソッドの代入を呼び出したいのであれば、以下のように記述することで実現できます。

もしくは、一度メソッドのブロック構文で括り、引数として渡されたオブジェクトに対して、代入を行うこともできます。

クラス側は以下のようになります。

このように代入文を使用したい場合は、ローカル変数への代入にならないように、
何らかのオブジェクトを示すことで実現できます。

気をつけるべきこと

Rubyの言語として動作しているので、悪意のあるプログラムを埋め込むこともできてしまいます。なので、使用できる場面としては、設定ファイルの内容に責任をもてるような場面に限られるでしょう。

最後に

このように、Rubyをそのまま設定ファイルとすることで、記述する方法が自由に決められます。

VagrantやChefなどのソフトの設定ファイルもRubyによって記述されます。機会があれば使って、参考にしてみてください。

今回もRubyでした。次回もRubyの予定です。近日中には小ネタで投稿すると思います。