Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails time stamps on images in CSS

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

like image 874
brad Avatar asked Sep 08 '09 13:09

brad


4 Answers

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.

like image 118
Ben Crouse Avatar answered Nov 16 '22 05:11

Ben Crouse


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.

like image 24
Les Nightingill Avatar answered Nov 16 '22 05:11

Les Nightingill


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

like image 3
lamplighter Avatar answered Nov 16 '22 04:11

lamplighter


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!

like image 3
brad Avatar answered Nov 16 '22 03:11

brad