yaml_db で生成したYAMLファイルの日本語が文字化けする

本番環境のPostgreSQLに格納されたデータを開発環境のSQLite3に持ってくるに書いた方法で、本番環境のPostgreSQLを開発環境のSQLite3へ持ってきたいのだけど、うまくいかなくなった。

症状としてはyaml_db で生成したYAMLファイルの日本語が文字化けしてしまう。見えないチカラ:Ruby1.9.2で日本語を含むYAMLを出力する時、バイナリ文字列化されないようにするによると、Ruby 1.9.3からYAMLのパーサーが標準ライブラリーとなっているとのこと。で、旧YAMLパーサーのsyckの場合、日本語をバイナリ文字列で扱っているのだけど、標準ライブラリのYAMLパーサーpsychは日本語をそのままYAMLに書き出すらしい。

本番環境が ruby 1.9.3p0で、開発環境が1.9.3p194となっている。たぶん、この結果、本番環境でyaml_dbでダンプしたときはsyckが使われており、開発環境でyaml_dbでロードするときはpsychが使われているっぽい。この結果として文字化けが発生している様子。実際、yaml_dbでダンプしたdata.ymlを以下のスクリプトで読み込み、日本語を確認したところ本番環境でも日本語が表示された。

require 'rubygems'
require 'syck' #これを削ると psychが呼び出される
require 'yaml'

File.open("db/data.yml") do | io |
  YAML.load_documents(io) do |y|
    puts Marshal.dump(y)
  end
end

yaml_dbのソースコードを見るかぎり、本番環境のrubyを最新にすれば両方共psychが使われて問題なくなりそうではあるが、本番環境のアップグレードをいますぐはできないので、とりあえずメモ。

とりあえずの対処法

YAML::ENGINE.yamlerという変数によって、YAMLパーサーをソースコード中で切り替えられるようなのでこれを切り替えることで対応した。具体的には、syck形式で出力されたと思しきダンプファイルがdb/data.ymlにあるとき、以下のスクリプトでpsych形式に置き換えた。

require 'rubygems'
require 'yaml'

buffAry = Array.new

YAML::ENGINE.yamler= 'syck'
File.open("db/data.yml") do | io |
  YAML.load_documents(io) do |y|
    buffAry.push(y)
  end
end

puts "start output\n"

YAML::ENGINE.yamler= 'psych'
File.open("output.yml", "w") do |file|
  buffAry.each do | y |
    file.puts y.to_yaml
  end
end

puts "End\n"

そして、こいつをdb/data.ymlとして、db:data:loadした。

% mv db/data.yml db/syck.data.yml
% mv output.yml db/psych.data.yml
% cp db/psych.data.yml db/data.yml
% rake db:data:load

これで文字化けせずにデータを移すことができた。