So Rails time stamping is great. I'm using it to add expires headers to all files that end in the 10 digit timestamp. Most of my images however are referenced in my CSS. Has anyone come across any method that allows for timestamps to be added to CSS referenced images, or some funky re-write rule that achieves this? I'd love for ALL images in my site, both inline and in css to have this timestamp so I can tell the browser to cache them, but refresh any time the file itself changes.
I couldn't find anything on the net regarding this and I can't believe this isn't a more frequently discussed topic.
I don't think my setup will matter because the actual expiring will hopefully happen the same way, based on the 10 digit timestamp, but I'm using apache to serve all static content if that matters
I've been using asset packager, and I ended up editing the plugin's compress_css
method to solve this problem. I basically just regex for images in the css, and insert the current timestamp:
timestamp = Time.now.to_s.gsub(/\D/, '')
source.gsub!(/url\((['"])(.+)(['"])\)/) do
open, file, close = $1, $2, $3
if file =~ /.\.(ico|css|js|gif|jpe?g|png)/
"url(#{open}#{file}?#{timestamp}#{close})"
else
"url(#{open}#{file}#{close})"
end
end
That way, whenever I deploy, the compressed css images contain timestamps appended. The downfall with this method is that each image doesn't get its own timestamp, so every time you deploy new css, all of the css images are 'expired'. Better than nothing unless you deploy css often.
You can append the actual timestamp of each image file by getting the file modification time like this:
source.gsub!(/url\((['"]*)(.+)(['"]*)\)/) do
open, file, close = $1, $2, $3
css_dir = File.join(RAILS_ROOT,"public/stylesheets")
timestamp = ''
FileUtils.cd(css_dir) do
if file =~ /^\// # absolute path
f = File.new(RAILS_ROOT + "/public" + file)
else # relative path
f = File.new(file)
end
timestamp = f.mtime.to_i.to_s
end
if file =~ /.\.(ico|css|js|gif|jpe?g|png)/
"url(#{open}#{file}?#{timestamp}#{close})"
else
"url(#{open}#{file}#{close})"
end
end
(There's probably a more elegant way to write this, my ruby chops are still weak!) Now the hack's getting ugly, though... you'd think there would be a more Rails-like way to do this.
The best solution seems to be to use ERB to generate your stylesheets so you can use the Rails image_ helpers instead of direct image paths. Meaning, get rid of your public/stylesheets/application.css file and create app/views/stylesheets/application.css.erb. You'll also have to create a controller and enable caching, and setup the route.
Here is the full details: http://deaddeadgood.com/2009/9/28/far-future-expires-headers-for-css-images-in-rails
In case anyone stumbles upon this, Jammit now supports this out of the box. I've been using jammit on a new project and I'm incredibly impressed!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With