The error is something like NameError: uninitialized constant Foo::Bar
(when Bar
is not a descendant of Foo
).
I get that it has something to do with loading (autoloading?) constants, and I feel that anything inside of lib/
is safe to not prefix with ::
because it's autoloaded (or something).
An example that just happened to me is something like this:
app/assets/classes/base_class.rb
class BaseClass
end
app/assets/some_module/some_class.rb
module SomeModule
class SomeClass < BaseClass
end
end
I was running a spec and got "An error occurred while loading [file]": NameError: uninitialized constant SomeModule::SomeClass::BaseClass
.
Now, I get that it's trying to look for BaseClass
within SomeModule::SomeClass
. But this was working a few hours before and then stopped after no changes to these files.
So, I could just tack on a ::
and go with class SomeClass < ::BaseClass
, but without understanding why it feels bad and then I'm like, do I need to pepper all my code with ::
all the time?
When do you need to prefix Ruby constants with "::"?
When you refer to a constant Ruby will look for it in the current module nesting.
module SomeModule
puts Module.nesting.inspect # the current module nesting is [SomeModule]
end
The module nesting is primarily set by using the module
and class
keywords to open a class/module.
If its not found there it will keep going upwards in the module nesting until it reaches main and if the constant still isn't found by then you'll get a missing constant error.
This error can be somewhat confusing since the message contains the module nesting where it started looking for the constant.
By prefixing a constant with the scope resolution operator (::
) you're explicitly telling Ruby to resolve the constant from the top level namespace:
module Bar
def self.hello
puts "Hello from the top level Bar"
end
end
module Foo
module Bar
def self.hello
puts "Hello from Foo::Bar"
end
end
def self.test
Bar.hello # the current module nesting is Foo.
::Bar.hello
end
end
Foo.test
# Will output:
# Hello from Foo::Bar
# Hello from the top level Bar
In most cases its not strictly necissary but it is a good practice though to explicitly refer to constants outside your own namespace (dependencies).
So why did my code stop working?
Without an actual reproducable example its near impossible to tell. It can either be "programmer related issues" (you screwed up and moved/deleted/renamed the file) or it can be tied to issues with the autoloader in use. The classic autoloader was far more prone to buggy behavior due to the way it monkeypatched Object#constant_missing
.