35

I'm deploying a Rails 3.1 app to Heroku's Cedar stack. With Heroku Cedar and Rails 3.1, you can compile the assets yourself locally, let Heroku compile them when you push (during "slug compilation"), or have them be compiled just-in-time while the app is running. I want to do the middle option, letting Heroku precompile the assets.

When Heroku runs the assets:precompile task, it errors with "could not connect to server" because the app is trying to connect to the database but no database is available at that stage of slug compilation. The lack of database connection is expected and unavoidable at this point. I'm looking for a way to move past it, because a database connection isn't crucial to asset precompilation.

The part of my app that's trying to connect to the database is Devise. There's a devise_for :users line in routes.rb that wants to look at the User model.

I could just write a rake task that stubs out devise_for and make it a prereq of assets:precompile. I think that would solve my problem, but I'm looking for a more universal solution that I could use on any Rails 3.1 app with this problem on Heroku.

Is there anything out there, or can you conceive of anything that silences database connection errors while still running the app enough to have route and asset path generation?

Obviously if an app needs to read/write data during startup, we can't stub that, but can we fake every ActiveRecord model automatically?

jasongarber
  • 2,136
  • 2
  • 18
  • 12

10 Answers10

47

add this to config/application.rb

config.assets.initialize_on_precompile=false                                                  

took me a while to hunt this down... adding it to config/environments/*.rb did NOT work

UPDATE: It doesn't work with rails 4

jigfox
  • 18,057
  • 3
  • 60
  • 73
fringd
  • 2,380
  • 1
  • 18
  • 13
  • 1
    it seems that if you @import('compass') in your main sass file (to get all the compass mixins) this does not work. it errors out with 'File to import not found or unreadable: compass.'. require 'compass' is included in the application.rb file but if the environment is not loaded that does not get loaded. does anyone know of a way around this? – Marc Nov 14 '11 at 18:31
  • 3
    This is also cited in official Rails guide about assets pipeline: http://guides.rubyonrails.org/asset_pipeline.html#precompiling-assets – Giovanni Cappellotto Dec 01 '11 at 10:42
  • Worked for me too - using Oracle. – ryan Mar 21 '12 at 14:50
  • This is the proper way. Just make sure that any assets not declared in a manifest are added to the precompile array like [this](http://stackoverflow.com/a/7541958/963396). The answer that uses `heroku labs:enable user-env-compile` also works, but since that feature is experimental, I wouldn't use it for the time being. – XåpplI'-I0llwlg'I - Jan 09 '13 at 09:21
  • This worked for me as well, the key was "adding it to config/environments/*.rb did NOT work". Instead add it to `application.rb` as stated in the answer. – Ross Allen Apr 22 '13 at 21:00
  • jigfox says it doesn't work in rails4, can anybody else confirm this? – fringd Feb 25 '14 at 01:16
13

Heroku now makes a labs flag available that'll make the runtime environment available during compilation time, which means your app will be able to successfully connect to your DATABASE_URL database.

First you need to install the labs plugin:

$ heroku plugins:install http://github.com/heroku/heroku-labs.git

then enable the user-env-compile labs feature:

$ heroku labs:enable user-env-compile --app your-app-name
XåpplI'-I0llwlg'I -
  • 21,649
  • 28
  • 102
  • 151
kch
  • 77,385
  • 46
  • 136
  • 148
  • Of all of the suggestions on this page, this is the only thing that worked for me. FYI: Rails 3.2.0.rc2 – Kevin Moore Jan 12 '12 at 14:32
  • Finally, a working solution. I wish Heroku would document this feature on their Rails troubleshooting page. – mshafrir Feb 10 '12 at 09:27
  • This is the answer! If you have any gem that tries to access the db unconditionally (devise? active_admin? etc) then this is the only solution that actually works. Thanks a ton. – trisweb Nov 02 '12 at 19:22
5

For me the problem is activerecord calling instantiate_observer in lib/active_record/railtie.rb:92. This will load the observers and the respective models. has_and_belongs_to_many then connects to the db.

I think I'll override this method when ENV["RAILS_ASSETS_PRECOMPILE"] is present, which is used by devise in the fix Bradley linked to.

EDIT: So this snippet fixed it for me:

namespace :assets do
  # Prepend the assets:precompile_prepare task to assets:precompile.
  task :precompile => :precompile_prepare

  # This task will be called before assets:precompile to optimize the
  # compilation, i.e. to prevent any DB calls.
  task 'precompile_prepare' do
    # Without this assets:precompile will call itself again with this var set.
    # This basically speeds things up.
    ENV['RAILS_GROUPS'] = 'assets'

    # Devise uses this flag to prevent connecting to the db.
    ENV['RAILS_ASSETS_PRECOMPILE'] = 'true'

    # Prevent loading observers which will load the models which in turn may hit
    # the DB.
    module ActiveModel::Observing::ClassMethods
      def instantiate_observers; end
    end

    # Prevent route drawing because certain gems might get called which will hit
    # the DB.
    class ActionDispatch::Routing::RouteSet
      def draw; end
    end
  end
end
fphilipe
  • 9,739
  • 1
  • 40
  • 52
4

Workaround for Rails (4.2 edge):

Add the following as /config/initializers/precompile.rb:

module Precompile

  # Public: ignore the following block during rake assets:precompile
  def self.ignore

    unless ARGV.any? { |e| e == 'assets:precompile' }
      yield
    else
      line = caller.first
      puts "Ignoring line '#{line}' during precompile"
    end

  end

end

and use it in your routes.rb like this:

Precompile.ignore { ActiveAdmin.routes(self) }
yawn
  • 8,014
  • 7
  • 29
  • 34
3

EDIT: This answer is out of date and no longer works - See fringd's answer.

Not quite a universal stubbing but devise has added a check now to fix this particular problem . See the issue and fix on Github. By providing a RAILS_ASSETS_PRECOMPILE environment config devise should skip building the routes

Bradley Priest
  • 7,438
  • 1
  • 29
  • 33
  • Awesome! Thanks for alerting me to this. I've added to my ticket with Heroku that they should set this environment variable if they don't already. – jasongarber Sep 29 '11 at 17:35
  • 2
    Sorry, I think this fix has actually been pulled back out of Devise and Rails, Jose Valim did a bit of fancy footwork to try and get around the problem but I think he decided it wasn't something they could do too much about at this point in time. If it's still broken with the Devise HEAD and Rails 3-1-stable branch, I'm afraid I don't think there's anything else to do apart from working through your devise models and trying to pinpoint which specific method is causing the problems. – Bradley Priest Sep 30 '11 at 23:37
  • 1
    Thanks for the update. Heroku told me they're working on the problem from a different angle too. Guess we just have to wait for it to shake out. I have a workaround for my own situation. – jasongarber Oct 01 '11 at 14:34
1

I stuck this in 'lib/tasks/assets.rake' and was able to get assets:precompile to actually succeed. This should work as long as you don't actually access the database as a result of requiring your environment. It obviously won't help with ActiveRecord, but it should work for all mongoid-based apps.

task 'assets:precompile' => 'assets:stub_mongoid'

task 'assets:stub_mongoid' do
  def Mongoid.load!(*args)
    true
  end
end
bdimcheff
  • 298
  • 2
  • 6
1

Heroku added an unofficial flag to make the environment (i.e. also the DB) accessible during precompilation. Just ask them to switch it on and DB dependencies during asset precompilations are no longer an issue. Not sure, if/when this flag is officially available though, or if it will simply be the new default.

Nico
  • 881
  • 1
  • 6
  • 19
  • Thanks, Nico. I was helping a friend with this yesterday and we sure could have used it! I think this will help a lot of people. Cheers! – jasongarber Nov 15 '11 at 09:33
  • you can now enable this flag yourself. I'll post an answer about it. – kch Jan 03 '12 at 05:46
0

I lack sufficient reputation to comment, so here's another answer.

It's true that @fringd's top-rated answer does not work on Rails 4. I have, however, found this technique to work:

https://iprog.com/posting/2013/07/errors-when-precompiling-assets-in-rails-4-0

Although, I rearranged the BASH variables like so:

~$ RAILS_ENV=production DATABASE_URL=postgresql://user:pass@127.0.0.1/dbname bundle exec rake assets:precompile

BTW, This is a fantastic aid if you need to build a Docker image. Put that line into your Dockerfile so your DB can live in a different container and your app containers don't need to precompile assets every time they start up!

dayer4b
  • 978
  • 14
  • 26
0

Spork.trap_method is also an interesting solution to the problem of Devise's routes_for calling the model early in the load process. Solution can't be applied directly AFAIK, but it's solving the same sort of problem, so it might provide inspiration for somebody.

Spork.trap_method

jasongarber
  • 2,136
  • 2
  • 18
  • 12
-1

Disable AR:

config = Rails.application.config
def config.database_configuration
  {}
end

ar = ActiveRecord::Base
def ar.establish_connection
end
grosser
  • 14,707
  • 7
  • 57
  • 61