I'd searched internet on this problem, and the answers say that the most common things are:
Rails naming conventions are begin violated. For example: filename is hello_world.rb and the classname is Hello_World instead of HelloWorld.
In config.autoload_paths in application.rb there is no path required.
None of these cases are mine: I got file under /lib/admin_analytics.rb and module name AdminAnalytics. Also I have config.autoload_paths << Rails.root.join('lib) in application.rb.
But in development mode, when I change the code and refresh the page (making request) the
Unable to autoload constant AdminAnalytics, expected /lib/admin_analytics.rb to define it.
error comes. How to fix this? Thanks in advance!
Ruby version 2.6.0
Rails is 5.2.5
The beginning of the module
module AdminAnalytics
def self.compute_numbers(options = {})
require 'admin/compute_numbers'
# ComputeNumbers.debug = true
ComputeNumbers.send(options[:method], options)
end
Related
I am running a Rails application.
app/workers/sample/fetch_book_report_worker.rb
class Sample::FetchBookReportWorker
// body
end
app/workers/sample/fetch_student_report_worker.rb
class Sample::FetchStudentReportWorker
// body
end
app/workers/sample/fetch_teacher_report_worker.rb
class Sample::FetchTeacherReportWorker
// body
end
app/workers/sample/fetch_college_report_worker.rb
class Sample::FetchCollegeReportWorker
// body
end
When I ran the above worker individually, I didn't see an issue. But I ran all the workers at the same time. I got the below error.
LoadError: Unable to autoload constant Sample::FetchStudentReportWorker, expected /home/ubuntu/my-app/app/workers/sample/fetch_student_report_worker.rb to define it
How to solve this?
You can see autoload_paths to type this command.
bin/rails r 'puts ActiveSupport::Dependencies.autoload_paths'
If you can't see .../app/workers/sample, add the autoload_paths in config/application.rb
config.autoload_paths << Rails.root.join("app/workers/sample")
If it could causes problem in production mode, use the 'eager_load_paths'
referece from https://guides.rubyonrails.org/autoloading_and_reloading_constants.html
One likely root cause for the LoadError error is, that as of Rails 5.2.0, there are changes to autoload, that require submodules (namespaces) to (at the least) be defined (as a constant).
In the original post's example, the module Sample should be defined in the app/workers/sample.rb file (as commented by Vasfed above).
Refer to this related SO answer for more details.
I'd like to add a microapp for errors to be processed (file I placed inside the lib folder).
module MyApp
class Application < Rails::Application
config.autoload_paths += %W( #{config.root}/lib )
config.exceptions_app = FooApp.new(Rails.public_path)
end
end
But Rails raises an unitialized constant (NameError) during it's initialization. Recently I found a similar post and tried every solution from there, but got no result. What actually I've tried:
Name a class and a filename according to convention (even tried to simplify to a single word - class Foo, filename lib/foo.rb).
Use config.eager_load_paths += %W( #{config.root}/lib ) rather than config.autoload_paths, same effect. :(
Create an initializer file and load a class with require: require "#{Rails.root}/lib/foo"
It doesn't work - seems initializers are performed after the initial Rails configuration.
Move the file into app/misc but it doesn't help.
Put a class inside a module with the same name, rename a class while it's still in the module - no effect.
The only working solution I found - is to require a file right inside the Rails configuration block, but... it's a freaky solution. Probably there still exists an idiomatic one?
I make a script to try some functions. But I cannot use models in lib. Interesting is, that I already have an lib, and there it works fine with the mostly same code(?).
// script/tags.rb:
require File.expand_path('../../config/application', __FILE__)
require 'company_tags'
host = ARGV[0] || 'team1.crm.tld'
c = CompanyTags.new(host)
c.run
// lib/company_tags.rb
class CompanyTags
def initialize(host)
#site = Site.where(host: host).first
end
def run
comp = #site.companies.first
comp.tag_list.add("tag1")
comp.general_list.add("tag_general")
comp.save!
p comp.tag_list
end
end
Error: /lib/company_tags.rb:3:in `initialize': uninitialized constant CompanyTags::Site (NameError)
You need to require the environment, not the application.
require File.expand_path('../../config/environment', __FILE__)
require 'company_tags'
The environment will load all the dependencies, including the application, and it will bootstrap the application.
Just an idea, but try changing Site.where(host: host).first to ::Site.where(host: host).first. Putting the :: in front, causes ruby to look for Site in the global namespace instead of as a constant defined in CompanyTags.
You can add a simple configuration to config/application.rb:
# Autoload lib/ folder including all subdirectories
config.autoload_paths += Dir["#{config.root}/lib/**/"]
Check this question out for more details.
In my previous answer, I thought you were running rails console. Your main issue is that the Site class is not required. Here is how requiring files to your classes work.
Through the require_all function
require_all(MY_CLASSES_DIRECTORY)
Or by requiring each class manually:
require 'Class_NAME.rb'
Notice that the Site class is not required anywhere.
For more details, check this link
This is a tricky one to explain. I have a module in another module namespace like so:
# app/models/points/calculator.rb
module Points
module Calculator
def self.included(base)
base.send(:include, CommonMethods)
base.send(:include, "Points::Calculator::#{base}Methods".constantize)
end
end
end
So then in other classes all I need to do is:
class User
include Points::Calculator
end
I've specified this directory in application.rb to be autoloadable...(even though i think rails recurses through models...)
config.autoload_paths += Dir[ Rails.root.join('app', 'models', "points") ]
In development env, everything works fine. When running tests(and production env), I get the following error:
Unable to autoload constant Points::Calculator, expected /Users/pete/work/recognize/app/models/points/calculator.rb to define it (LoadError)
I actually followed the advice here to fix the problem: Stop Rails from unloading a module in development mode by explicitly requiring calculator.rb in application.rb.
However, why is this happening??
I stuck some debug output in ActiveSupport's dependencies.rb file and noticed that this file is being required twice. The first time its required I can see that the constant is indeed loaded.
But the 2nd time its required the constant has been unloaded as far as Rails can tell, but when the actual require is called, ruby returns false because ruby knows its already required it. Then Rails throws the "unable to autoload constant" error because the constant still isn't present and ruby didn't "re-require" the file.
Can anyone shed light on why this might be happening?
Rails augments the constant lookup mechanism of ruby.
Constant lookup in Ruby:
Similar to method missing, a Module#constant-missing is invoked when a reference to a constant fails to be resolved. When we refer to a constant in a given lexical scope, that constant is searched for in:
Each entry in Module.nesting
Each entry in Module.nesting.first.ancestors
Each entry in Object.ancestors if Module.nesting.first is nil or a module.
When we refer to a constant, Ruby first attempts to find it according to this built-in lookup rules.
When ruby fails to find... rails kicks in, and using its own lookup convention and its knowledge about which constants have already been loaded (by ruby), Rails overrides Module#const_missing to load missing constants without the need for explicit require calls by the programmer.
Its own lookup convention?
Contrasting Ruby’s autoload (which requires the location of each autoloaded constant to be specified in advance) rails following a convention that maps constants to file names.
Points::Calculator # =>points/calculator.rb
Now for the constant Points::Calculator, rails searches this file path (ie 'points/calculator.rb') within the autoload paths, defined by the autoload_paths configuration.
In this case, rails searched for file path points/calculator in its autoloaded paths, but fails to find file and hence this error/warning is shown.
This answer is an abstract from this Urbanautomation blog.
Edit:
I wrote a blog about Zeitwerk, the new code reloader in Rails. Check it out at -> https://blog.bigbinary.com/2019/10/08/rails-6-introduces-new-code-loader-called-zeitwerk.html
If someone is having this issue in rails 6 which has zeitwerk autoloader,
Change ruby constant lookup back to classic in your application.rb
# config/application.rb
#...
config.autoloader = :classic
#...
Read more details here Rails Official Guides
Calculator should be a class to be autoloaded correctly
module Points
class Calculator
...
end
end
I have module in /lib/models/scopes.rb
module Models
module Scopes
extend ActiveSupport::Concern
...
end
end
I'm trying to include it from model:
class User < ActiveRecord::Base
include Models::Scopes
end
And getting error:
NameError: uninitialized constant User::Models
How to solve this trouble? Maybe it`s wrong to keep this types of files in /lib?
Environment:
Rails v3.1
Ruby v1.9.3
Rails doesn't require files in the lib directory automatically, but you can add to the autoloaded paths in config/application.rb:
config.autoload_paths += %W(#{config.root}/lib)
Restart the server to pick up the new settings.
This will now load the file automatically when the module name is first used. In development mode, you might want to reload the module after every change in order to see the changes without restarting the server. To do that, add it as an eager load path instead:
config.eager_load_paths += %W(#{config.root}/lib)
The scope shouldn't be a problem as long as you don't have a Models class or module within User or anywhere else.
when you define your class, you're "opening" a new scope. So when you do Models::Scopes, ruby is looking for User::Models::Scopes. You can fix this by using ::Models::Scopes, the :: telling ruby to look in the global scope.
FYI: I'm not sure about the terms I used or even if my train of thought if correct; but the solution should be good anyway. I'd think Ruby would try for ::Models::Scope after failing to find User::Models::Scope, but it doesn't.. Maybe there is a User::Models scope defined somewhere? Anyway, as you can see, I'm not yet familiar with those. You might want to dig on the subject if that interests you