image_tagで付加される画像のタイムスタンプを、production環境でも画像ファイルが更新されたタイミングで更新されるようにする

image_tagで画像を表示した際に、srcの後ろに付くタイムスタンプは
基本的には File.mtime(image_path) なので、画像ファイルの最終更新時刻です。

image_tag 'rails.png'  #=> <img src="/images/rails.png?1298610241" alt="Rails" />


なので、developement環境では画像ファイルをtouchしてあげたりすると、タイムスタンプも更新されます。
しかし、production環境ではtouchしても更新されません。(passengerなり、サーバをリスタートすれば更新されます)


なぜこうなるかは、ActionView::Helpers::AssetTagHelper の rails_asset_id メソッドを見るとわかります。

# File actionpack/lib/action_view/helpers/asset_tag_helper.rb
        def rails_asset_id(source)
          if asset_id = ENV["RAILS_ASSET_ID"]
            asset_id
          else
            if @@cache_asset_timestamps && (asset_id = @@asset_timestamps_cache[source])
              asset_id
            else
              path = File.join(ASSETS_DIR, source)
              asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : ''

              if @@cache_asset_timestamps
                @@asset_timestamps_cache_guard.synchronize do
                  @@asset_timestamps_cache[source] = asset_id
                end
              end

              asset_id
            end
          end
        end

最初1回は、File.mtime(path) をちゃんと取ってくれるのですが、
その際に、@@asset_timestamps_cache[source] = asset_id で、キャッシュして、
以降は、そのキャッシュが使われます。


で、@@cache_asset_timestamps が何かを見ると

# File actionpack/lib/action_view/helpers/asset_tag_helper.rb
      def self.cache_asset_timestamps
        @@cache_asset_timestamps
      end

      # You can enable or disable the asset tag timestamps cache.
      # With the cache enabled, the asset tag helper methods will make fewer
      # expense file system calls. However this prevents you from modifying
      # any asset files while the server is running.
      #
      #   ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
      def self.cache_asset_timestamps=(value)
        @@cache_asset_timestamps = value
      end

      @@cache_asset_timestamps = true

booleanの値です。
さらにセッターの上に今回の答えがコメントで書かれてますが、
ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false すれば、
毎回 File.mtime(path) してくれるようになります。


なので、initializersとかに、適当なファイルを作って、

# config/intializers/cache_asset_timestamp.rb とか
ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false

だけ書いておけば、production環境でもtouchしたらタイムスタンプが更新されるようになります。


ちなみに、development環境は、config/environments/development.rb で

config.cache_classes = false

になってると思いますが、cache_classesがfalseだと、
ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
が実行されるので、development環境では何もしなくてもタイムスタンプが更新されるっぽいです。