NameError: uninitialized constant on Heroku - ruby-on-rails

I have a Rails 5 application with some modules/classes located under /lib. In development I can access those through the rails console like so:
irb(main):001:0> MyClass.do_something
In Production on Heroku I get this:
irb(main):001:0> MyClass.do_something
NameError: uninitialized constant MyClass
As you might have guessed I autoload the /lib directory in my application.rb:
config.autoload_paths << Rails.root.join('lib')
However, the most curious thing about this is, that I can access this class from rake tasks. So something like this works fine:
task do_something: :environment do
MyClass.do_something
end
Which tells me that the class is present on Heroku.
Any ideas?

Rails doesn't autoload in production for thread safety, instead eager loading application constants. You can fix your issue by using the eager_load_paths method instead.
config.eager_load_paths << Rails.root.join('lib')
If you still want to autoload in development you can make it conditional
load_path_strategy = Rails.env.production? ? :eager_load_paths : :autoload_paths
config.public_send(load_path_strategy) << Rails.root.join('lib')
If you really need autoloading of this directory in production you can set enable_dependency_loading to true.
config.enable_dependency_loading = true
config.autoload_paths << Rails.root.join('lib')
See this blog post for more explanation.

I have been puzzled why all of my objects were uninitialized constant in console on heroku in production. In my local production, they were fine.
Turns out that the problem was that I was running: "heroku run console" not "heroku run rails console".
Worth noting that when you access console from the heroku website, the same problem occurs there too. Wasted a lot of time on this.

Related

Heroku Production: config.autoload_paths doesnt seem to work

I have a Rails application that works just fine in development. It accesses a class I have in my /lib folder due to the
config.autoload_paths += Dir["#{config.root}/lib/**/"]
line in my application.rb config file. However, when I run the application in production on Heroku, I get an Uninitialized Constant error.
For example running rails c locally:
>rails c
001> OrderPdf
=> OrderPdf
002>
but on production:
>heroku run rails c
001> OrderPdf
NameError: uninitialized constant OrderPdf
(stack trace)
002>
Whats going on here?
its because on prod you probably have this line in your config config.eager_load = true
, which loads all of your classes once before hand.
To fix this, everything that you add to the autoload_path, add to the eager_load path as well
config.eager_load_paths += Dir["#{config.root}/lib/**/"]

Loading initializers during assets precompile

I am trying to precompile the assets while deploying to production environment. I am also trying to automate it for first time installation on a server using capistrano. It looks like none of the initializer's are getting loaded during the assets precompile process.
I am facing these two problems
For the first time installation I am generating a initializer file(initializers/freshinstall.rb) on the fly with the below content
config.assets.initialize_on_precompile = false
so that precompile doesn't check the database which doesn't exist yet
I also have some vendor files, and their locations are set in the asset pipeline, and are placed in separate initializer file initializers/vendor.rb
MyApp::Application.config.assets.paths << "#Rails.root}/vendor/assets/images/xxxx" << "# {Rails.root}/vendor/assets/images/xxxx/helpers"
MyApp::Application.config.assets.paths << "#{Rails.root}/vendor/assets/stylesheets/xxxx"
MyApp::Application.config.assets.paths << "#{Rails.root}/vendor/assets/stylesheets/yyyy" << "#{Rails.root}/vendor/assets/images/yyyy"
When capistrano runs the assets precompile task, its not able to find the vendor paths or stopping it from looking into the database. This brings me to a conclusion that the initializer's are not getting loaded. After going through some stackoverflow questions. I even added a railtie to config/application.rb
module AssetsInitializers
class Railtie < Rails::Railtie
initializer "assets_initializers.initialize_rails",
:group => :assets do |app|
require "#{Rails.root}/config/initializers/freshinstall.rb"
require "#{Rails.root}/config/initializers/vendor.rb"
end
end
end
But I still don't see any initializers getting loaded. Can I get some info on the internals of the boot process of an rail application, and also why the initializers are not getting loaded during the assets precompile process.
Some documentations would be really helpful to understand this. The Rails documentation is very minimal with respect to railtie and the initializer method.
http://guides.rubyonrails.org/configuring.html
Thank you in advance
Finally was able to figure out how to do it. This works for my single server capistrano deployment. Below is the capistrano task for my new_deploy on to a fresh server.
set :fresh_install, false
task :new_deploy do
set :fresh_install,true
deploy.setup
#The assets:precompile process is part of the deploy.update. Before the precompile process, we will create database.
deploy.update
deploy.migrate
run_seed
#load unicorn server
end
Dont do any configuration setting mentioned below
config.assets.initialize_on_precompile = false # no need of this in application.rb
Instead just before assets:precompile create your database
before "deploy:assets:precompile" , "yourapp:create_database"
See that you check if the fresh install flag is set, then only create the database.This method will also be called during regular deploy when ever you are updating the server with latest revision. During that scenario fresh_install flag will be false.
desc 'Create a new database'
task :create_database, :roles => :app do
if fresh_install
run "cd #{release_path}; bundle exec rake db:create RAILS_ENV=#{rails_env}"
end
end

Heroku not running migrations due to ruby module

I am unable to run migrations in Heroku, which I believe is due to a module I created in my lib directory. After executing the command heroku run rake db:migrate I receive the below error:
uninitialized constant ApplicationController::PgTools
/app/app/controllers/application_controller.rb:4:in <class:ApplicationController>
Line 4 of the Application controller is include PgTools, which is there to gain access to methods within the PgTools module I created.
Despite the heroku migration failing, I am able to run rake db:migrate in my local dev environments without fail (please note that both environments utilize postgres databases).
I also have the following two lines in my application.rb file
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
I resolved the error by renaming Pg_Tools.rb to pgtools.rb and modifying all include PgTools statements to include Pgtools
Links that I used in the troubleshooting process are shown below
Rails 3 library not loading until require
http://www.williambharding.com/blog/technology/rails-3-autoload-modules-and-classes-in-production/

uninitialized constant during rake assets:precompile for lib class

Rails 3.2.2
When running rake assets:precompile I get the following error:
uninitialized constant Redirect
Redirect is a custom middleware class that redirects naked domain requests from mydomain.com to www.mydomain.com.
I load the middleware in production.rb using:
config.middleware.use Redirect
The redirect.rb is located at lib/middleware/redirect.rb. I load the path in application.rb using:
config.autoload_paths += %W(#{config.root}/lib/middleware)
It works fine when you run the application, and other rake tasks run fine. But running rake assets:precompile appears to not load the lib properly. I first noticed the issue running on Heroku, but I've been able to reproduce locally no problem.
Any ideas? Thanks!
You probably have config.assets.initialize_on_precompile = false set somewhere.
I experienced this error after setting that configuration for something related to Heroku. To fix, i just require "#{Rails.root}/lib/my_middleware.rb" right above the line where I configure the app to use the middleware.
I was getting the same error for loading a class from /lib and assigning it to a ::GLOBAL variable.
This was because I had forgotten to place it inside an after_initialize block, which is how I had done it in development.
config.after_initialize do
::GLOBAL = MyLib::MyClass.new
end
Hope this helps somebody!

Rails Model Not Updating on Capistrano deployment

Alright so I've spent considerable amount of time with this and still can't find out what's wrong.
Here's whats going on: I've got a model reward.rb with a method X like this:
class Reward < ActiveRecord::Base
def x
puts "foo" # Method does something...
end
end
The rails app is now on a production environment, in development, if you do
rails c
and then
>> r = Reward.new
>> r.x
"foo" # I.e it works...
now, if you go into the server and do
rails c
and then
>> r = Reward.new
>> r.x
NoMethodError: undefined method `x' for #<Reward:0x0000000561fa08>
If you check reward.rb in the same server you'll see the method's there, actually it's the first method in the file... so, as I can see Rails is not loading the latest code of the model when it loads the console...
I thought it might have something to do with Rails' cache but production.rb says:
MyApp::Application.configure do
config.cache_classes = false
config.action_controller.consider_all_requests_local = false
config.action_controller.perform_caching = false
# SMTP settings and other stuff.. not related to caching...
end
And environment.rb:
# Load the rails application
require File.expand_path('../application', __FILE__)
# Initialize the rails application
MyApp::Application.initialize!
And application.rb:
module MyApp
class Application < Rails::Application
config.encoding = "utf-8"
config.filter_parameters += [:password]
end
end
So I've ran out of ideas... The app was deployed with capistrano, I'm using Rails 3.0.10.
UPDATE
Alright, so I figured out the problem, pretty silly to be honest, there was a models.bak folder inside app/ that someone created, on production the models were getting picked up from that folder
Have you run database migrations on your deploy server?
You may need to set up a capistrano recipe to do this. I believe an out-of-date database schema could cause this error.
A stupid question: have you checked the reward.rb file on the server? Is it the correct (new) version?
Try to restart unicorn o what ever you have manually.
you have to do rails c production in order to run the production environment.

Resources