読者です 読者をやめる 読者になる 読者になる

日常

ケ・セラ・セラ

YAMLファイルに別のYAMLファイルの値を参照、設定したい

ruby

こういうことがしたかったのですが、

# a.yml
id: 1
type: foo
description: baz

# b.yml
id: 2
type: bar
description: (ここで a.yml の description の値を参照したい)

こんな風にしました。

YAML.add_domain_type(nil, "include") do |type, val|
  YAML.load_file("path/to/a.yml")[val]
end

# b.yml
id: 2
type: bar
description: !include "description"

最初は b.yml.erb みたいにすればまあ出来るかなと、こんな感じのことを考えましたが、後で知った上の方法に変更したのでした。

# b.yml.erb
id: 2
type: bar
description: <%= YAML.load_file(path)["description"] %>

2016/08/05 追記

深い階層の場合にも対応しようと思い、こうしました。

# a.yml
id: 1
type: foo
description: baz
  too:
    deep:
      key: !include "深い階層にも対応"

# b.yml
id: 2
type: bar
description: (ここで a.yml の description の値を参照したい)
  too:
    deep:
      key: !include "too deep key"

# 今回は space 区切りを想定した。 うっかり space が多く含まれてしまった場合にも対応している。
      key: !include "  too                        deep           key     "

# YAML
YAML.add_domain_type(nil, "include") do |type, val|
  y = YAML.load_file("path/to/a.yml")["some_key"]
  val.to_s.strip.split(/\s+/).each do |key|
    y = y[key] unless y.nil?
  end
  y
end

# ruby 2.3 の場合は Hash#dig でこう書ける
YAML.add_domain_type(nil, "include") do |type, val|
  y = YAML.load_file("path/to/a.yml")["some_key"]
  y.dig(*(val.to_s.strip.split(/\s+/)))
end