RJS Templateが描画されない問題

Ruby on Railsの習熟がてら、簡単なBBSを作ってみたんですよ。で、せっかくだからプレビューをAJAXで出そうぜ、などと思い立った。

#
# app/controllers/entries_controller.rb
#
def preview
  @entry = Entry.new(params[:entry])

  render :update do |page|
    page.replace_html 'preview', :partial => 'preview'
    page.visual_effect :highlight, 'preview'
  end
end

これはご機嫌に動いてくれた。画面遷移なしに、かっこいいエフェクトつきでプレビューが表示されるのだ。イカス。

ところで、ここではinline RJSにしているが、RJSがある一定以上の大きさになると、*.rjsといった名前のテンプレートファイルに切り出すのが一般的である。そんなわけで、切り出してみた。

#
# app/controllers/entries_controller.rb
#
def preview
  @entry = Entry.new(params[:entry])
end
#
# app/views/entries/preview.rjs
#
page.replace_html 'preview', :partial => 'preview'
page.visual_effect :highlight, 'preview'

動かないんだ、これが。

FireBugでレスポンスを見てみたが、どちらのケースでも同じ内容を返しているように見える。なぜinlineで動くものがテンプレート化すると動かないのか、さっぱりわからない。

結論から言うと、見ているところが違った。レスポンスヘッダが変わっている。Content-Typeが、inlineだとtext/javascriptなのに、テンプレートだとtext/htmlになっている。text/javascriptでないと動かないのだ。そりゃそうだ。

この原因は、日本語環境向け設定にある。Railsを日本語環境で使う場合、app/controllers/application.rbにちょっとした細工を行うのが普通だ。

before_filter :set_charset

def set_charset
  headers['Content-Type'] = 'text/html; charset=UTF-8'
end

ここで、使用する文字コードUTF-8にしたいがためにContent-Typeヘッダを設定している。この方法は、かのAWDwR本で紹介されているので、まったく同じ実装を行っている人は多いはず。ところが、どうやらRailsは設定値を動的に変えているらしく、この実装だとせっかくRailsが裏で調整した設定を台無しにしてしまう。

そこで、対処。

before_filter :set_charset

def set_charset
  content_type = headers['Content-Type'] || 'text/html'

  if /^text\// === content_type
    headers['Content-Type'] = "#{content_type}; charset=UTF-8"
  end
end

基本的にRails任せにしておいて、テキスト形式(text/*)である場合のみ文字コードの設定を追加で行う。これで無事動くようになった。

参考サイトはこちら。

ありがちな問題に思えるのに、なかなか情報が見つからなくて苦戦した・・・。

2007/08/14 追記
こちらのサイトによると、Ruby-Gettext-Packageを使ってローカライズしている場合は、そのパッケージ側で文字コードの設定を実施するそうである。この場合、"Content-Type"の置き換え処理はまるまる不要になる。