How does Ruby's Object#const_get actually work? - ruby

I have recently discovered that Ruby (2.2.1) has some "interesting" behavior.
module Foo
class Foo
end
class Bar
end
end
Foo.const_get('Foo') #=> Foo::Foo
Foo.const_get('Bar') #=> Foo::Bar
Foo.const_get('Foo::Foo') #=> Foo
Foo.const_get('Foo::Bar') #=> NameError: uninitialized constant Foo::Foo::Bar
Foo.const_get('Foo::Foo::Bar') #=> Foo::Bar
Foo.const_get('Foo::Foo::Foo::Bar') #=> NameError: uninitialized constant Foo::Foo::Bar
Foo.const_get('Foo::Foo::Foo::Foo::Bar') #=> Foo::Bar
Foo.const_get('Foo::Foo::Foo') #=> Foo::Foo
Foo.const_get('Foo::Foo::Foo::Foo') #=> Foo
Foo.const_get('Foo::Foo::Foo::Foo::Foo') #=> Foo::Foo
Foo.const_get('Foo::Foo::Foo::Foo::Foo::Foo') #=> Foo
This is a bit surprising. My understanding has been that const_get first looks for a constant in the receiver's collection of constants and then looks at Object's constants. OK, fine. Why then does the fourth Foo#const_get above fail and the third one doesn't?
I'm also curious as to why calling Foo#const_get alternates between the module and class depending on how many ::Foos you add.

The docs say:
This method will recursively look up constant names if a namespaced class name is provided.
So Foo.const_get('Foo::Bar') is basically the same as Foo.const_get('Foo').const_get('Bar'). Using this interpretation your results make sense.
Your third example:
Foo.const_get('Foo::Foo')
is the same as
Foo.const_get('Foo').const_get('Foo')
The first const_get looks at constants defined within the top level Foo (the module), and finds the nested class. So the whole thing effectively becomes:
Foo::Foo.const_get('Foo')
The second call then looks at the class, first looking the any contained constants (finding none) and then looking in its ancestors. Object is an ancestor, and has the top level Foo module as a constant, so this is found and returned.
This also explains the alternation when adding extra ::Foos. The alternation is between const_get on the top level module finding the nested class, and on the nested class looking up the inheritance chain and finding the top level module.
The fourth example, Foo.const_get('Foo::Bar'), which raises an exception, can also be explained. It is equivalent to
Foo.const_get('Foo').const_get('Bar')
The first part, Foo.const_get('Foo') is the same as the case above, evaluating to Foo::Foo, so the whole thing now effectively becomes:
Foo::Foo.const_get('Bar')
Now the nested Foo class doesn’t contain a Bar constant, and looking up the inheritance chain, neither does Object, so the result is a NameError.

Related

const_defined? vs. defined? in Ruby

To test, whether a constant (say: a class) is known at a certain point in the code, I can write for instance:
if defined? :String
or I can write
if self.class.const_defined? :String
Is there a situation where I these two ways of testing would make a difference? Note that I don't ask about the case where I have an explicit receiver, such as MyModule.const_defined? :Something, but only for the case where I want to test whether a certain constant (which in my case happens to be a constant denoting a class) is already defined.
First things first, defined? is a keyword which behaves a bit similar similar to a method. It receives the name of the thing (variable, constant, ...) to check. What makes this method different from all others is that the parser will not resolve the value of the given name but rather check directly for whether it is defined (hence the keyword property). To check if a constant is defined, you thus have to pass the actual name (rather than a Symbol):
if defined?(String)
The const_defined? on the oither hand is more regular. It expects a Symbol or String with the name of a constant and checks whether it is defined on the receiver.
Now as for the differences between the two (when used correctly): if you use them both within the context of an instance method to check for the existence of a constant, they work the same.
When running e.g. in a class definition (such that self is e.g. a class), you need to make sure to use the correct receiver for your const_defined method, e.g. if self.const_defined? :String.
Also, defined? can check for a lot more than just constants (e.g. methods, expressions, variables, ...)
If you want to use this to make sure you actually have the name of a constant at hand in a given variable, you need to use const_defined?. If you want to "statically" check whether an constant was defined, you can use defined?.
defined? is a keyword that will check if an expression exists in the current scope.
const_defined? is a method that will check if a constant exists through the ancestor chain of the receiver.
planet = "Earth"
class Experiment
def diff
""
end
def show
puts "defined" if defined?(diff)
puts "Earth not found" if !defined?(planet)
puts "String constant defined" if self.class.const_defined?(:String)
end
end
Experiment.new.show
#=> defined
#=> Earth not found
#=> String constant defined
p Experiment.ancestors #=> [Experiment, Object, Kernel, BasicObject]
p String.superclass #=> Object
Here's an example of situations where this will make a difference:
Using defined?(Nothing's printed)
class Lab
class Coco
end
end
class Experiment
def diff
""
end
def show
puts "defined" if defined?(Coco) #=> Nothing printed
end
end
Experiment.new.show
Using self.class.const_defined? (Something's printed)
class Lab
class Coco
end
end
class Experiment < Lab
def diff
""
end
def show
puts "defined" if self.class.const_defined? :Coco #=> defined
end
end
Experiment.new.show
p Experiment.ancestors #=> [Experiment, Lab, Object, Kernel, BasicObject] We find 'Lab' class in the ancestor chain.
To test, whether a constant (say: a class) is known at a certain point in the code, I can write for instance:
if defined? :String
or I can write
if self.class.const_defined? :String
Is there a situation where I these two ways of testing would make a difference?
These two really do two completely different things. The first tests whether the Symbol literal :String is defined. Obviously, a literal will always be defined, so this expression will always be true.
The second will check whether the constant String is defined, but not starting at the current constant lookup scope, instead starting at the class of self.
TL;DR
There may be cases where you can use them interchangeably, but one is a keyword and the other a method. In addition, the semantics and return values of the two are quite different.
Keywords vs. Methods
Among other things, one key difference is that Module#const_defined? is a method on a class or module that looks up constants in a class and its ancestors, while defined? is a keyword that determines whether its argument is currently known at the calling point in your code.
For example:
char = 'a'
char.const_defined?
#=> NoMethodError (undefined method `const_defined?' for "a":String)
defined? char
#=> "local-variable"
Exceptions vs. Return Values
If you're only concerned about constants, then the main difference is that you can use defined? to determine whether a constant is currently in scope without triggering a NoMethodError exception. For example:
defined? String
#=> "constant"
defined? FooBarBaz
#=> nil
As a bonus, defined? will tell what type of object you're passing as an argument (e.g. "constant"), while #const_defined? returns a truthy value.
Float.constants
#=> [:ROUNDS, :RADIX, :MANT_DIG, :DIG, :MIN_EXP, :MAX_EXP, :MIN_10_EXP, :MAX_10_EXP, :MIN, :MAX, :EPSILON, :INFINITY, :NAN]
defined? Float::NAN
#=> "constant"
Float.const_defined? :NAN
#=> true
As a rule of thumb, it's often considered best practice to reserve exceptions for handling something unexpected that may require your application to halt. Introspection or branching should generally rely on return values or Booleans, so defined? is usually a better choice if you aren't already expecting a given class to already be defined and available within the current scope.
Is there a situation where I these two ways of testing would make a difference?
const_defined? only checks the receiver and its ancestors, but it doesn't take the current module nesting into account:
module Foo
ABC = 123
class Bar
def self.test
p defined?(ABC) #=> "constant"
p const_defined?(:ABC) #=> false
end
end
end
In order to do so, you have to traverse Module.nesting:
module Foo
ABC = 123
class Bar
def self.test
p defined?(ABC) #=> "constant"
p Module.nesting.any? { |m| m.const_defined?(:ABC) } #=> true
end
end
end

Why does Ruby class_eval remove constants from scope?

I made a class with a constant & a method:
class A
FOO = 'hello'
def bar
puts FOO
end
end
A.new.bar
=> 'hello'
And everything works as expected. However when I do this:
A.class_eval do
def bar
puts FOO
end
end
A.new.bar
NameError: uninitialized constant FOO
Weirdness... To get around this I'm doing:
A.class_eval do
def bar
puts self.class::FOO
end
end
Any good explanation on why this is so?
Constants are looked up
outwards in the lexically enclosing module declarations
upwards in the inheritance chain of the current module declaration
So, let's just do what Ruby does:
look outwards in the lexically enclosing module declarations: easy – there are no module declarations
look upwards from the current module declaration: again, there is no current module declaration … or is there? Well, if there is no module declaration, it is implicitly assumed to be class Object – but Object doesn't have a constant named FOO and neither do its ancestors Kernel and BasicObject.
Ergo: Ruby is right. There is no constant named FOO within the constant lookup path. It didn't remove FOO from the scope, it was never in scope to begin with.
[Ruby's reflection API actually gives you access to anything you need: #1 is exactly the same as Module.nesting and #2 is (almost) the same as Module.nesting.first.ancestors.]
You might think: wait, isn't module_eval a module declaration? No, it isn't! It's just a method like any other method taking a block that's just like any other block. It doesn't alter the constant lookup rules in any way.
Note that that's not quite true: after all, e.g. instance_eval does change method lookup rules, for example, so it would not be inconceivable that module_eval changes constant lookup rules. And even more confusingly, when called with a String instead of a block, it actually does change Module.nesting and thus the constant lookup rules!

Referencing constants from Module

In a tutorial, there are examples querying constants using const_get:
class This
end
p Module.const_get('This').new
and
A_CONSTANT = 42
p Module.const_get('A_CONSTANT')
I can't see any module definition. Why is Module used? Why isn't const_get('This') or self.const_get('This') enough? The class where all this is written in is Object, ancestor of BasicObject.
Does the fact that This is a constant mean that it holds the class definition inside of it? Like as it just could hold the number 42? Or to be clearer, is it the same as:
This = class
end
like an unnamed method so I can write p This and get the class definition?
Some nice knowledge you have acquired. But I don't know why are you mentioning metaprogramming in your title, when your question is all about constants. Metaprogramming is something else.
In any case, #const_get is an instance method of Module class, so it won't work at the top level, where the implicit receiver is of Object class. When you write
class Foo; end
Constant Foo gets added to Object:
Object::Foo #=> Foo
Object::Bar #=> error (we didn't define constant Bar)
This constant is available also in other classes, such as
Module::Foo #=> Foo, but with a warning
Array::Foo #=> same Foo with the same warning
Fixnum::Foo #=> ditto
In other words, your Foo defined at toplevel is visible from almost every module. But accessing toplevel constants like this is frowned upon, as this is often not what the programmer intended. You can indeed see that the constant is defined on Object:
Object.constants( false ).include? :Foo #=> true
and not on eg. Module:
Module.constants( false ).include? :Foo #=> false
However, if you use #const_get, the warning message is suppressed and you may come to think that Module::Foo actually exists:
Module.const_get( :Foo ) #=> Foo
It doesn't.

Find a constant (a class) scoped to a specific namespace (a module)

I'm trying to programmatically fetch a constant (e.g. a class), but want to only look at the constants defined in a specific namespace. However, const_get will bubble up to higher namespaces while searching for the constant. For instance:
module Foo
class Bar
end
end
class Quux
end
If you then ask Foo to return the constant "Bar", it'll return the correct class.
Foo.const_get('Bar')
#=> Foo::Bar
However, if you ask it for "Quux", it'll bubble up it's search path and find the top-level Quux:
Foo.const_get('Quux')
#=> Quux
Is there any way to make it only search in the module that const_get is called on?
Module#const_get says:
Checks for a constant with the given name in mod If inherit is set, the lookup will also search the ancestors (and Object if mod is a Module.)
The value of the constant is returned if a definition is found, otherwise a NameError is raised.
You can then do as below:
module Foo
class Bar
end
end
class Quux
end
Foo.const_get('Quux',false) rescue NameError
# >> NameError
Foo.const_get('Bar',false) rescue NameError
# >> Foo::Bar

Confusing behaviour of const_get in Ruby?

According to the documentation mod.const_get(sym) "Returns the value of the named constant in mod."
I also know that const_get by default may look up the inheritance chain of the receiver. So the following works:
class A; HELLO = :hello; end
class B < A; end
B.const_get(:HELLO) #=> :hello
I also know that classes in Ruby subclass Object, so that you can use const_get to look up 'global' constants even though the receiver is a normal class:
class C; end
C.const_get(:Array) #=> Array
However, and this is where i'm confused -- modules do not subclass Object. So why can I still look up 'global' constants from a module using const_get? Why does the following work?
module M; end
M.const_get(:Array) #=> Array
If the documentation is correct - const_get simply looks up the constant defined under the receiver or its superclasses. But in the code immediately above, Object is not a superclass of M, so why is it possible to look up Array ?
Thanks
You are correct to be confused... The doc didn't state that Ruby makes a special case for lookup of constants in Modules and has been modified to state this explicitly. If the constant has not been found in the normal hierarchy, Ruby restarts the lookup from Object, as can be found in the source.
Constant lookup by itself can be bit confusing. Take the following example:
module M
Foo = :bar
module N
# Accessing Foo here is fine:
p Foo # => bar
end
end
module M::N
# Accessing Foo here isn't
p Foo # => uninitialized constant M::N::Foo
end
p M::N.const_get :Foo # => uninitialized constant M::N::Foo
In both places, though, accessing Object level constants like Array is fine (thank god!). What's going on is that Ruby maintains a list of "opened Module definitions". If a constant has an explicit scope, say LookHereOnly::Foo, then only LookHereOnly and its included modules will be searched. If no scope is specified (like Foo in the example above), Ruby will look through the opened module definitions to find the constant Foo: M::N, then M and finally Object. The topmost opened module definition is always Object.
So M::N.const_get :Foo is equivalent to accessing Foo when the opened classes are only M::N and Object, like in the last part of my example.
I hope I got this right, coz I'm still confused by constant lookups myself :-)
I came up with the following script to load name spaced constants:
def load_constant(name)
parts = name.split('::')
klass = Module.const_get(parts.shift)
klass = klass.const_get(parts.shift) until parts.empty?
klass
end
As long as we are not checking for errors you can:
def load_constant(name)
name.split('::').inject(Module) do |mod_path, mod_to_find|
mod_path.const_get(mod_to_find)
end
end

Resources