RubyでのYAMLの基本的な扱い方

/

導入

Rubyでプログラムを書く際にクラスなどを使いプログラムを機能毎に小分けにしておくと後々使い回しが効ききやすく便利ですが、プログラムの対象や細かい挙動の変更を指定する設定ファイルも他とは別にしておきたい時があります。
Rubyのプログラムの一部として分けるのも不可能ではないですが、プログラムの実行毎にコードを直接弄るのは好ましくないですしこまごました変更をする際にも便利なのでYAMLを試してみようと思いRubyでの基本的な使い方を調べてみました。

実行環境

Ruby 2.3.1

YAML

YAML(YAML Ain’t Markup Language)は構造化データを表現するためのフォーマットの一種です。
インデントを使用したシンプルで人間にとって扱いやすい文法を持っているためアプリケーションの設定ファイルに使われたり、AnsibleのPlayBookにも採用されています。

拡張子は.yamlもしくは.ymlで表します。
yaml.org

配列

YAMLで配列を表す場合には[ハイフン][半角スペース]を使用します。

- list A
- list B
- list C

ハッシュ

ハッシュは[Key][:][半角スペース][値]で表します。

Name: 7-11
Category: Convenience Store
Area: Katsushika

ネスト

配列とハッシュは半角スペースを使うことで、それぞれネストさせる事が出来ます。

- list A
- 
 - list B 1
 - list B 2
   - list B 3-1
   - list B 3-2
- list C
Hash1: aaa
Hash2:
 Hash2-1: bbb
 Hash2-2: ccc

また配列とハッシュを組み合わせる事も出来ます。

- Hash1-1: aaa
  Hash1-2: bbb
- Hash2-1: ccc
  Hash2-2: ddd

コメントアウト

YAMLのコメントアウトには#を使用します。
また範囲コメントは用意されていません。

セパレータ

YAMLでは3つの「-」で区切る事で一つのファイルに複数のYAMLドキュメントを書くことが出来ます。
また3つの「.」でドキュメントの終了を表します。

---
- List 1
- List 2
---
- List A
- List B
---
Hash1: aaa
Hash2: bbb
...

Ruby

RubyにはYAMLのためのライブラリが用意されています。

Library yaml

Rubyでyamlを扱うためのライブラリです、YAML moduleなどを含んでいます。
Library yaml/Ruby 2.3.0 リファレンスマニュアル
YAML module//Ruby 2.3.0 リファレンスマニュアル

- text
- 1234
- true
# coding:utf-8

# YAML moduleの読み込み
require 'yaml'

path = './file.yml'
# 外部のYAMLファイルを読み込んでRubnのオブジェクトに変換。
yaml = YAML.load_file(path)
puts yaml
$ ruby yaml.rb

text
1234
true

データ型

YAMLは文字列や真偽値などいくつかのデータ型を扱いますがRubyではYAML moduleによってそれぞれタイプが自動的に認識されます。

yaml.each do |line|
  puts line.class
end
String
Fixnum
TrueClass

load_stream()

一つのファイル内でセパレータによって区切られた複数のYAMLドキュメントを読み込む際にはload_stream()メソッドを使います。

---
- List 1
- List 2
---
- List A
- List B
---
Hash 1: aaa
Hash 2: bbb
...
# coding:utf-8
require 'yaml'

path = './file.yml'
file = File.open(path)
yaml = YAML.load_stream(file)
yaml.each do |doc|
  puts doc
end

外部ファイルから複数のYAMLドキュメントを直接読みんでRubyオブジェクトに変換してくれるメソッドは用意されていないようなので、ファイルを読み込んでからオブジェクトをload_stream()に渡しています。

$ ruby yaml.rb

["List 1", "List 2"]
["List A", "List B"]
{"Hash 1"=>"aaa", "Hash 2"=>"bbb"}

Rubyのプログラムを実行してみるとセパレータで区切られた全てのYAMLドキュメントが読み込まれているのが解ります。

load()メソッドやload_file()メソッドの場合だと一つのドキュメントしか扱わないため、区切られた以後の内容は読み込まれないのでセパレーターを使ったYAMLファイルを扱う場合には注意が必要です。

# coding:utf-8
require 'yaml'

path = './file.yml'
file = File.open(path)
yaml = YAML.load(file)
yaml.each do |doc|
  puts doc
end
$ ruby yaml.rb

# 最初のドキュメントのみ出力
List 1
List 2

サンプル

実際の使い方を想定して、というにはしょぼいですがサンプルのコードを書いてみました。

---
number:
 int1: 10
 int2: 20
calc: add
...
# coding:utf-8
require 'yaml'

path = './file.yml'
conf = YAML.load_file(path)

if conf['calc'] == "add"
  puts conf['number']['int1'] + conf['number']['int2']
elsif conf['calc'] == "multi"
  puts conf['number']['int1'] * conf['number']['int2']
end
$ ruby app.rb
30

YAMLに書き込まれた値を元にしてRubyのプログラムが実行されています。
YAMLの値を変更すればそれに伴ってRubyが扱う数値や挙動が変更されます。

---
number:
 int1: 10
 int2: 40
calc: multi
...
$ ruby app.rb
400

この例は非常に簡単ですが色々なアプリケーションを見ても基本的な部分は変わらず、YAMLを設定ファイルとして扱いハッシュで解りやすい名前着けてその値を読み込んでプログラムを実行するというのが多いようです。

あとがき

クラスなどでRubyのプログラムを細かく分けてみても状況によって細かい変更は必要なので設定ファイルとしてYAMLを試してみました。
YAMLは文法が簡単でわずらわしくないので最初に手を出すのに良さそうだと思います。