Heroku - Uninitialized constant for importing a module - ruby-on-rails

I have a module
lib/Basicstats.rb (module Basicstats ...etc. end)
I am importing this into a model
class Vote < ActiveRecord::Base
include Basicstats
#additional class code etc.
end
I grep-d for the module and 'Basicstats' is only referenced in Basicstats.rb and app/model/vote.rb.
This works fine for my local development. But during my Heroku deployment I am getting this error and it can't seem to recognize the module? (I'm also curious how this is working in my local development without a require anywhere.)
2015-03-28T22:19:52.714077+00:00 app[web.1]: /app/app/models/vote.rb:16:in `<class:Vote>': uninitialized constant Basicstats (NameError)

It sounds like your module isn't being explicitly required or auto-loaded by Rails (this will/won't happen depending which version of Rails you're using and how config.autoload_paths is configured).
Your best bet is to add an initializer which explicitly requires your module:
# config/initializers/basicstats.rb
require Rails.root.join('lib/basicstats')

Related

Rails 6: Zeitwerk::NameError doesn't load class from module

I have a file which looks like this
#app/services/account/authenticate/base.rb
module Account
module Authenticate
AuthenticateError = Class.new(StandardError)
class Base < ::Account::Base
def self.call(*attrs)
raise NotImplementedError
end
end
end
end
Now when I will run the code from rails c I have an error
> ::Account::Authenticate::AuthenticateError
=> NameError (uninitialized constant Account::Authenticate::AuthenticateError)
> ::Account::Authenticate.constants
=> [:Base, :ViaToken]
So rails doesn't see AuthenticateError class. But when I will create a nested class from this folder like
=> Account::Authenticate::ViaToken
> ::Account::Authenticate.constants
=> [:Base, :AuthenticateError, :ViaToken]
AuthenticateError class is now visible
> ::Account::Authenticate::AuthenticateError
=> Account::Authenticate::AuthenticateError
The solution for this problem is to create a separate file authenticate_error.rb which will work from the beginning but this solution is not ideal for me. Is there any solution to preload all classes or smth?
(Ruby 2.6 with Rails 6.0.0.rc2)
I experienced this same issue when deploying a Rails 6.0.2 application to an Ubuntu 18.04 server.
Unable to load application: Zeitwerk::NameError: expected file /home/deploy/myapp/app/models/concerns/designation.rb to define constant Designation, but didn't
I found out that the issue was with zeitwerk. Zeitwerk is the new code loader engine used in Rails 6. It’s meant to be the new default for all Rails 6+ projects replacing the old classic engine. Zeitwerk provides the features of code autoloading, eager loading, and reloading.
Here's how I solved it:
Navigate to the config/application.rb file on your project.
Add this line within your application module to switch to classic mode for autoloading:
config.autoloader = :classic
Here's an example:
module MyApp
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.0
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.
config.autoloader = :classic
end
end
You can read up more on zeitwerk in this article: Understanding Zeitwerk in Rails 6
updating a Rails app from 5.2 to 6.0 and also hit the Zeitwerk bump!
If you want to continue to use the autoloading mode you're currently using, avoiding Zeitwerk, then add this line to your application.rb file (#PromisePreston answer and Rails doc)
config.autoloader = :classic
If you want to upgrade to Zeitwerk then a command to use is bin/rails zeitwerk:check (From this guide article).
Scenario we hit closest to this particular question was where we had a file within a subfolder like this:
#presenters/submission_files/base.rb
module Presenters
module SubmissionFiles
class Base < Showtime::Presenter
def method_call
#code_here
end
end
end
end
Removing an extra modules to have:
#presenters/submission_files/base.rb
module Presenters
class SubmissionFiles::Base < Showtime::Presenter
def method_call
#code_here
end
end
end
Then, when calling the method in other ruby files in the app use: Presenters::SubmissionFiles::Base.method_call
zeitwerk follow conventional file structure which is able to load your project's classes and modules on demand (autoloading) as long as you follow it's rule.
# service/account/authenticate/base.rb
module Account
module Authenticate
puts "load service ....."
AuthenticateError = Class.new(StandardError)
class Base
end
end
end
::Account::Authenticate::AuthenticateError # uninitialized constant
::Account::Authenticate::Base # load service ....
::Account::Authenticate::AuthenticateError # OK
as you can see, the first time you attempt to reach the constant AuthenticateError, the log load service ... does not show, that because you don't play zeitwerk rule:
whenever it gets a request to load the const ::Account::Authenticate::AuthenticateError, first it'll check and return if that constant already loaded, otherwise it'll look for the file /account/authenticate/authenticate_error.rb which corresponding to the constant ::Account::Authenticate::AuthenticateError to find that constant definition, but it could not find it.
on step 2, when you call ::Account::Authenticate::Base, it could find the file /account/authenticate/base.rb and load it, during this time, it's also load the constant AuthenticateError which is defined on that file, now we have the constant ::Account::Authenticate::AuthenticateError, and of course, it's OK on step 3.
now let try to play with the zeitwerk rule, i create a file /account/authenticate/authenticate_error.rb as below
# service/account/authenticate/authenticate_error.rb
module Account
module Authenticate
puts "load error ....."
AuthenticateError = Class.new(StandardError)
end
end
and try to attempt that constant at the step 1
$ spring stop
$ rails c
> ::Account::Authenticate::AuthenticateError
load error .....
=> Account::Authenticate::AuthenticateError
it worked since zeitwerk found the file account/authenticate/authenticate_error.rb. (note that the file name /____authenticate_error.rb still work)
my thought: i think you could work safely with the constant AuthenticateError inside the module ::Account::Authenticate, in case of you want to expose those error constants to outside, you could create file /account/authenticate/error.rb
# service/account/authenticate/error.rb
module Account
module Authenticate
module Error
AuthenticateError = Class.new(StandardError)
end
end
end
then you could access ::Account::Authenticate::Error::AuthenticateError, in my opinion, it even clearer than put AuthenticateError inside base.rb.
I was able to fix it by not trying to fight Rails 6. Zeitwerk autoloads certain expected folders, which includes app/models, app/controllers, app/helpers and others.
I created a folder app/helpers and moved my services folder into it.
That's it!

Include a custom module on Spree and Rails

I'm trying to add my module to my class_eval on Spree.
This is located on: lib/spree/core/app/models/spree/payment/processing.rb
Tried with the following:
module Spree
Payment.class_eval do
require GatewayError
end
end
I am trying to include the following located on: lib/spree/error_override.rb
module Spree
module GatewayError
end
end
The error I'm getting when I try to load the server is:
`block in <module:Spree>': uninitialized constant Spree::GatewayError (NameError)
Its my first time trying to include my own module to a class, would be awesome if someone can point me in the right direction.
Thank you in advance!
One solution would be to manually require the file during initialization process.
config/initializers/require.rb:
# put here all files that you want to require manually
require "#{Rails.root}/lib/spree/error_override.rb"
And that's it - your module is now ready to use ;)

Rails unable to autoload constant from file despite being defined in that file

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

Rails include module in model trouble

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

Rails 3 problem loading classes from lib in config/environments/development.rb

I'm working on upgrading a Rails 2.3.11 app to 3.0.10. I'm getting a NameError in my development.rb file, when I try to run any of the rails scripts like rails console, or run my unit tests.
I'm calling a class that I have defined in lib, but it seems that the library hasn't been loaded when development.rb calls the class.
I'm doing something like:
config.cache_store = CustomMemcachedStore.new(Memcached.new(...))
I have a file lib/custom_memcached_store.rb that declares the class
class CustomMemcachedStore < ActiveSupport::Cache::Store
I'm getting the following error:
~/.rvm/gems/ruby-1.9.2-p290/gems/rake-0.8.7/lib/rake.rb:2503:in `const_missing': uninitialized constant CustomMemcachedStore (NameError)
from ~/app_name/config/environments/development.rb:20:in `block in <top (required)>'
In application.rb, I am already using
config.autoload_paths += Dir["#{Rails.root}/lib"]
Thanks for any help you can give me.
You will need to require the file explicitly, rather than relying on autoload.
This is because the loading of the environment config happens early on in the startup process before autoload paths are set up.
In some cases you can work with initializers to insert configuration code into a place that works, via something like:
initializer "my_setup", :before => "some_other_setup" do |app|
# ...
end
Unfortunately, this is not one of those cases, as the cache is set up here, while the autoload paths are not set up until here, immediately before the boostrap_hook.

Resources