I'm experiencing a failure to understand how Ruby module based namespacing is supposed to work. I've always understood it to be that if you have two classes with the same name you can put one in a module to clarify namespace.
At the moment this doesn't work and I can't see why. In app/models/something/baz.rb I have:
class Baz < ActiveRecord::Base
self.table_name = 'baz'
end
I'm not sure why the table_name assignment is in there. Perhaps because of the fact that this file is nested under something?
In lib/foo/bar/baz.rb I have:
module Foo
module Bar
class Baz
end
end
end
I've included, in order, the paths lib/foo, lib/bar in my autoload_paths config value in config/application.rb:
config.autoload_paths += %W(
#{config.root}/lib
#{config.root}/lib/import
#{config.root}/lib/foo
#{config.root}/lib/foo/bar
#{config.root}/app/models/concerns
#{config.root}/app/services
#{config.root}/app/controllers/concerns
#{config.root}/app
)
at the console I can successfully reference the Foo, Foo::Bar modules but when I try to do Foo::Bar::Baz I get the usual error
expected Baz to be defined in lib/foo/bar/baz.rb
now I change that class to be in lib/foo/bar/baz_thing.rb and refer to it in console as Foo::Bar::BazThing everything works. I've come to the possibly erroneous conclusion that the autoload implementation is mightly confused because there are two classes with the same name.
I've double, triple and quadruple checked names and paths on all of this.
I also read through all the suggested possible alternative/duplicate questions shown when creating this ticket and could find none that addressed my situation closely enough to answer my question.
Can anyone identify where I've gone wrong and why my expectations on module namespacing behavior are incorrect?
This is on ruby 2.2.2 and Rails 4.2.6
Related
While debugging an unrelated issue in rspec, I'm coming across issues with constant loading.
The setup is as follows:
# app/models/foo.rb
class Foo << ApplicationRecord
include Foo::Searchable
end
# app/models/foo/searchable.rb
module Foo::Searchable
extend ActiveSupport::Concern
included do
#yada yada
end
end
I received the following error while debugging. NameError: uninitialized constant #<Class:0x00007fadd32ea580>::Searchable
Changing the naming to Foos::Searchable with corresponding folder move does fix the issue but I would like to understand what is actually happening.
Rails 6.0.3.1 Ruby 2.6.6
As well as using << instead of < you have fallen victim to really common issue due to missusing the scope resolution operator ::. It should not be used when declaring nested classes or modules as it leads to the wrong module nesting and will lead to a missing constant error unless the module which you are nesting in is already loaded.
You should always explitly declare nested modules/classes:
class Foo < ApplicationRecord
module Searchable
extend ActiveSupport::Concern
included do
#yada yada
end
end
end
The reason this issue is popping up everywhere after the transition to Rails 6 is that the old classic autoloader overloaded Module#const_missing and glassed over these bugs as it would autoload Foo.
Zeitwork which replaces the classic autoloader instead uses Module#autoload which is a relatively new addition to Ruby and a lot less hacky.
This turns out to be caused by incompatibility with Byebug. Byebug stops the events Zeitwerk uses for autoloading to fire off in a debugging session. The issue is described here.
The << was a typo and would not result in that error.
Perhaps you should replace << with <. You inherit, not redirect
class Foo < ApplicationRecord
include Foo::Searchable
end
I am getting the "superclass mismatch for class" error when I run reload! in the Rails console. I have some super simple classes defined in ruby, something like this:
# base_class.rb
module A
module B
module C
class BaseClass
def close
#stub
end
end
end
end
end
And:
# more_specific.rb
module A
module B
module C
class MoreSpecific < BaseClass
def initialize
# ...
end
def close
end
end
end
end
end
I can see that in fact there's a problem because if I do this before I do reload!:
A::B::C::MoreSpecific.superclass.equal? A::B::C::BaseClass
I get true, and then if I do it after I get the error, I get a false. Additionally, the object_id of the BaseClass does in fact change.
Why might this happen? I've checked for additional references to the MoreSpecific class in the codebase because I thought that might lead to the BaseClass being established as a constant more than once, but did not see anything.
What could be causing the object_id of A:B:C::BaseClass to switch after the reload!?
Autoloading Modules Without a Require Statement
reload! is a Rails console method, not a standard Ruby method. While there could be other causes for the behavior you're seeing, it's worth noting that your C module in more_specific.rb doesn't require base_class at runtime, and may be losing its lookup; Rails may not autoload modules the way you're expecting without it.
Make sure that modules that depend on BaseClass contain a require base_class statement to be executed when the module reloads. If that doesn't resolve it, there may be other problems with your code as well that aren't shown in your current post.
I have an existing codebase of unit tests where the same classes are defined for each test, and a program that iterates over them. Something like this:
test_files.each do |tf|
load "tests/#{tf}/"+tf
test= ::Kernel.const_get("my_class")
Test::Unit::UI::Console::TestRunner.run( test )
While working on these tests, I've realized that ruby allows you to require classes with the same name from different files, and it merges the methods of the two. This leads to problems as soon as the class hierarchy is not the same: superclass mismatch for class ...
Unfortunately, simply unloading the class is not enough, as there are many other classes in the hierarchy that remain loaded.
Is there a way to execute each test in a different global namespace?
While I figure that using modules would be the way to go, I'm not thrilled with idea of changing the hundreds of existing files by hand.
--EDIT--
Following Cary Swoveland's suggestion, I've moved the test running code to a separate .rb file and am running it using backticks. While this seems to work well, loading the ruby interpreter and requireing all the libraries again has a considerable overhead. Also, cross-platform compatibility requires some extra work.
first I wanted to propose the following solution:
#main.rb
modules = []
Dir.foreach('include') do |file|
if file =~ /\w/
new_module = Module.new
load "include/#{file}"
new_module.const_set("StandardClass", StandardClass)
Object.send(:remove_const, :StandardClass)
modules << new_module
end
end
modules.each do |my_module|
my_module::StandardClass.standard_method
end
#include/first.rb
class StandardClass
def self.standard_method
puts "first method"
end
end
#include/second.rb
class StandardClass
def self.standard_method
puts "second method"
end
end
this wraps each class with it's own module and calls the class inside it's module:
$ruby main.rb
second method
first method
But as you answered there are references to other classes so modules can break them.
I checked this by calling the third class from one of the wrapped classes and got:
include/second.rb:5:in `standard_method': uninitialized constant StandardClass::Independent (NameError)
so probably using backticks is better solution but I hope my answer will be helpful for some other similar situations.
I upgraded Rails from version 3.1.2 (which worked fine) to 4.0, and got stuck with the following error:
circular dependency detected while autoloading constant Foo
I created a class ProductFactory, where I instantiate different models. For example:
p = Foo.new(params)
The model "Foo" is not always an ActiveRecord. Could anyone help me with this issue?
Best I'm aware, circular dependencies error messages usually occur when cascading includes go wrong by recursively requiring a file before it is fully loaded, e.g.:
# File A:
require 'B'
module Foo; end
# File B:
require 'A'
module Foo; end
Any odds this is the kind of situation you're ending up with?
I had this error because I manually renamed controllers, routes etc etc and forgot to rename it in the first line of the files.
Was named
class AController < ApplicationController
instead of
class ARenamedController < ApplicationController
and I had gone and renamed all the other files individuall.
Not best practice I know, but I am learning and figuring it out, and in this case created the error this person is talking about. So if you got here through Google like I did, there is my solution.
This kind of issues often happen when you change the version of Rails. You maybe didnt update the gems on the right order.
In Ruby a class named Foo would be defined with class Foo, used via require 'foo' and would live in $:[0]/foo.rb or something like that.
But what about Foo::Bar? Would it be called with require 'foo/bar'? Would it live in $:[0]/foo/bar.rb? And how would it be defined?
I am very used to Perl, where for personal projects I would make nested classes like Project::User, Project::Text::Index, Project::Text::Search, etc. I would then make a file like Project/Text/Index.pm, which would start with package Project::Text::Index, and be called via use Project::Text::Index;.
Now I'm starting a project in Ruby and have no idea how to do this. For some reason none of the Ruby books or docs I've read mention perl-style hierarchical class naming. When they mention inheritance it's usually via a trivial made up example like class Foo < Bar which doesn't really help me. Yet I figure it must be possible to do what I'm attempting because Rails (just to take one example) has classes like ActionView::Helpers::ActiveModelFormBuilder.
You're combining a couple of concepts here which aren't really related, namely the load path, inheritance, and the scope resolution operator.
When requiring (or loading) files the argument to the require keyword is simply taken as a file path and appended to the load search path (the .rb extension is optional for require). Inheritance and nesting don't come into play here and any file can define anything it wants, e.g.:
require 'foo' # Looks for "foo.rb" in each of $:
require 'foo/bar' # Looks for "foo/bar.rb" in each of $:
Nested classes (and modules, variables, etc) are defined as expected but resolved with the scope resolution operator, e.g.:
class Foo
def foo; 'foo'; end
class Bar
def bar; 'bar'; end
end
end
Foo.new.foo # => "foo"
Foo::Bar.new.bar # => "bar"
Note that class nesting and inheritance are irrelevant to the location of the file from which they are loaded. There don't seem to be any explicit conventions for class/module structuring, so you're free to do what works for you. The Ruby Language page of the Programming Ruby book might be helpful too.
You need modules. If you want a class identified by Funthing::Text::Index in Funthing/Text/Index.rb and be required with require 'Funthing/Text/Index', you do this:
# file Funthing/Text/Index.rb
module Funthing
module Text
class Index
def do_the_twist()
puts "Let's twist again!"
end
end
end
end
Then use it like this:
require "Funthing/Text/Index"
c = Funthing::Text::Index.new
Note: the file hierarchy is not required, but (in my opinion) is best practice.