Ruby
erb
Sinatra
YAML

Sinatra::ConfigFileでYAMLの中でERBの記法を使いたい

More than 3 years have passed since last update.

概要

問題

config.yml内にerb記法を書いたが、そのままの文字列で返ってきて困った。

FOO=hello
config.yml
foo: <%= ENV['FOO'] %>
settings.foo # => '<%= ENV['FOO'] %>'

解決策

読み込むファイルの名称に.erbを含める。

FOO=hello
config.yml.erb
foo: <%= ENV['FOO'] %>
settings.foo # => 'hello'

参考資料

Sinatra::ConfigFile自体の使い方は下記。
http://www.sinatrarb.com/contrib/config_file.html

ソースのコメントのほうがより詳細かもしれない。
https://github.com/sinatra/sinatra-contrib/blob/master/lib/sinatra/config_file.rb

詳細

以下自分が調べた際の覚書なので、長々書いたけど結論はもう上に書いたので充分

Herokuとか使ってるとmongoidのmonogoid.ymlとかでデータベース接続の情報を環境変数に設定するので<%= ENV['MONGOHQ_URL'] %>とか書く。
似たようなことをSinatra::ConfigFileでそのままやってみると下記のようになる。

config.yml
development:
  foo: <%= ENV['FOO'] %>

と、なるのだが、実際settings.fooとした際に帰ってくる値はまんま文字列として<%= ENV['FOO'] %>が返ってくる。

いやいや、そうじゃなく、展開した値を返して欲しいのさ、という場合は下記のようにする。

config.yml.erb
development:
  foo: <%= ENV['FOO'] %>

一緒に見えるけど、実はファイル名に.erbを足している。
ソースよく見りゃ書いてあるんだけど、config_fileメソッドの中で下記のようにしている。

document = IO.read(file)
document = ERB.new(document).result if file.split('.').include?('erb')
yaml = config_for_env(YAML.load(document)) || {}

つまりファイル名を.で分割した配列(今回なら['config', 'yml', 'erb'])中に'erb'があればファイルの内容をERBで処理するけど、含まないならERBで処理しない。

仮にERBの記法で書いてもそのままYAML.loadされるので展開されることなくそのまま文字列として返ってくる。
そりゃまあ、YAMLの記法ではないのだから当たり前といえば当たり前で、Railsとかmongoidがある意味気を利かせてYAMLファイル内にerbが使えるようになっている。

たとえばmongoidのload!メソッドでは下記のように。

YAML.load(ERB.new(File.new(path).read).result)[env]

なんでSinatra::ConfigFileじゃ上記のようにしていないのか、というのは下記のPull Requestsがまさにのもの。
https://github.com/sinatra/sinatra-contrib/pull/54

英語よくわかんないけど、ファイル名はconfig.erb.ymlよりもconfig.yml.erbがいいよね、というようなことが会話されている。
また、当初は上記のmongoidの例のように問答無用でERB噛ませようとしたみたいだけど、無駄にERBで処理しないほうがよいよね、的なことも会話されてる。

けども下記のIssuesもあったりする。
https://github.com/sinatra/sinatra-contrib/issues/116