ちなみに再来週になったら後編が掲載されてるはず。Rackのミドルウェアの話とかを予定してる(予定ってことはつまりまぁまだ書き上げてないんだけども...)ので、お楽しみに。だるろぐに触発されて俺もBBSを作ってみようと、ここ数日Sinatraをいじっていた。Sinatraさんは最近バージョンアップしてた気がするけど、どうやらちゃんとRuby1.9.1でも動くようだ。順調に行くかと思ったんだけど、どうにも書き込みのspecが通らない。そこで初めて、POSTやGETでパラメータにマルチバイトの文字列が入ってると何かおかしいことに気付いた。最初に書いたspecとアプリ側のコードを抜粋。'/'とかEntryのスキーマとかindex.hamlとかも用意してありますがそれは心の目で補完しておくれ。期待してるのはこれでセトがツンデレな投稿をしてそれがDBに記録され、その後トップにリダイレクトされることなんだけど、これがコケる。"body"=>"\xE3\x81\xB9\xE3\x80\x81\xE5\x88\xA5\xE3\x81\xAB\xE3\x82\xA2\xE3\x83\xB3\xE3\x82\xBF\xE3\x81\xAE"})ありゃ。UTF-8の文字列がくるべきところに、ASCII-8BIT、つまりバイト列がそのまま来ちゃってる。んむ、えーと、実際のバイト列を変更せずにエンコーディングだけ変えるのはforce_encodingだったよね。いうことでforce_encoding('UTF-8')してみる。"body"=>"べ、別にアンタの"})あ、あれ?なんか足りなくない?これはどういうこと?Rack::Utils#escapeところで、上では端折ったけど、実はspecを実行する際にこんなwarningが出ている。stringUTF-8に対してエンコーディングを無視して正規表現にマッチさせてる...うん、どうもこれが怪しい。これはどこから出てるかというと、こんなコードから出ている。endendなるほどですねー。要するに、escape中でunpackするときに、String#sizeを使っている。1.8まではこのメソッドはバイト数を返してたけど、1.9では文字数を返すように仕様が変更されている。マルチバイトな文字列だと当然バイト数と文字数は一致しないので、途中で切れちゃうことになる。packして返ってくるのは単なるバイト列、つまりASCII-8BITのString。1.8までは文字列がどんなエンコーディングであれ、unpackしてpackすればまた文字列に戻る。1.9ではunpackからpackの過程で元のエンコーディング情報は失なわれているので、明示的にエンコーディング情報を付加してやる必要がある。1に関してはString#sizeを使ってるところをString#bytesizeを使うように書き換えればいい。ちなみにRack::Utilsでは1.8でも1.9でも動くようにRack::Utils#bytesizeというメソッドが定義されているので、それを使うといい。いうか、なんで使ってないんだろう。忘れてるんだろうなぁ...。endendこれをrackをrequireした後に書いてRack::Utils#escapeを上書きしてやると、ちゃんとescapeされるようになる。次はASCII-8BITになっちゃってるのをforce_encodingしてエンコーディングを変える。これも本当はRack::Utils#unescapeを上書きして、その中でエンコーディングを推測してforce_encodingしておいて、アプリ側のフックでString#encodingを使って内部で使うエンコーディングに変換してやるのがいいかなぁとか思ったんだけど、それでいいのかなー。んでもまぁ、今回はUAに応じてviewのエンコーディング変えるみたいなことはしてないし、UTF-8決め打ちで良いよね☆...というわけで、Sinatraのbeforeの中で、RUBY_VERSIONが1.9以上だったときはparamの文字列をforce_encoding('UTF-8')するようにしたった。良い子はちゃんとして下さい。はい、これでちゃんとspec通りました。