开发者

Static asset caching on Heroku with Jammit by changing ActionController::Base#page_cache_directory

I'm attempting to use Jammit for packaging CSS and JS for a Rails app deployed on Heroku, which doesn't work out of the box due to Heroku's read only file system. Every example I've seen of how to do this recommends building all the packaged asset files in advance. Because of Heroku's Git-based deployment, this means you need to make a separate commit to your repository every time these files change, which is not an acceptable solution to me. Instead, I want to change the path that Jammit uses to write the cached packages to #{Rails.root开发者_JAVA百科}/tmp/assets (by changing ActionController::Base#page_cache_directory), which is writable on Heroku.

What I don't understand is how the cached files will be used without hitting the Rails stack every time, even using the default path for cached packages. Let me explain what I mean:

When you include a package using Jammit's helper, it looks something like this:

<%= include_javascripts :application %>

which generates this script tag:

<script src="/assets/application.js" type="text/javascript"></script>

When the browser requests this URL, what actually happens is that it gets routed to Jammit::Controller#package, which renders the contents of the package to the browser and then writes a cached copy to #{page_cache_directory}/assets/application.js. The idea is that this cached file is built on the first request, and subsequent requests should serve the cached file directly without hitting the Rails stack. I looked through the Jammit code and I don't see how this is supposed to happen. What prevents subsequent requests to /assets/application.js from simply routing to Jammit::Controller again and never using the cached file?

My guess is that there's a Rack middleware somewhere I'm not seeing that serves the file if it exists and forwards the request on to the controller if it doesn't. If that's the case, where is that code? And how would it work when changing ActionController::Base#page_cache_directory (effectively changing where Jammit writes cached packages)? Since #{Rails.root}/tmp is above the public document root, there's no URL that maps to that path.


Great question! I haven't set this up myself, but it's something I've been meaning to look into, so you've prompted me to do so. Here's what I would try (I'll give a shot myself soon, but you are probably going to beat me to it).

config.action_controller.page_cache_directory = "#{Rails.root}/tmp/page_cache"

Now change your config.ru to:

require ::File.expand_path('../config/environment',  __FILE__)
run Rack::URLMap.new(
   "/"       => Your::App.new,
   "/assets" => Rack::Directory.new("tmp/page_cache/assets"))

Just make sure not to have anything in public/assets, since that won't ever be picked up.

Notes:

  • This is for Rails 3. Not sure of the solution under Rails 2.
  • It looks like Rack::Directory sets cache control headers to 12 hours so Heroku will cache your assets to Varnish. Not sure if Jammit sets this in its controller, but even if it doesn't, it will be cached quite quickly.
  • Heroku also sets ENV['TMPDIR'] now as well, so you can use that instead of Rails.root + '/tmp' if you wish.


This might be of use, it's for a different gem but the idea is similar and I'm trying to get it working with the plain asset helpers.

http://devcenter.heroku.com/articles/using-compass

Unfortunately it seems to be quite difficult to get rails to do this without patching/rewriting the asset helpers module (which resembles coupled spaghetti).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜