We are a software consultancy based in Berlin, Germany. We deliver
high quality web apps in short timespans.

Upstream Agile GmbH

Pregenerating assets in Rails 2.0 revisited

January 24, 2008 by alex

In a previous post I wrote about how we wanted to pregenerate our packed assets simply by firing up a mongrel on deployemnt and request a page so it would generate the assets by calling the stylesheet_link_tag ... :cache => true and javascript_include_tag ... :cache => true methods in our application.rhtml.

It turned out starting and stopping a mongrel during development wasn’t really a good idea. We ran into all sorts of problems with deployemnts not shutting down the mongrel or not deleting its pid file which made the next deployemnt fails and stuff like that. Plus, to be honest, this really felt like a hack from the beginning.

So what we ended up doing was this: Write a small class that calls the methods to generate the all.js/all.css files and simply call that from our capistrano script. This is how it looks (config/assets.rb):

module Assets
  def stylesheet_filenames
    ['screen', 'screen_profile', 'game', 'screen_classes', 'screen_forms', 'screen_pages', 'redbox']
  end

  def javascript_filenames
    ['prototype', 'effects', 'controls', 'upload_progress', 'prototype_extensions', 'application',
      'redbox', 'dragdrop']
  end
end

The Assets module simply contains our list of stylesheet and css files which we had to pull out of application.rhtml so that we could generate the files without making any requests through the rails stack in order to call the methods in application.rhtml. After including the Assets module into ApplicationHelper the calls in the layout now look like this:

<%= stylesheet_link_tag *(stylesheet_filenames + [{:cache => true}]) %>
<%= javascript_include_tag *(javascript_filenames + [{:cache => true}]) %>

A bit ugly but it works. We have also added a class AssetsGenerator to our assets.rb file:

class AssetsGenerator
  include Assets
  include ERB::Util
  include ActionView::Helpers::TagHelper
  include ActionView::Helpers::AssetTagHelper


  def generate_assets
    stylesheet_link_tag(*(stylesheet_filenames + [:cache => true]))
    javascript_include_tag(*(javascript_filenames + [:cache => true]))
    path = File.join(RAILS_ROOT, 'public', 'stylesheets', 'all.css')
    css = File.read path
    File.open path, 'w' do |f|
      f &lt;&lt; css.gsub(/url\(("?|'?)\/images\//) do |matches|
        "url(#{$1}#{compute_asset_host(rand(1000).to_s).to_s}/images/"
      end
    end
  end
end

This does two things: first it calls the stylesheet_link_tag and javascript_include_tag methods from the AssetTagHelper module to generate the all.js and all.css files. After that it reads in the css file and replaces all ocurrences of url('images/*') with the url of an asset host - it’s easier than teaching our CSS guy to do it by hand, plus we don’t have any hard coded urls in our css files.

We’ve been running this setup for 2 weeks now withour any problems. You could argue that it might have been easier to stick with the old asset_packager plugin but with this solution we have now one less plugin that can cause trouble and we are re-using more of the built in rails stuff than before.