Uninitialized constant after upgrade rails 5.2 to 6.0 - ruby-on-rails

After upgrading my RoR app to 6.0, I'm getting this error:
NameError: uninitialized constant Sites::NetworkResolver::ExternalApi. This is my app/resolvers/sites/network_resolver.rb file:
module Sites
class NetworkResolver
ExternalApi::Graphql
...
end
end
and my app/services/external_api/graphql.rb
module ExternalApi
module Graphql
...
end
end
in the app/services folder, I have a settings file with the same name external_api.rb
module ExternalApi
...
end
rails zeitwerk:check
rails aborted!
NameError: uninitialized constant Sites::NetworkResolver::ExternalApi
If I put it at the top of app/resolvers/network_resolver.rb file
require 'external_api/graphql'
I need to update externalApi to externalAPI so that Api is uppercase
app/services/external_api/graphql.rb to define constant ExternalAPI::Graphql, but didn't
so code works but didn't want to update more than 100 files

By default, app/resolvers/network_resolver.rb should define NetworkResolver, rather than Sites:: NetworkResolver, maybe the path is mistaken?
You can force inflection for external_api to not use the acronym this way:
# config/initializers/zeitwerk.rb
Rails.autoloaders.main.inflector.inflect("external_api" => "ExternalApi")

Related

Zeitwerk doesn't requires lib classes properly in Rails 6.1.6

I'm trying to update my app from Rails 5.2 to 6.1, and use Zeitwerk autoload, and I am having some issues for autoloading /lib classes.
I included this in config/application.rb:
config.load_defaults 5.2
config.autoloader = :zeitwerk
config.enable_dependency_loading = true
config.autoload_paths += Dir[Rails.root.join('lib/**/*')]
config.eager_load_paths += Dir[Rails.root.join('lib/**/*')]
And, when I run zeitwerk:check, it alerts me that:
Hold on, I am eager loading the application.
expected file lib/facades/coconut/v2/foo.rb to define constant Foo
It is declared as:
# /lib/facades/coconut/v2/foo.rb
module Coconut
module V2
class Foo
# ...
end
end
end
When I try to debug it (putting a binding.pry in some test file), Zeitwerk says it do not defined, until I call it without modules (namespaces):
[1] pry(#<#filename>)> Coconut::V2::Foo
NameError: uninitialized constant Coconut::V2::Foo
from (pry):1:in `setup`
[2] pry(#<#filename>)> Foo
Zeitwerk::NameError: expected file /my-app/lib/api/coconut/v2/foo.rb to define constant Foo, but didn't
from /usr/local/bundle/gems/zeitwerk-2.5.4/lib/zeitwerk/loader/callbacks.rb:25:in `on_file_autoloaded'
[3] pry(#<#filename>)> Coconut::V2::Foo
=> Coconut::V2::Foo
It sounds really strange, because there are another classes in /lib that follows the same structure, but it works well, example
# /lib/api/services/youtube/client.rb
module Services
module Youtube
class Client
# ...
end
end
end
Inspecting it with binding.pry in some test:
pry(#<#filename>)> Services::Youtube::Client
=> Services::Youtube::Client
Has anyone had this problem or know what could be happening?
System:
ruby 2.7.6p219
Rails 6.1.6
Zeitwerk (2.5.4)
An autoload path is a root directory, not its contents.
You need to remove the wildcards as documented here.

Sudden "uninitialized constant PurIssue" error on STI_preload while upgrading rails to 7.0.1

I am in the process of upgrading a ~2 year old Rails app from 6.1 to 7.0.1
I have an STI setup where Pursuit is the main class and it has several other types as descendants:
# app/models/pursuit.rb
require 'sti_preload'
class Pursuit < ApplicationRecord
# ...
end
Descendant classes all look like this:
# app/models/pur_issue.rb
class PurIssue < Pursuit
# ...
end
--
# app/models/pur_goal.rb
class PurGoal < Pursuit
# ...
end
--
# app/models/pur_tracker.rb
class PurTracker < Pursuit
# ...
end
I have been using STI preload snippet as recommended by the Ruby Guides, and all worked well under Rails 6.0 and 6.1.
But now that I am upgrading to Rails 7.0.1, I suddenly get an "Uninitialized Constant" error for only the PurIssue subclass whereas all the other subclasses load fine:
Showing /Users/me/gits/rffvp/app/views/pursuits/_stats.html.slim where line #1 raised:
uninitialized constant PurIssue
Extracted source (around line #33):
33 types_in_db.each do |type|
34 logger.debug("Preloading STI type #{type}")
35 type.constantize
36 end
37 logger.debug("Types in database #{types_in_db}")
Trace of template inclusion: #<ActionView::Template app/views/layouts/_footer.html.slim locals=[]>, #<ActionView::Template app/views/layouts/application.html.slim locals=[]>
Rails.root: /Users/me/gits/rffvp
Application Trace | Framework Trace | Full Trace
lib/sti_preload.rb:33:in `block in preload_sti'
lib/sti_preload.rb:31:in `each'
lib/sti_preload.rb:31:in `preload_sti'
lib/sti_preload.rb:13:in `descendants'
app/models/pursuit.rb:71:in `<class:Pursuit>'
app/models/pursuit.rb:54:in `<main>'
app/models/pur_issue.rb:52:in `<main>'
app/views/pursuits/_stats.html.slim:1
app/views/layouts/_footer.html.slim:14
app/views/layouts/application.html.slim:13
I can't seem to figure out why PurIssue will not load anymore whereas all the other subclasses will. This is breaking my entire app since PurIssues are the most important data points.
Can someone point me to a Rails configuration change between 6.0, 6.1 and 7.0 that might cause this different behavior?
# lib/sti_preload.rb
module StiPreload
unless Rails.application.config.eager_load
extend ActiveSupport::Concern
included do
cattr_accessor :preloaded, instance_accessor: false
end
class_methods do
def descendants
preload_sti unless preloaded
super
end
# Constantizes all types present in the database. There might be more on
# disk, but that does not matter in practice as far as the STI API is
# concerned.
#
# Assumes store_full_sti_class is true, the default.
def preload_sti
types_in_db = \
base_class
.unscoped
.select(inheritance_column)
.distinct
.pluck(inheritance_column)
.compact
types_in_db.each do |type|
logger.debug("Preloading STI type #{type}")
type.constantize
end
logger.debug("Types in database #{types_in_db}")
self.preloaded = true
end
end
end
end
Zeitwerk shows the same error by the way:
$ rails zeitwerk:check
Hold on, I am eager loading the application.
rails aborted!
NameError: uninitialized constant PurIssue
/Users/me/gits/rffvp/lib/sti_preload.rb:33:in `block in preload_sti'
/Users/me/gits/rffvp/lib/sti_preload.rb:31:in `each'
/Users/me/gits/rffvp/lib/sti_preload.rb:31:in `preload_sti'
/Users/me/gits/rffvp/lib/sti_preload.rb:13:in `descendants'
/Users/me/gits/rffvp/app/models/concerns/validateable.rb:10:in `block in <module:Validateable>'
/Users/me/gits/rffvp/app/models/pursuit.rb:68:in `include'
/Users/me/gits/rffvp/app/models/pursuit.rb:68:in `<class:Pursuit>'
/Users/me/gits/rffvp/app/models/pursuit.rb:54:in `<main>'
/Users/me/gits/rffvp/app/models/pur_issue.rb:1:in `<main>'
Tasks: TOP => zeitwerk:check
(See full trace by running task with --trace)
I saw the same thing trying to do an upgrade to rails 7. I believe it originates with this change: https://github.com/rails/rails/commit/ffae3bd8d69f9ed1ae185e960d7a38ec17118a4d
Effectively the change to invoke Class.descendants directly in the internal association callback methods exposes a more longstanding implicit issue with invoking Class.descendants at all during the autoload, as the StiPreload implementation may result in a problem where it circularly attempts to constantize the original class being autoloaded. I've added an issue in the Rails repo here https://github.com/rails/rails/issues/44252

How to monkey patch Numeric in config/application.rb file

I would like to know if it's possible to monkey patch the Numeric DSL in the config/application.rb file. I'm trying to with:
Numeric.include CoreExtensions::Numeric
At the end of the file. But when I try to run the local server I see:
NameError: uninitialized constant CoreExtensions
The CoreExtensions namespace exists within app/lib/core_extensions.
Thanks.

uninitialized constant Rails::Generators (NameError)

I'm updating the code for a generator that I wrote, but has been working fine until now.
When I simply rue the command
bundle exec rails g
I get the following error
/Users/mpierc200/projects/prototype_conflux/vendor/gems/itrc_client_files_generator-1.0.13/lib/itrc_client_files_generator.rb:6:in `<top (required)>':
uninitialized constant Rails::Generators (NameError)
The offending line is
class ItrcClientFilesGenerator < Rails::Generators::Base
My Rails version is
Rails 3.1.9
ruby version is
ruby 1.9.3p194
It looks like the Rails generator modules were pulled out and not automatically loaded at some point during Rails 3 development. This is probably for good reasons.
You have to include them in custom generators:
require 'rails/generators'
class ItrcClientFilesGenerator < Rails::Generators::Base
# Fancy generator code here
end

Forcing rails to autoload class

I have several small classes that are in a single file in /app/models, similar to:
# /app/models/little_class.rb
class LittleClass; ...do stuff; end;
class AnotherLittleClass; ...do stuff; end;
Rails only seems geared to autoload classes in files reflecting the class name. So referencing AnotherLittleClass outside of the file raises "unitialized constant" errors as below until LittleClass is referenced:
irb(main):001:0> AnotherLittleClass
NameError: uninitialized constant AnotherLittleClass
irb(main):02:0> LittleClass
=> LittleClass
irb(main):03:0> AnotherLittleClass
=> LittleClass2
It would be a pain and messy to split them into individual files. Is there a way to autoload these classes, so referencing AnotherLittleClass without LittleClass doesnt raise an error?
You could put them into a module and use them within this namespace SomeLittleClasses::LittleClass.do_something
# /app/models/some_little_classes.rb
module SomeLittleClasses
class LittleClass
def self.do_something
"Hello World!"
end
end
class AnotherLittleClass
def self.do_something
"Hello World!"
end
end
end
Try this trick:
1.9.2p312 :001 > AnotherLittleClass.new
# => NameError: uninitialized constant AnotherLittleClass
1.9.2p312 :002 > autoload :AnotherLittleClass, File.dirname(__FILE__) + "/app/models/little_class.rb"
# => nil
1.9.2p312 :003 > AnotherLittleClass.new
# => #<AnotherLittleClass:0xa687d24>
These are your choices, as I see it:
split your file up into one file per class, put them in a dir named according to the rails convention (SomeClass => some_class.rb) and in a startup file (say, create a file in config/initializers), call:
autoload_paths Rails.application.config.root + "/path/to/lib"
add something like this to a startup file:
%W[
Class1 Class2
Class3 Class4 Class4
].map(&:to_sym).each dp |klass|
autoload klass,Rails.application.config.root + "/path/to/lib/file"
end
This of course will have to be updated each time a new class is added to the file.
Move all of the classes into a module/class namespace and call autoload to add it as above
just load the whole file up-front in a startup file with require. Ask yourself: does the extra effort warrant delaying the load of this file?
The following file app/models/statistic.rb is given :
class Statistic
# some model code here
end
class UsersStatistic < Statistic; end
class CommentsStatistic < Statistic; end
class ConnectionsStatistic < Statistic; end
Create a file config/initializers/autoload_classes.rb and add the following code:
# Autoloading subclasses that are in the same file
# This is the normal way to load single classes
#
# autoload :UsersStatistic, 'statistic'
# autoload :CommentsStatistic, 'statistic'
# autoload :ConnectionsStatistic, 'statistic'
# This is the most dynamic way for production and development environment.
Statistic.subclasses.each do |klass|
autoload klass.to_s.to_sym, 'statistic'
end
# This does the same but loads all subclasses automatically.
# Useful only in production environment because every file-change
# needs a restart of the rails server.
#
# Statistic.subclasses

Resources