RON(Ruby Object Notation)

YAML と同じような軽量マークアップ言語に JSON(JavaScript Object Notation)があります。こちらは、YAMLのように人間に読みやすいデータ記述をねらったと言うよりは、複数のプログラム言語間で構造化したデータをやりとりするときのデータ交換言語をめざしているようです。ところが、この仕様書をみると分かるように Ruby でオブジェクトを記述するときのやり方とほとんど同じです。ただ一ヶ所ハッシュのペアを表すのに Ruby で => が使われているところが : になっているだけなのです。実際、RubyForgeにはたった一行のjason parser が紹介されています。

null=nil;jsonobj=eval(json.gsub(/(["'])\s*:\s*(["'0-9tfn\[{](/){"#{S1}=>#{S2}"})

また、JSON では YAML の場合とは違ってインデントに意味を持たせてはいませんが、JSON Exampleではインデントを活用することで XML よりはるかに可読性が良くなることを示してあります。

Ruby 用の JSON のライブラリの開発も始まっているようですし、JSONを使えば問題はないようにも思えますが、JSON もまた、多バイト文字のエンコーディングは UTF で、EUC が使えないようなのです。

そこで、Ruby のオブジェクトの記述を、インデントして可読性を高めて印字するプログラムを作ってみました。Object メソッドのインスタンス・メソッドとして定義したので、obj.to_ron とすると打ち出してくれます。Rubyによるオブジェクトの記述をJSONをもじって、RON(Ruby Object Notation)と勝手に呼ぶことにしました。オブジェクトから RON を出力するプログラム ron.rb は次のようになります。ライブラリなので、require 'ron' で使います。

class Object
  def to_ron
    str = self.inspect
    lines = str.gsub(/\{/, "{\n").gsub(/\[/, "[\n").gsub(/\,\s*/, ",\n").to_a
    indent = 0
    lines.collect! do |line|
      line.sub!(/^/, " " * indent)
      case line
      when /[\[\{]$/
        indent += 4
      else
        if line =~ /([\]\}]+).*$/
          indent -= 4 * $1.size
        end
      end
      line
    end
    lines.join
  end
end

irb で試した結果を次に示します。inspect の結果とくらべても、随分可読性が上がったようです。

$ irb
irb(main):001:0> require 'ron'
=> true
irb(main):002:0> a = {"apple"=>"red", "orange"=>"orange", "vegitables"=>
irb(main):003:1* ["onion", "cabbage", "raddish"], "lemon"=>"yellow"}
=> {"vegitables"=>["onion", "cabbage", "raddish"], "apple"=>"red", "orange"=>"orange", "lemon"=>"yellow"}
irb(main):004:0> puts a.to_ron
{
    "vegitables"=>[
        "onion",
        "cabbage",
        "raddish"],
    "apple"=>"red",
    "orange"=>"orange",
    "lemon"=>"yellow"}
=> nil

YAML.load に相当するメソッドはありません。a = eval(ron_string) 一発でオブジェクトに変換できるからです。

実行結果からも分かるようにオブジェクトを作成するときと、to_ron の出力では、ハッシュのペアの順番が変ってしまいます。この事情は YAML でも一緒です。定型的なフォーマットで入出力を行うためには出力は puts 関数を使ってペアの順序が変らないようにしたほうが良いようです。

そう考えると YAML や JSON が UTF しかサポートしなくても、少なくとも読みこむときにエンコーディングの変換をせずにそのまま取りこんでくれれば、日本語 EUC を使って障害なく読み書きすることができます。