YAML Ain't Markup Language だそうです。ain't ってなんのことか知らなかったんですが、 am not とか isn't とか hasn't とか haven't とかのことらしい。
一般的な型について、どのように記述するか、というのが決まっている。Yaml Cookbook に詳しい。英語なんて読まなくても、ソースを見ればわかる。
基本的に接頭語とインデントで表現。
Ruby 1.8 から YAML を利用するためのライブラリが標準添付なので、何も考えず、 require 'yaml' か、 require 'yaml/store' をするだけです。
YAML を Ruby で美味しく使うためのリファレンスは参考文献のyaml4r のページでダウンロードできます。chm とかもあります。
Ruby の(ほとんどの)オブジェクトは、YAML文字列 へ簡単に変換することができます。
require 'yaml' # 必要 obj.to_yaml # YAML へ変換
例としてはこんな感じ。
require 'yaml'
ary = [1,[2,[3]],4]
map = {'a' => 'AAA', 'b' => 'BBB', 'c' => 'CCC',}
class Video
attr_accessor :title, :year, :rating
def initialize( t, y, r )
@title = t
@year = y
@rating = r
end
end
puts ary.to_yaml
puts map.to_yaml
puts 'string desu-'.to_yaml
puts true.to_yaml
puts(/abc/.to_yaml)
puts Object.new.to_yaml
puts 12345678901234567890.to_yaml
puts 3.14159.to_yaml
puts Time.now.to_yaml
puts "abc\ndef\nghi".to_yaml
puts :sym.to_yaml
puts Video.new('a','b','c').to_yaml
#=>
---
- 1
-
- 2
-
- 3
- 4
---
a: AAA
b: BBB
c: CCC
--- "string desu-"
--- true
--- !ruby/regexp "/abc/"
--- !ruby/object:Object {}
--- 12345678901234567890
--- 3.14159
--- 2003-08-12 01:12:48.875000 +09:00
--- >-
abc
def
ghi
--- !ruby/sym sym
--- !ruby/object:Video
rating: c
title: a
year: b
また、YAML::load によって、to_yaml した結果の文字列を Ruby オブジェクトに復元することができます。
obj = ... decoded_obj = YAML::load(obj.to_yaml)
Marshal は Ruby のオブジェクトをある一定の方式でバイナリへ変換したり、それを復元したりするためのものです。YAML は ruby のオブジェクトを自由に表現できるらしいので、Marshal でやっていたことを YAML でできる、ということです。
次のような対応関係があります。
obj = ... # 略 bin = Marshal.dump(obj) # オブジェクトからバイナリへ loaded_obj = Marshal.load(bin) # バイナリからオブジェクトへデコード require 'yaml' yml = obj.to_yaml # オブジェクトからYAML へ loaded_obj = YAML::load(yml) # YAML からオブジェクトへデコード
もちろん、Marshal では表現できない入出力オブジェクトや Proc オブジェクトなどは YAML でも表現できません。
PStore とは、Marshal を用いたオブジェクトデータベースだそうです。
YAML は Marshal の機能を置き換えることができるため、PStore も YAML で実現できるはず、ということで実装したのが YAML::Store だそうです。
既にある PStore を利用したプログラムに、require 'yaml/store' を加え、PStore という文字列を YAML::Store という文字列に置換すれば大丈夫です。
require 'yaml/store'
db = YAML::Store.new('yaml_db')
db.transaction{
db['data'] = obj
val1 = db['val1']
}
(PStore を知っていれば)何も考えることはありませんね。
ごめん、よく読んでない。けど、DOM で XPath みたいなのを狙ってるらしい。
気になる(俺だけかもしれない)性能についてみてみました。
某投票システムでは、次のようになりました。
# Marshaled
# binary 104 バイト
# YAML テキストファイル 156 バイト
---
data: !ruby/object:Vote::VoteItem
cnt:
- 3
- 0
- 4
sel:
- "です。"
- "ノー。"
- "どちらとも言えない。"
title: "幸せ?"
まぁ、やっぱりちょっと大きいです。可読性のために冗長性を取る、最近の流行ですね。
Marshal で dump するのと、to_yaml するのの性能比を測ってみます。
## to_yaml vs marshal about
## [1,2,3,{:a => "A", :b => "B", :c => "C"},"string",3.14]
user system total real
to_yaml 4.219000 0.078000 4.297000 ( 4.453000)
marshal 0.032000 0.000000 0.032000 ( 0.031000)
2桁くらい違います。
では、PStore と YAML::Store の速度性能差を出して見ます。
読み込み。
## read
user system total real
yaml::store 8.078000 1.812000 9.890000 ( 10.375000)
PStore 0.750000 0.516000 1.266000 ( 1.375000)
書き込み。
=========================================================
## write
user system total real
yaml::store 7.890000 2.000000 9.890000 ( 10.141000)
PStore 0.703000 0.531000 1.234000 ( 1.375000)
一桁ほどの性能差です。ファイルシステムへの読み書きのオーバーヘッドで、to_yaml の速度差が緩和されたんですかね。
まぁ、この結果から、大規模でクリティカルな用途では、あんまり利用したくないことがわかります(しねーよ)。
まぁ、ちょこっと使う分には、あとで手を入れやすいしいいんじゃないですかね。