Rails2.0.2のjavascript_include_tagのcacheはRailsがルート以外(/appとか)で動いてるとバグる

タイトルの通り。
たとえば、mongrel_clusterなんかをつかってて、

prefix: /rails

とかやってて、アプリのURLが

http://taslam.example.jp/sample

なんかになってると、javascript_include_tag(*, :cache => 'cache')の動作がおかしくなる。

Railsのソースを追ってみると、
/usr/local/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_view/helpers/asset_tag_helper.rb

def join_asset_file_contents(paths)
  # ASSETS_DIR => /home/admin/rails/sample/public
  # paths => ["/sample/javascripts/prototype.js", ...])
  # /home/admin/rails/sample/public/app/javascripts/prototype.js を読み出そうとしている
  paths.collect { |path| File.read(File.join(ASSETS_DIR, path.split("?").first)) }.join("\n\n")
end

/sampleで動かすと、scriptタグのsrcに記載されるパス(URL)は"/sample/javascripts/prototype.js" という具合になる。
しかし、サーバ内でのパスは、RAILS_ROOT/javascripts/prototype.js のままのはず。

javascript_include_tagにもどってもういちどみてみる。

def javascript_include_tag(*sources)
  options = sources.extract_options!.stringify_keys
  cache   = options.delete("cache")

  if ActionController::Base.perform_caching && cache
    joined_javascript_name = (cache == true ? "all" : cache) + ".js"
    joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name)

    write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources))
    javascript_src_tag(joined_javascript_name, options)
  else
    expand_javascript_sources(sources).collect { |source| javascript_src_tag(source, options) }.join("\n")
  end
end

write_asset_file_contentsは、第1引数にキャッシュファイルのパス、第2引数にファイルのパスの配列を渡すと、キャッシュファイルにまとめてくれるメソッド。(キャッシュが既にあれば処理しない)
内部で、さっきみたjoin_asset_file_contentsを呼んでる。

compute_javascript_pathsは、ファイル名の配列(['prototype', 'effects',...]とか)を受け取って、それぞれのサイトルートからのパス(/sample/javascripts/prototype.js)の配列を返すメソッド。

けど、ここではサイトルートからのパスなんていらないような・・・
ここでほしいのは、読み出すために必要なサーバ内のパスだよね。


あんまり深く考えるのは面倒だったので、とりあえず適当なところで上書きしてやった。これだと、「/スクリプトのパス」での指定が出来ないけど、使わないからまぁいいや。

module ActionView
  module Helpers
    module AssetTagHelper
      def javascript_include_tag(*sources)
        options = sources.extract_options!.stringify_keys
        cache   = options.delete("cache")

        if ActionController::Base.perform_caching && cache
          joined_javascript_name = (cache == true ? "all" : cache) + ".js"
          joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name)

          write_asset_file_contents(joined_javascript_path, sources.collect{ |source| "/javascripts/#{source}.js"})
          javascript_src_tag(joined_javascript_name, options)
        else
          expand_javascript_sources(sources).collect { |source| javascript_src_tag(source, options) }.join("\n")
        end
      end
    end
  end
end