2009-01-07(水) [長年日記]
■ tDiary: ruby 1.9.1対応でdefault_externalを使うのは適切か?
tDiaryのtrunkで、ruby 1.9.1対応のためにdefault_externalを設定している件に関して、ZnZさんからツッコミがあった:
default_externalの影響範囲がよくわからないけど、tdiaryのここで使うのは適切なのだろうか?
ツッコミ欄に書くには長くなるので、こっちで書いてみる。ホントは書きたくなかったんだけど、なぜかというとるびまに書くネタがなくなっちゃうから!(ぉ もっとも、ruby 1.9のエンコードに関してちゃんと理解しているとは言いがたいし、ここでツッコミ受けたほうがいいでしょう。
実行中のruby 1.9全域に影響するエンコーディングには3種類ある(たぶん):
- scriptエンコーディング
- default_internal
- default_external
scriptエンコーディングは、*.rbファイルそのもののエンコーディングで、これはマジックコメントを入れれば対応できる。もしくはevalなどする場合には、その文字列のエンコーディングを適切に設定してやれば良い。これはOK。tDiary trunkの*.rbにはすべて以下の1行が挿入済みだ:
# -*- coding: utf-8; -*-
default_internalはrubyの内部処理(?)におけるデフォルトで、あんまり考える必要はないと思ってる。だから今のところスルー。
default_externalは、プログラムの「外界」でどんなエンコーディングが使われているかの標準値で、基本的にはインタープリターがlocaleなどを参考に起動時に決定するし、-Kなどのパラメタでも制御できる。tDiaryで問題になるのはコレ。
tDiaryが使われる状況は「ふつーのレンタルサーバ」を想定している。「外界」の環境設定はレンサバ屋さんが自分たちの都合で設定していて、ユーザには変更できないことが多い。また、tDiary設置者のリテラシを考慮すると、shebangに適切なオプションが指定されることも期待できない(そういう縛りを増やすとサポートの手間が増えて開発が滞るのである)。
また「とりあえずrubyだけ入っていれば動く」というのを最低限の環境として想定していて、それもできる限り古いrubyもサポートしたい。実際、1.8.7が入っているサーバはまだまだ少なくて、(セキュリティ的に好ましくないとはいえ)1.8.2~1.8.6がゴロゴロしているのが実態だ。この上にruby 1.9対応を付け加えるのがtDiary 2.3系開発のミッション。
default_externalに依存せずに「外界」のエンコーディングを制御する方法としては、例えばファイル等の読み出し時に下記のようにに指定する方法があるわけだが:
open( 'hoge', 'r:utf-8' )
ちょっと古いruby 1.8系だとこんなオプションは受け入れられない。そもそも、tDiaryはしばらく(EUC-JPな)2.2系と(UTF-8な)2.3系が並存するし、rubyも1.8系と1.9系が長く並存するだろう。tDiary用のプラグインを書く人には、これらの組み合わせにできるだけ対応して欲しいし(tDiary 2.2とruby 1.9の組み合わせはないけど)、そのためのハードルは低く保ちたい。
となると、以下のように書くだけで、読み込んだファイルは(ruby 1.9では)UTF-8だと認識して欲しいわけだ:
open( 'hoge' )
現在のtrunkのコードには(試行錯誤の結果として)「'r:utf-8'」が入っているが、default_externalが期待通りのデフォルト値を設定してくれることがわかれば、これは元に戻してしまいたい。
逆に言えば、利用者やプラグイン開発者の負担になることなく外界のエンコーディングをUTF-8に設定できるなら、default_externalなど使わずに済ませたい。
というのも、CGI以外のプラットフォームで他のアプリケーションと同じインタープリタ上でtDiaryを動かすような場合、default_externalを勝手に変更するのはまずいからだ。もっとも、default_externalの値に何かを期待するアプリを同じインタープリタ上で動作させるのはそもそも無茶だとは思うけど。
というわけで、利用者やプラグイン開発者に「外界」のことを意識させずにUTF-8で統一されたエンコーディングを使わせたいので、external_encodingを設定している、というのが現在の状況。
せめてRUBY_VERSIONで分岐はやめてください。
evalはBinding渡しても無理ですか?
RUBY_VERSION分岐は暫定措置なので、たぶんなんとかします(^^;
後半の意味がよくわかりません。evalのエンコーディング(?)は渡す文字列で決まると思っていたんだけど、Bindingでも左右できるんですか?
http://sho.tdiary.net/20090104.html#p01
> eval内でローカル変数が定義できないのは相当痛い。
の件です。
evalのスクリプトエンコーディングは文字列中のマジックコメント、文字列自身のエンコーディングだけで決まります。
ぬおぉ、できました! >binding
そうか、こんなテクがあったのか……。