«前の日記(2008-01-22 (火)) 最新 次の日記(2008-01-24 (木))»  

まちゅダイアリー


2008-01-23 (水)

tDiary2.2 for Ruby1.9.0

昨年末から tDiary2.2 を Ruby1.9.0 で動かそうとしている。

なんとか形になったので、思い切ってこの日記を Ruby1.9.0 で動かすように切り替えた。 フッタの「Powered by Ruby version 1.9.0」に注目。 一応動いているけど、正直、何が起こっても不思議じゃない。 何かあればツッコミしてもらえると嬉しいなぁ。 ちなみに、ちゃんと Ruby1.8 とも共用できるようになっているはず。

方向性は以下の通り。

  • 一つのコードで Ruby1.8 と Ruby1.9 に両対応する
  • 非互換の部分は極力 compatible.rb でカバーする
  • Ruby1.9 は --encoding=Binary で動作させる

自分が使っているプラグインは一通り動いたけど、それ以外のは分かんない。

インストール方法

  • tdiary-full-2.2.0.tar.gz を取得する
  • tdiary-core-2.2.0.patch と tdiary-plugin-2.2.0.patch を当てる
  • compatible.rb を misc/lib に入れる
  • 起動オプションに --encoding=Binary を付ける
  • cache ディレクトリの *.parser を削除 (PStore は基本的に互換性ないはず)

これらのファイルを CodeRepos に置きたいんだけど、どこに置けばいいかなぁ。

以下、変更点の説明。

compatible.rb

Ruby1.9 で無くなったメソッドを復活させる。

unless "".respond_to?('to_a')
  class String
    def to_a
      [ self ]
    end
  end
end

unless "".respond_to?('each')
  class String
    alias each each_line
  end
end

class String
  def method_missing(name, *args, &block)
    each_line.__send__(name, *args, &block)
  end
end

次に Ruby1.8 で存在しないメソッドを追加。こっちは Ruby 1.8 で動かすために必要。

unless "".respond_to?('force_encoding')
  class String
    def force_encoding(encoding)
      self
    end
  end
end

unless "".respond_to?('bytesize')
  class String
    alias bytesize size
  end
end

unless "".respond_to?('ord')
  class String
    def ord
      self[0]
    end
  end
end

最後に tDiary に特化した変更。

# Ruby1.9でNKF::nkfを呼ぶと文字列のencodingが変わってしまう。
# そのため、encodingがBinaryの環境で動かすと
# "character encodings differ" エラーとなる。
begin
  require 'nkf'
  module NKF
    alias :_nkf :nkf
    def nkf(option, src)
      r = _nkf(option, src)
      r.force_encoding('Binary')
    end
    module_function :nkf, :_nkf
  end
rescue
end

# 日本語を含むツッコミを入れると diary.last_modified が String になる (原因不明)
# (PStore 保存前は Time だが, 保存後に String となる)
# 暫定的に String だったら Time へ変換する
module TDiary
  class WikiDiary
    def last_modified
      if @last_modified.class == String
        @last_modified = Time.at(0)
      end
      @last_modified
    end
  end
end

index.rb

$defout を $stdout に変更。 $defout は Ruby1.6 との後方互換性のためで、1.8でも非推奨

Index: index.rb
===================================================================
--- index.rb  (revision 97)
+++ index.rb  (working copy)
@@ -5,7 +5,7 @@
 # Copyright (C) 2001-2006, TADA Tadashi <sho@spc.gr.jp>
 # You can redistribute it and/or modify it under GPL2.
 #
-BEGIN { $defout.binmode }
+BEGIN { $stdout.binmode }
 $KCODE = 'n'

 begin

update.rb

index.rb と同じ。

Index: update.rb
===================================================================
--- update.rb (revision 97)
+++ update.rb (working copy)
@@ -5,7 +5,7 @@
 # Copyright (C) 2001-2003, TADA Tadashi <sho@spc.gr.jp>
 # You can redistribute it and/or modify it under GPL2.
 #
-BEGIN { $defout.binmode }
+BEGIN { $stdout.binmode }
 $KCODE = 'n'

      if pos == 'detail' then

tdiary.rb

ちょこっと変更あり。

  • compatible.rb をロード
  • instance_variables.each が文字列でなくシンボルを返すように変更になったので to_s で文字列に変換
  • eval で宣言したローカル変数が外側で読めないので、 eval 実行時にインスタンス変数への格納まで実施
Index: tdiary.rb
===================================================================                                                           --- tdiary.rb (revision 97)
+++ tdiary.rb (working copy)
@@ -11,6 +11,7 @@

 $:.insert( 1, File::dirname( __FILE__ ) + '/misc/lib' )

+require 'compatible'
 require 'uri'
 begin
@@ -466,6 +467,7 @@
      load

      instance_variables.each do |v|
+       v = v.to_s
        v.sub!( /@/, '' )
        instance_eval( <<-SRC
          def #{v}
@@ -631,11 +633,10 @@
        cgi_conf.untaint unless @secure
        def_vars = ""
        variables.each do |var| def_vars << "#{var} = nil\n" end
-       eval( def_vars )
+       extend_vars = 'variables.each do |var| eval "@#{var} = #{var} if #{var} != nil" end'
        Safe::safe( @secure ? 4 : 1 ) do
-         eval( cgi_conf, binding, "(TDiary::Config#cgi_conf)", 1 )
+         eval( def_vars + cgi_conf + extend_vars, binding, "(TDiary::Config#cgi_conf)", 1 )
        end
-       variables.each do |var| eval "@#{var} = #{var} if #{var} != nil" end
      rescue IOError, Errno::ENOENT
      end
    end

misc/lib/hikidoc.rb

String#[] は文字コードではなく文字を返すようになったので、 ord メソッドを呼ぶように変更。 Ruby1.8 環境の場合は compatible.rb で対応している。

Index: misc/lib/hikidoc.rb
===================================================================
--- misc/lib/hikidoc.rb (revision 109)
+++ misc/lib/hikidoc.rb (working copy)
@@ -476,7 +476,7 @@

   def escape_meta_char( text )
     text.gsub( META_CHAR_RE ) do |s|
-      '&#x%x;' % s[1]
+      '&#x%x;' % s[1].ord
     end
   end

plugin/ja/05referer.rb

前に書いたとおり、エスケープの数の問題。

Index: plugin/ja/05referer.rb
===================================================================
--- plugin/ja/05referer.rb  (revision 97)
+++ plugin/ja/05referer.rb  (working copy)
@@ -31,7 +31,7 @@
  <p>→<a href="#{h @update}?referer=volatile" target="referer">既存設定はこちら</a></p>                                                                               
  <p><textarea name="only_volatile" cols="70" rows="10">#{h @conf.only_volatile2.join( "\n" )}</textarea></p>
  <h3 class="subtitle">#{label_referer_table}</h3>
- #{"<p>リンク元リストのURLを、特定の文字列に変換する対応表を指定できます。1件につき、URLと表示文字列を空白で区切って指定します。正規表現が使えるので、URL中に現れた「(&#12316;)」は、置換文字列中で「\\1」のような「\数字」で利用できます。</p>" unless @conf.mobile_agent?}
+ #{"<p>リンク元リストのURLを、特定の文字列に変換する対応表を指定できます。1件につき、URLと表示文字列を空白で区切って指定します。正規表現が使えるので、URL中に現れた「(&#12316;)」は、置換文字列中で「\\\\1」のような「\\数字」で利用できます。</p>" unless @conf.mobile_agent?}                                                                      き、URLと表示文字列を空白で区切って指定します。正規表現が使えるので、URL中に現れた「(&#12316;)」は、置換文字列中で「\\1」のような「\数字」で利用できます。</p>" unless @con  <p><textarea name="referer_table" cols="70" rows="10">#{h @conf.referer_table2.collect{|a|a.join( " " )}.join( "\n" )}</textarea></p>
  HTML

amazon.rb

たぶん、rexmlライブラリでエラー - 橋本幸樹のいまさら日記と同じ問題。 とりあえず force_encoding で逃げる。

Index: misc/plugin/amazon.rb
===================================================================
--- misc/plugin/amazon.rb (revision 113)
+++ misc/plugin/amazon.rb (working copy)
@@ -205,6 +205,7 @@
        xml =  amazon_call_ecs( asin, id_type )
        File::open( "#{cache}/#{asin}.xml", 'wb' ) {|f| f.write( xml )}
      end
+     xml.force_encoding('UTF-8')
      doc = REXML::Document::new( xml ).root
      item = doc.elements.to_a( '*/Item' )[0]
      if pos == 'detail' then

あとは contrib の category_to_tag.rb も一部変えてる。

追記

移行のときにテストツッコミがRSSに入ったり、RSSがテスト環境にリダイレクトされたりといくつかミスっちゃった。

追記2

自分でやっといてなんだけど、よほどの覚悟がないと Ruby1.9 で動かすのはお勧めしない。 まだまだ 1.9 の非互換な変更が入るかもしれないしね。

追記3

やっぱりまだ不具合がありそう。

  • ツッコミが入った日の日記にツッコミフォームが表示されない → hide_comment_form プラグインを無効にすれば表示される
  • ツッコミフォームが表示されてもツッコミが反映されない (原因不明)
  • ツッコミを入れたら ruby の CPU 使用率が 70% 前後になり、処理が終わらない (強制停止したら makerss でストップ)

たださんからの嬉しいツッコミにお返事できないよ…。なのでここに書く。

ありがとうございます! 熱すぎるかも…と思っていたのですが、UTF8部ランチとマージされる頃にはちょうどいい位に冷めていそうですね。

追記4

ツッコミフォームが表示されない原因が分かった。 今の Ruby1.9 環境だと、ツッコミを入れたときに cache/*.parser に保存されるキャッシュの last_modified が Time でなくて String になってしまうという問題がある。 PStore に保存するときは Time なのに、取り出すときは(読めない) String になる。 原因が分からなかったので、 last_modified が String のときは Time.at(0) にしていたんだけど、この影響で古い日記と判断されてツッコミフォームが消えていた(SPAM対策で古い日記にツッコミできないようにしている)。

Tags: tDiary Ruby
本日のツッコミ(全5件) [ツッコミを入れる]
ただただし (2008-01-24 (木) 08:52)

>これらのファイルを CodeRepos に置きたいんだけど、どこに置けばいいかなぁ。<br>CodeReposじゃなくて、2.2ブランチを切ってUTF8ブランチをマージしたあとに、Trunkに入れればいいと思いました。

しばた (2008-01-24 (木) 22:19)

とりあえず、CodeReposのtdiary/util以下にでも突っ込んでおいて公開しておくのがいいんじゃないでしょか。

まちゅ (2008-01-25 (金) 01:11)

ありがとうございます。<br>CodeRepos の util で冷ましながら、 Trunk マージを待つことにします。

たけ(tk) (2008-01-25 (金) 08:59)

二つほど分からないことがあったので教えてください。<br><br>(1)<br><br>each_line.__send__(name, *args, &block)<br><br>を1.8 で実行すると、"" 以外ではエラーになってしまいませんか? 1.9 なら OK なのかなあ?<br><br>R:\USR>ruby -e 'p "".each_line.__send__(:to_i)'<br>0<br><br>R:\USR>ruby -e 'p "a".each_line.__send__(:to_i)'<br>-e:1:in `each_line': no block given (LocalJumpError)<br> from -e:1<br><br>(2)<br><br>+ '&#x%x;' % s[1].ord<br><br>の s[1] は 1.8 では Fixnum を返すので、<br><br>unless "".respond_to?('ord')<br> class String<br> def ord<br> self[0]<br> end<br> end<br> class Integer<br> def ord<br> self<br> end<br> end<br>end<br><br>(3)compatible.rb はruby 1.9 の標準で欲しいようなライブラリですね。(もしくは、独立のプロジェクトで完成させたいライブラリ)。

まちゅ (2008-01-25 (金) 15:20)

>たけさん<br>逆に僕が教わってますね。<br><br>(1) 現在の compatible.rb では 1.8 での each_line を考慮してないです。 1.9 で each を使うことしか考えてませんでした。<br><br>(2) ああっ。確かに今のコードじゃダメですね。でも Integer に ord を付けるのも…ううん。<br><br>(3) 賛成です。どうせならみんなで使えるといいですね。ここだけ分離して CodeRepos に入れようかなぁ。<br>いずれにせよ、ちゃんとテストケースを書かないとダメですね。