Ruby 1.9.1 への移行挑戦の記録

ということで久しぶりに trunk のソースツリーを svn up。というところで、そういえば 1.9.1 の release candidate 2 のリリースがアナウンスされていたなとふと思い出したので、仕事のプロダクトが 1.9 でどの程度動くかまた挑戦してみることにしました。半年前くらいに挑戦した時は確か Thread.critical に依存しているところがあってすぐに動かせなかったのですが、そのコードは書き換えられたので今度はそこそこいけるんじゃないかと思ったので。

なので 1.9 への移行の経験談としてちょっと残しておきます。

  • とりあえず magic comment は全部つけておく。
    • m17n関連の対応その1。ちなみに会社のコードは未だに EUC-JP。一行目に "# -*- coding: euc-jp -*-" とか書いておくだけ。もちろん実際のファイルの文字コードのものを書く。いまどきなら UTF-8 かな。これは 1.8 でも単にコメントとして読み飛ばされるだけなので今後は常に書くようにするのが良いでしょう。というかエディタのマクロとかで自動的に挿入させるのがいいでしょうね。
    • それはともかく、標準添付のライブラリのほとんどに magic comment がついてないのはどうなの。要らないの?
  • IO.read 等で読み込んだ String に encoding が付いていることによる問題
    • m17n関連の対応その2。
    • これはたとえば、String#length が文字コードを認識してくれて、バイト数じゃなくて文字数を返すようになったこととか。
      • 1.8 では String はただのバイト列だったので、ファイル等からバイナリのデータを読む時に特に意識しなくて良かったけど、1.9 では読み込んだ文字列が例えば EUC-JP の encoding になっていたとすると、バイナリのデータの一部がたまたま EUC-JP の文字として認識されるバイト列だった場合に String#size が思っていたのと違う数値を返してエラーになったり。
      • バイト数を得るには String#bytesize を使えばいいらしい。あ、1.8.7 にもバックポートされているんだ。だったらこれを使えばいいか。これは今後ソースをチェックしていこう。
    • あとそもそもバイナリを期待しているものを EUC-JP として読んでいることがだめなわけで、そのへんもなんとかしないといけない。
      • 例えば IO.read で読み込みサイズを指定しない呼び出しだと、勝手に Encoding.default_external のエンコーディングになってしまう。
      • IO.read(filepath, :conding => "ASCII-8BIT") のようにオプション引数(ただのハッシュだけど)で指定するといいらしい、でもこれは 1.8 では動かない。Kernel#open も、第二引数に "r:utf-8" のように書けばいいらしいけどこれも 1.8 では動かない。もしかして open(filepath, "rb") なら ASCII-8BIT になるのかなと思ったらどうやらそうではないらしい。まあこれは改行コードの扱いの指定だからかな?
      • そんなわけで 1.8 でも動くようにしつつ、あんまりたくさん書き換えずにすむ対処法としては、Encoding.default_external= で ASCII-8BIT を指定するという方法でした。
        • プロダクトは ruby インタプリタを常にラッパ経由で起動するようになっていたので、コマンドラインオプションに -EASCII-8BIT を追加することで対処できました。
        • でもなんか過去の ruby-dev の議論をぼんやり眺めていた印象としては ASCII-8BIT を多用するのはあまりよくないことなのかも。本当はスクリプトをちゃんと確認して、必要なところで適宜 encoding を指定したり、bytesize 等適切なメソッドを使うという対処をしたほうがいいのかもしれません。
        • しかし 1.8 では常に ASCII-8BIT だったというのとだいたい同じなんじゃ? だったら 1.8 向けのスクリプトは Encoding.default_external = "ASCII-8BIT" でファイル等からの読み込みのぶんについては OK なんじゃないのかな? magic comment をつけたスクリプトの中の文字列リテラルの問題もあるけど。
      • ちなみに今回対応させたプロダクトはコメント以外は日本語を使っていない(us-ascii)ので m17n の問題の影響は少ないほうだと思う。よく知りませんが日本語の正規表現のこととか、どんなことになるのかぞっとしますね。
  • classx の verification で一部 :default に定数を渡しているところで動かなかった。
    • 定数の検索が変化したんだったか。でも :default => lambda { Const } のように Proc で包むと回避できました。うーん、もうちょっと調べておきたい。
  • { 1, 2, 3, 4 } => { 1 => 2, 3 => 4 } というリテラル記法が廃止された影響
    • 誰だよこんな記法を見付けてきて使ったのは……。即修正。
  • termios 拡張ライブラリが 1.9 に未対応(RubyGems版)
    • gem install termios がエラーになったというだけでよく調べていません。http://arika.org/ruby/termios によると 0.9.5 で 1.9.1 対応のパッチを取り込んだとあるんだけど……。これも追加調査が必要。(追記:確かに tarball 版の 0.9.5 はあるんだけど、RubyGems版はまだ 0.9.4 みたい。gem パッケージは誰が作っているんだろう。)
    • でもとりあえず依存してる部分をコメントアウトした。
  • Find.find が渡されたディレクトリが存在しないときに Errno::ENOENT を raise するようになってた。1.8 では黙ってブロックを yield せずに返ってきてた。
    • まあ多分正しい挙動になったということなのだろうから Find.find する前にチェックするように。

だいたいこれくらいやったら、とりあえず動いたかもという状態にはなりました。多分まだ細々としたところが残ってるでしょう。やっぱり encoding 関連が一番インパクト大きいかなという印象。

これだったら差し替え版でフルテスト流してみて移行できるか評価してみようという話にもっていけるかも。正直 1.9 系を本気でプロダクトで使ってリリースするのはまだ早いんじゃないかとも思いますが、1.9 にすると速度面や Hash の順序保存、それと地味に iseq をダンプしたものを読み込むとか、かねてから要望があったものの 1.8 では実現の難しかった項目が解決 or 対応の可能性ができるので、けっこうアピールできるかも。安定版だってバグがないわけじゃないし。できれば 1.9.1 の正式リリース前に踏めるバグは踏んでおきたいし。

(追記) デブサミ2009で yugui さんが 1.9 の導入について話すらしいからぜひ聞きに行こう。