Using Sass on Heroku with Hassle
Posted (updated )
A little while ago Lucas Willett and I hacked together Compass, Rails 3 and Heroku without a Hassle. This is a combination of three components:
-
Render compiled CSS to
tmp/stylesheets/
instead ofpublic/stylesheets/
as the file system is read only on Heroku. -
Use
Rack::Static
to mounttmp/stylesheets
on/stylesheets
so they're accessible via their original URL. -
Monkey patch
ActionView::Helpers::AssetTagHelper
to checktmp/stylesheets/
for stylesheets in addition to the default ofpublic/stylesheets/
. This is to ensure cache busting continues to function and you don't unintentionally serve old stylesheets to your users.
The whole reason this workaround came about was that we had some trouble in getting Hassle to work. The problem was around initialisation which wasn't hard to solve in retrospect but I had the hair-brained idea to write the quick workaround above. It worked. The cache busting was an added bonus (of which I would have needed anyway).
One problem though is Rack::Static
does not fall-through if it doesn't find a file. As a result, this solution does not work if you have existing files in public/stylesheets/
you want to serve alongside your Sass stylesheets. Luckily for us this wasn't a problem.
However now I am wanting to use this same setup on an existing project which has a mix of both CSS files and Sass stylesheets. The time for the quick hack is over. I have forked Hassle and added a couple of features from our workaround:
-
Always run Hassle even when not on Heroku. I didn't want to have
public/
polluted with generated files so I set Hassle to run all the time. This keepspublic/stylesheets/
clean in development mode. -
Fix cache busting on Hassle stylesheets. The monkey patch to ensure Sass stylesheets correctly refresh when you redeploy.
Update: I have also made a couple of tweaks to the HTTP response headers to improve the cacheability of the generated stylesheets. By adding a Last-Modified
header to the response, browsers can use a If-Modified-Since
header in their request to get a much smaller 304 response if nothing has changed. Ideally browsers will just use the existing max-age
, but this doesn't happen often enough.
If you'd like to use my fork, add the following to your Gemfile
:
gem 'hassle', :git => 'git://github.com/jasoncodes/hassle.git'
We use Hassle in combination with Compass and it's working great.