Stephen Celis
Quickly version your styles and scripts 5 Apr
When you add a Expires header to your assets, you expect a performance gain, but something doesn’t add up.
In theory, Rails has asset versioning built in—each asset is identified by its modification date (seconds since the Unix epoch) in a query string appended to its path, e.g,
<link href="/stylesheets/application.css?1209513600" ... />.
You’d expect that as long as the server is properly configured, browsers should cache assets appropriately and serve up new content when that time-stamp changes. But this doesn’t follow the W3C standard, and we can’t expect browsers to flout conventions for our convenience. Some may never cache our assets. (Of course, this is to accept the theory that a browser should—and will—follow this convention; most of them do not, and some that once did no longer do.)
We can nullify the query string with an environment variable—adhering to Expires convention—and use the :cache option of stylesheet_link_tag and javascript_include_tag to version our styles and scripts appropriately. Here’s an initializer to do just that (and we can be a bit more lightweight and hands-off than Asset Packager):
config/initializers/set_mtimestamps.rb:
# Defines STYLESHEETS_MTIME and JAVASCRIPTS_MTIME for versioning assets.
#
# <%= stylesheet_link_tag :all, :cache => STYLESHEETS_MTIME %> # =>
# <link href="/stylesheets/1209513600.css" media="screen" rel="stylesheet"
# type="text/css"/>
#
# <%= javascript_include_tag :all, :cache => JAVASCRIPTS_MTIME %> # =>
# <script type="text/javascript" src="/javascripts/1209513600.js"></script>
ENV['RAILS_ASSET_ID'] = '' # Cancel the query string
{ :css => 'stylesheets', :js => 'javascripts' }.each do |key, value|
mtimes = Dir["#{RAILS_ROOT}/public/#{value}/*.#{key}"].map &File.method(:mtime)
Kernel.const_set "#{value.upcase}_MTIME", mtimes.max.to_i.to_s
end
The only thing to keep in mind is that this doesn’t cover the rest of your assets. If you replace an image and it needs to be freshly served, make sure to rename it, as well.