Ruby 1.9.0 で tDiary を動かしてみる(トップページだけ)
Ruby 1.9.0 で非互換が増えたって話だけど、実際のところどれくらい影響があるかよく分かんない。 ってことで、ちょうど tDiary 2.2 もリリースされたことだし、無謀にも tDiary を Ruby 1.9.0 で動かそうとしてみた。 とりあえず、トップページが表示されるところまではできた。 でも、編集とかプラグイン系はダメっぽい。
以下、トップページを動かすまでの記録。
$defout
まず、普通に設置してみると、いきなり Internal Server Error が出た。 どうも、 $defout.binmode がダメっぽい。irb で実験。Ruby1.8.5だと $binmode は IO オブジェクトが返ってくる。
irb(main):001:0> $defout => #<IO:0x8120a7c>
Ruby1.9.0だと nil が返ってくる。
irb(main):001:0> $defout => nil
ってことで、 index.rb から2行を削除。 $KCODE はともかく、 $defout を削除していいのかなぁ…。
@@ -5,8 +6,6 @@ # Copyright (C) 2001-2006, TADA Tadashi <sho@spc.gr.jp> # You can redistribute it and/or modify it under GPL2. # -BEGIN { $defout.binmode } -$KCODE = 'n' begin if FileTest::symlink?( __FILE__ ) then
文字コードの指定
次に出たのがこのエラー。
(tdiary.conf):138: invalid multibyte char
Ruby1.9 から文字列がエンコーディング情報を持つようになった…ってどこかで読んだ。 文字コードが EUC-JP だよと Ruby に伝えていないから、不明なマルチバイト文字だよって言っているんだろうなぁ。
Ruby1.9 の m17n に関する情報ってどこでまとまっているんだろ? いろいろググってたら、2007-12-16 - diary of a madman で文字コード関連の情報を発見。
ファイルの先頭付近に "# -*- encoding: UTF-8 -*-" と書いておくと、そのファイルのデフォルトエンコーディングを指定できる。
コマンドラインから --encoding=UTF-8 といった指定も可能。
最初は tdiary.conf の先頭に "# -*- encoding: UTF-8 -*-" と書いたんだけど、すべてのマルチバイト文字が含まれるファイル (特にプラグインの日本語リソース) で同じ指定が必要になりそうな気配だったので、 --encoding=EUC-JP を指定することにした。 index.rb の先頭を修正。
-#!/usr/bin/env ruby +#!/home/machu/local/bin/ruby --encoding=EUC-JP
instance_variables の戻り値が文字列からシンボルへ
次に出たエラーがこれ。
undefined method `sub!' for :@cgi:Symbol (NoMethodError) /home/machu/www/www.machu.jp/diary2/tdiary.rb:469:in `block in initialize'
tdiary.rb にて instance_variables の返り値を処理するところでのエラーみたい。
また irb を使って試してみる。 Ruby1.8.5 だと、インスタンス変数名は文字列として返ってくる。
irb(main):001:0> @a = @b = 0 => 0 irb(main):002:0> instance_variables => ["@b", "@a"]
Ruby1.9.0 だと、文字列ではなくシンボルとして返ってくる。
irb(main):001:0> @a = @b = 0 => 0 irb(main):002:0> instance_variables => [:@b, :@a]
シンボルに sub! をしていたのでエラーになっていたと。 なので、 sub! の直前でシンボルを文字列に変換した。 こんなんでいいのだろうか?
@@ -466,6 +466,7 @@ load instance_variables.each do |v| + v = v.to_s v.sub!( /@/, '' ) instance_eval( <<-SRC def #{v}
複数文字コードの混在
次のエラーはこれ。
Plugin error in '00default.rb'. invalid multibyte escape: /[\x80-\xff]/ (plugin/00default.rb):615: invalid multibyte escape: /[\x80-\xff]/
該当の行の処理は 00default.rb の以下のコード。
mail_header = comment_mail_mime( @conf.to_mail( mail_header ) ).join( "\n " ) if /[\x80-\xff]/ =~ mail_header
メールを送ろうとしているから、 mail_header の文字コードは ISO-2022-JP なんだろう。 EUC-JP で使われていないコードを指定しているからエラーがでているのかな? 正規表現で使用している [\x80-\xff] が NG っぽい。 原因が分からなかったので、とりあえずここはコメントアウトした。
…ダメじゃん。
文字列中のバックスラッシュ
こんどはこれ。
TDiary::PluginError Plugin error in '05referer.rb'. (plugin/ja/05referer.rb):34: invalid multibyte char
ソースコード中のバックスラッシュの扱いがよくないみたい。
置換文字列中で「\\1」のような「\数字」で利用できます
以下のようにすれば動いた。
置換文字列中で「\\\\1」のような「\\数字」で利用できます
String#to_a がなくなった
今度は以下のエラー。
TDiary::PluginError Plugin error in '50sp.rb'. undefined method `to_a' for "/home/machu/www/www.machu.jp/diary2/misc/plugin":String (plugin/50sp.rb):4:in `block in load_plugin'
該当ソースは以下の通り。最後の to_a で失敗している。
@sp_path = ( @conf["#{SP_PREFIX}.path"] || "#{TDiary::PATH}/misc/plugin" ).to_a
よく分からないので、 irb で実験してみよう。 まずは Ruby 1.8.5。 String#to_a で配列が返ってくる。
irb(main):001:0> "abc".to_a => ["abc"]
次に Ruby 1.9.0。 String#to_a はないよ、って言われた。
irb(main):001:0> "abc".to_a NoMethodError: undefined method `to_a' for "abc":String
ってことで、以下のようにソースを修正。なんか間違っている気がする…。
@sp_path = @conf["#{SP_PREFIX}.path"] || [ "#{TDiary::PATH}/misc/plugin" ]
変数の文字コードを指定
次がこれ。 ようやくプラグインの実行 (eval_src) のところまでたどり着いたみたい。
ArgumentError character encodings differ(TDiary::Plugin#eval_src):10:in `concat' (TDiary::Plugin#eval_src):10:in `block in eval_src' /home/machu/www/www.machu.jp/diary2/tdiary.rb:753:in `eval'
これまたエンコーディングが違うよ、と。 どうも、 eval の対象文字列 (src) の文字コードが ASCII と判定されているみたい。 ちょっとやっつけで、 eval の直前で src の文字コードを EUC-JP と指定したらエラーがでなくなった。
@@ -747,6 +748,7 @@ @subtitle_procs.taint @section_leave_procs.taint return Safe::safe( secure ? 4 : 1 ) do + src.force_encoding('EUC-JP') eval( src, binding, "(TDiary::Plugin#eval_src)", 1 ) end end
ページが途中で切れる
これでようやくトップページが表示されたんだけど、ソースをみるとページが途中で切れている。
どうも index.cgi で指定している Content-Length の長さが正しくないみたい。 Ruby 1.9.0 では String#size がバイト数ではなく文字数を返すようになったからだろうなぁ。 String#size の代わりに String#bytesize を使うように修正。
@@ -78,7 +81,7 @@ body = '' else head['charset'] = conf.encoding - head['Content-Length'] = body.size.to_s + head['Content-Length'] = body.bytesize.to_s end head['Pragma'] = 'no-cache' head['Cache-Control'] = 'no-cache'
これでようやくトップページが表示された。 でも、日記を編集しようとするとパーサ周りでまたエラーが出てる。
undefined method `collect' for "日記のテスト":String (NoMethodError) /home/machu/www/www.machu.jp/diary2/tdiary/tdiary_style.rb:202:in `block in to_html4'
こっちは時間かかりそうだなぁ…。とりあえずここまで。
追記
そんなに時間かからなかった。 String#collect の仕様変更について、 eigenclass - Changes in Ruby 1.9より引用。
No longer an Enumerable
String is not Enumerable anymore. Use #each_line instead of #each, and #lines (see below) to iterate over the lines.
ここに書かれているように、 each_line を使えば Enumerable が取れる。 tdiary_style.rb のエラーが出ている箇所を body.collect から body.each_line.collect に書き換えれば OK 。
@@ -199,7 +199,7 @@ r << %Q[<p>#{section.body.collect{|l|l.chomp.sub( /^[ ]/e, '')}.join( "</p>\n<p>" )}</p>\n] else r << %Q[<p><%= subtitle_proc( Time::at( #{date.to_i} ), nil ) %>] - r << %Q[#{section.body.collect{|l|l.chomp.sub( /^[ ]/e, '' )}.join( "</p>\n<p>" )}</p>] + r << %Q[#{section.body.each_line.collect{|l|l.chomp.sub( /^[ ]/e, '' )}.join( "</p>\n<p>" )}</p>] end r << %Q[<%= section_leave_proc( Time::at( #{date.to_i} ) ) %>\n] r << %Q[</div>]
んで、今は以下のエラーがでてる。
ArgumentError character encodings differ (erb):66:in `concat' (erb):66:in `do_eval_rhtml' /home/machu/local/lib/ruby/1.9.0/erb.rb:743:in `eval' /home/machu/local/lib/ruby/1.9.0/erb.rb:743:in `result' /home/machu/www/www.machu.jp/diary2/tdiary.rb:1094:in `do_eval_rhtml'
TDiaryBase#do_eval_rhtml でエラーが出てる。 また文字エンコーディングが違うよと。 うーん。そろそろ行き当たりばったりでつぶしていくのは辛いかな?
追記2
defaultio.rb と 05referer.rb にて String#each を String#each_line へと置き換えると、マルチバイト文字を含まない日記は更新できるようになった。これも String が Enumerable じゃなくなった影響。
@@ -103,7 +103,7 @@ headers, body = ::TDiary::parse_tdiary( l ) ymd = headers['Date'] next unless body - body.each do |r| + body.each_line do |r| count, ref = r.chomp.split( / /, 2 ) next unless ref yield( ref.chomp, count.to_i )
でも、マルチバイト文字を含むと前述の character encodings differ が発生するのは変わらず。 うーん。 Web ブラウザから送信した日記データと、日記のテンプレート (skel/*.rhtml) の文字エンコーディングが一致していないのかな? でも、本体だけなら出口が見えてきた。
試しに以下の URL に設置してみた。そのうち消すかも。 ちゃんと "Powered by Ruby version 1.9.0" だよ。