Heroku is a great way to host your Rails apps. Rails 3 is the new hotness. Sass is a decent way to write your stylesheets. Unfortunately combining the three isn’t as straight forward as it could be, but it can be done.
Getting Rails 3 running on Heroku is pretty easy and fairly well documented. Just create an app on their Bamboo stack and you’re pretty much set:
$ heroku create --stack bamboo-ree-1.8.7 mysuperapp
One detail that is documented, but I had missed, is the fact that Rails 3 by default isn’t configured to serve up static assets. Heroku will fix this for you when you push your code to them, but if you want to debug production mode issues locally, you want to change the setting in your production.rb:
config.serve_static_assets = true
Failing to set this caused me a bunch of grey hairs.
Sass on the other hand is a pain to get running. When Sass compiles templates into actual CSS files that can be served to browsers, it writes them to disk. Heroku features a mostly read only filesystem. Those things don’t really go together.
Luckily there is a loophole. The tmp folder is writable, so some smart folks have created a plugin that compiles your Sass templates into the tmp folder and serves them from there using the magic that is Rack middleware.
This works great if you can live with the default locations. Alas, I am picky and I want my Sass templates in app/stylesheets (I believe this is the case if you use Compass as well). I definitely don’t want them - or any of my other source files - in public.
Unfortunately the Hassle plugin doesn’t work with that configuration, as people have found out.
So I set out to fix that, and after a few detours - one involving weird issues with Rack::File - I think I might have finally nailed it. If you install my Hassle fork
$ rails plugin install git://github.com/koppen/hassle.git
and configure your Sass options like so:
Sass::Plugin.options[:template_location] = { 'app/stylesheets' => 'public/stylesheets' }
It should just work on Heroku, using your Sass templates from app/stylesheets and serving them at domain.com/stylesheets/ with a roundtrip through your tmp folder.
I’ve tested this on Rails 3.0.0.beta2, Heroku Bamboo (REE 1.8.7) and Haml 2.2.20 and it works for me.
I recently red a similar problem on Dr. Nic's blog about Coffe Script on Heroku.
But instead of trying to have heroku server generating the file, it's generated automatically at each commit.
I think it's fair to generate your stylesheet locally and then only send it to your server. But maybe I'm missing the point, why do you want the css file to be generated on the server?
Using a pre-commit hook to generate the files on commit is definitely one way to go. It does invoke a rake task on each commit, which might be okay. It also adds build artifacts to your repository and history, which is not really okay. And it forces a different location in production, which is at least confusing.
Leaving it to the server to handle means I don't have to worry about it during development at all, which is great. I just leave it to my deployment stack to handle the setup and artifacts it needs when it needs them.
Thanks. This has saved me hours of work/discovery.
The easiest way of make sass and compass work on heroku is something like this:
Configure your sass or compass options to:
config.css_dir = "tmp/stylesheets/compiled" (compass)
Create initializer which contains:
require "fileutils"
FileUtils.mkdir_p(Rails.root.join("tmp", "stylesheets", "compiled"))
ActionController::Dispatcher.middleware.use(Rack::Static, :root => "tmp/", :urls => ["/stylesheets/compiled"])
This will always compile your styleeshets to tmp.
And that's all, you don't need hassle or sass_on_heroku plugin.
That is definitely a valid way of doing things and it's roughly what Hassle does.
If you prefer doing a little legwork yourself, implement the above. If you're lazy, install the plugin that does it for you.
That said, I encountered what seemed to be issues with Rack::Static in the new Rails beta, so be aware of that.
Jakob,
Your solution worked for me:
Rails 3.0.0.beta3, Ruby 1.9.1, and Compass on Heroku.
Thank you!
I strongly suggest using ruby 1.9.2 instead of 1.9.1 with Rails 3 :-)
Again, thanks for the post, it's exactly what I needed, I might use this for development purposes for a while and wanting to run the app on a server for demo, but when going production I will probably just convert the stylesheets and use .css.
Unfortunately Hassle doesn't seem to be working with the Rails Beta3 and Wojciech's solution doesn't work either. I think the problem is this line:
ActionController::Dispatcher.middleware.use(Rack::Static, :root => “tmp/”, :urls => [“/stylesheets/compiled”])
I have tried a couple of things to get this working but without much luck yet.
Jeroen, use my fork of the Hassle plugin. In your Gemfile:
gem 'hassle', :git => 'git://github.com/koppen/hassle.git'or whatever the correct syntax is.
I played around with both of these solutions and it seems just generating your stylesheet(s) in a controler like so:
class StylesheetsController < ApplicationController
SASS_PATH = Rails.root.join('app','stylesheets')
APPLICATION_TEMPLATE_PATH = File.join(SASS_PATH,'application.sass')
def application
template = File.open(APPLICATION_TEMPLATE_PATH).read
engine = Sass::Engine.new(template, {:load_paths => [SASS_PATH]})
render :text => engine.render, :content_type => "text/css"
end
end
would allow varnish to take care of the caching rather then the tmp files.
I receive an error complaining about Rails.root being nil If I add hassle to my gemfile.
Solved it for now by committing the resulting css files and by using:
Sass::Plugin.options[:never_update] = true
in my production environment. Sass doesn't try to update the css files this way.
Seems to work ...
The best way to use Compass with Rails 3 is to use Wojciech's method. You'll need to add this to your config.ru file:
require ::File.expand_path('../config/environment', FILE)
use Rack::Static, :urls => ["/stylesheets/compiled"], :root => "tmp"
run MyApp::Application