2009-08-15 [長年日記]

_ [Ruby] Rubyの互換性警告ライブラリを作ってみました / Je viens de faire un librairie qui alerte la compatibilité de Ruby.

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で大体動くはず!

このエントリーを含むはてなブックマーク 
本日のツッコミ(全3件) [ツッコミを入れる]
_ かずひこ (2009-08-16 04:09)

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>に変更しました。

_ かずひこ (2009-08-16 04:55)

引数を取るメソッドでエラーになっていたのと、メソッド名に!が含まれるメソッドでエラーになっていたのを修正しました。

_ kou (2009-08-16 10:05)

#to_s -> #to_a?

[]

トップ «前の日記(2009-08-11) 最新