Space Vatican

Ramblings of a curious coder

Introducing Asset_symlink

Rails 4 made asset pipeline precompiling now lightning fast compared to rails 3.x. There were two reasons (that I am aware of) for the slowness in rails 3:

  • it would regenerate all the assets, each time (you can fix this via the turbo-sprockets-rails3 gem).
  • it used to precompile everything twice: once with the digests (the checksums in the filename) and once without.

The precompilation happened twice because your assets can reference other assets (eg a stylesheet with some image or font urls) so the difference between the digest assets and the non digest assets wasn’t just the filename.

This is great nearly all of the time - as long as you’re using the rails helpers, it doesn’t make the slightest bit of difference. However there are some cases where it’s a nuisance. At Dressipi we had 2 cases:

  • A few static pages (eg 500.html) where we wanted to use an image asset like a logo. We use these same assets elsewhere in the app so it would have been a bit annoying to have a copy in public and a copy in images. At least for us, this is just a minor inconvenince
  • Javascript we make available to 3rd parties. One of the things Dressipi does is allow clothing retailers to embed information in their product pages. When you pull in twitter functionality to your side you load platform.twitter.com/widgets.js and it’s basically the same with Dressipi.

The second one is the dealbreaker for us - you don’t want to have to tell people to embed platform.twitter.com/widgets-abc1346e6fa2c9a3c6ef5c16fe198.js and keep changing those instructions whenever the file (and thus its digest changes). It’s much simpler to tell people to just link to platform.twitter.com/widgets.js and set the caching headers to a reasonable compromise.

For this case we only care about the entry point to our javascript, so all we want to do is alias widgets.js to platform.twitter.com/widgets-abc1346e6fa2c9a3c6ef5c16fe198.js. If we were doing this with css (we aren’t), we’d also be happy for that to contain links to digested assets: again, we just want to make life easier to people embedding our functionality, so all we need to do is alias those top level files.

Solutions

In terms of solutions we had a few things on our wishlist:

  • no extra steps on deploys
  • assets should still be served by the webserver (in fact via cloudfront), not rails
  • easy to setup/configure

The result of this is the asset_symlink gem. It hooks onto the assets:precompile rake task and sets up the symlinks you ask for. Once you’ve deployed it’s out of the picture - your webserver will still serve these assets for you.

Other than adding to you Gemfile, a you have to do is define in application.rb what you want symlinked (or you could do it in the environment specific files but then we’d be duplicating it across staging & production). For example in one of our apps we have

config.asset_symlink = ['widget.js', 'layouts/powered_by_dressipi.png']

Which does what you would expect: you’ll be able to request /assets/widget.js and get the current digested version of the asset.

You can also set this ‘public’ name to be different to the internal name, for example

config.asset_symlink = [{'widget.js' => 'v1/all.js'}, 'layouts/powered_by_dressipi.png']

sets up a symlink so that http://example.com/assets/v1/all.js will retrieve the current version of widget.js. There are some more examples at the github repo.