HikiのRuby-1.9対応をしていて、よくあるはまりどころを見つける良い方法はないかと思い、Ruby-1.8で動かしたまま、互換性の問題のあるメソッドを呼び出す時に警告を出すライブラリを作ってみました。
例えば、「Ruby-1.8では String#to_a は存在しますが、Ruby-1.9ではエラーになる」という風に無くなってしまうものに関しては、ruby -wで実行すれば、
warning: treating String as Enumerable object is deprecated; use String#each_line/lines
と警告を出してくれるのでいいのですが、「Ruby-1.9でも存在するけれど、挙動が違うよ」というケースは警告を出してくれません。 例えば、「String#sizeは、Ruby-1.8ではバイト列の長さを返すが、Ruby-1.9では文字数を返す」みたいなケースです。
というわけで、 compatibility_warning.rb という短いライブラリを作ってみました。 使い方は、動かすプログラムの適当な場所で require "compatibility_warning" と加えるだけで、例えばHikiをRuby-1.8で動かすと、こんなerror_logが得られます。
(compatibility warning) String#to_a used in /hiki_0_8_8/plugin/01sp.rb:37:in `load_file' (compatibility warning) String#size used in /hiki_0_8_8/hiki/pluginutil.rb:50:in `convert_value' (compatibility warning) String#size used in /hiki_0_8_8/plugin/00default.rb:47:in `page_name' (compatibility warning) String#size used in /usr/lib/ruby/1.8/erb.rb:548:in `compile' (compatibility warning) String#size used in /usr/lib/ruby/1.8/erb.rb:585:in `compile' (compatibility warning) String#size used in /hiki_0_8_8/hiki/page.rb:49:in `process'
これを見ながら、String#to_aはstr.split(/^/)に書き換えるとか、String#sizeはString#bytesizeにすべきかそのままでいいか確認するとか、していけるわけです。
いまのところのソースはこんな感じです。 同じ警告を1回しか出さないために、グローバル変数を使っているのがちょっとダサすぎますが。
たぶん、もっとたくさんのメソッドを追加すべきだと思うので、「これも!」というのがあればお知らせ下さい。
def add_warning(method)
owner = method.owner
name = method.name
orig_name = "orig_#{name}"
str = <<-SRC
#{owner.class.to_s.downcase} #{owner}
alias :#{orig_name} :#{name}
def #{name}(*args)
log_warning "#{method.receiver.class}##{name}"
#{orig_name}(*args)
end
end
SRC
eval str
end
$log_warning_hash = {}
def log_warning(method_name)
log_msg = %Q!(compatibility warning) #{method_name} used in #{caller[1]}!
return if $log_warning_hash.has_key?(log_msg)
$log_warning_hash[log_msg] = true
$stderr.puts log_msg
end
# String#size has different behaviour on Ruby-1.9
add_warning "".method(:size)
# String#length has different behaviour on Ruby-1.9
add_warning "".method(:length)
# String#slice has a different behaviour on Ruby-1.9
add_warning "".method(:slice)
# String#slice! has a different behaviour on Ruby-1.9
add_warning "".method(:slice!)
# Array#to_s has different behaviour on Ruby-1.9
add_warning [].method(:to_s)
# String#to_a is not defined on Ruby-1.9
add_warning "".method(:to_a) rescue nil
そんなわけで、Hikiのsvn trunk (r976)は、Ruby-1.9で大体動くはず!
znzさんの指摘で <br> raise rescue log_msg = %Q!(compatibility warning) #{method_name} used in #{$@[2]}! <br>を <br> log_msg = %Q!(compatibility warning) #{method_name} used in #{caller[1]}! <br>に変更しました。
引数を取るメソッドでエラーになっていたのと、メソッド名に!が含まれるメソッドでエラーになっていたのを修正しました。
#to_s -> #to_a?