Sep 26, 2011

Running Rails 3.1.1 on Heroku

Heroku officially supports rails 3.1.1 on their Cedar stack now, unfortunately, you may still fail on deploy. The trouble maker is the assets:precompile rake task in rails 3.1. Heroku runs 'rake assets:precompile' for you when deploy, at a time many application dependencies, e.g. database server, redis, are NOT ready, so if there's any database/redis/any-other-not-ready-dependency use in your rails initialization process, assets:precompile will fail.

The tricky thing is, even you know the underlying theory, it's hard to find the exact place caused the problem, because heroku deployment do not use '--trace' flag when running precompile task, the limited error output is almost useless; and rails initialization process walks through so many files it's stupid to check them one by one.

For example, this is the error I got today when I deploy to heroku:

rake aborted!
bad URI(is not URI?):

Can you spot the broken code with so few information? I can't ... Fortunately, we can ask rake to give detail error stack without passing it '--trace' option, by adding this line in your Rakefile:

Rake.application.options.trace = true if %w(staging production).include?(Rails.env)

Then another problem pops up, I now found the broken code, how to fix it? Since the broken code uses something that is not ready when assets:precompile task running, an easy way would be:

# in Rakefile
if %w(staging production).include? Rails.env
$heroku_deploying = true if File.basename($0) == 'rake' && ARGV.include?('assets:precompile')
end

# in some initializers:
# reference to UsersController will trigger operation on database
provider :identity, :fields => [:name, :email, :nickname], :on_failed_registration => UsersController.action(:new) unless $heroku_deploying

With this duct tape kind fix, you may have $heroku_deploying scattered in your code .. it's ugly but works. Let me know if you have better idea :-)