/ 最近 .rdf 追記 編集 設定 本棚

脳log[20090223] 続報・ruby-1.9.1で、不思議で困ったことが起こった。



2009年02月23日 (月)

[Ruby][tDiary] 続報・ruby-1.9.1で、不思議で困ったことが起こった

問題のコード(再掲)はこれ。あるハッシュのキーについて繰り返しているのに、ハッシュにそのキーが存在しない。(すべてのキーが見つからないわけではないが、見つからないキーはいつでも見つからない)

categorized.keys.each do |c|
	PStore.new(cache_file(c)).transaction do |db|
		categorized.fetch(c) #=> key not found (KeyError)
		db['category'] = {} unless db.root?('category')
		db['category'].update(categorized[c])
	end
end

fetchをブロックの最初に持って行くと、そこではエラーにならない。

categorized.keys.each do |c|
		categorized.fetch(c) #=> O.K.
	PStore.new(cache_file(c)).transaction do |db|
		db['category'] = {} unless db.root?('category')
		db['category'].update(categorized[c])
	end
end

cache_file(c)の呼び出しが原因。その中でも includeしてある ERB::Utilの u()メソッドが核心。

categorized.keys.each do |c|
		::ERB::Util.u(c)
		categorized.fetch(c) #=> key not found (KeyError)
	PStore.new(cache_file(c)).transaction do |db|
		db['category'] = {} unless db.root?('category')
		db['category'].update(categorized[c])
	end
end

引数にした文字列のエンコーディングが変わってしまっている。

categorized.keys.each do |c|
		enc1 = c.encoding;
		::ERB::Util.u(c)
		enc2 = c.encoding
		categorized.fetch(c) { raise "#{enc1} #{enc2} #{::ERB.version}" } #=> UTF-8 ASCII-8BIT erb.rb [2.1.0 2009-01-11] (RuntimeError)
	PStore.new(cache_file(c)).transaction do |db|
		db['category'] = {} unless db.root?('category')
		db['category'].update(categorized[c])
	end
end

ERB::Util.url_encodeの定義を見ると、引数の文字列を dupした後にエンコーディングを変更しているにも関わらず、呼び出し元に影響を与えてしまっている。

    def url_encode(s)
      s.to_s.dup.force_encoding("ASCII-8BIT").gsub(/[^a-zA-Z0-9_\-.]/n) {
        sprintf("%%%02X", $&.unpack("C")[0])
      }
    end
    alias u url_encode

そんなわけだから呼び出し側(category.rb)で

u( c.dup )

なんてやっても効果はなく、

u( ""+c )

あるいは

u( "#{c}" )

とやって初めて、今回の現象を回避することができた。

これは、文字列の複製を遅らせた結果、期せずしておこった現象にみえる

バグのはずなんだけど、irbで再現しようと思ってもできないんだこれが。


 追記@2009-08-13: 見る人が見れば修正はあっという間でした。

http://redmine.ruby-lang.org/issues/show/1929

(ここに、見るべき場所を見つけることもできなかった人間がひとり)

* 正しくは ruby-1.9.2dev(2009-02-03)