In this article, we are going to learn about the method lookup and constant lookup in ruby. We will learn detailed information about the hierarchy of both of the lookup methods. But before diving into that, let's first have some brief about Ruby for better understanding.
Ruby is a dynamic and purely object-oriented programming language that was developed by Yukihiro Matsumoto. It is a high-level programming language with much easier syntax so that it can be learned easily.
Before we get into the method lookup and constant lookup first we have to understand the ancestor chain in ruby.
Ancestor Chain in Ruby
Ancestor chain in ruby depicts the hierarchy of the classes as which class is a superclass of another class.
Below mentioned is a hierarchy by which one can gain the knowledge of super and child classes.
The above diagram shows the hierarchy of the parents and child classes or modules.
Let's get to know about each of them individually.
Basic object The top parent of all classes is BasicObject. It has the bare minimum of tools for comparing and creating objects.
Kernel The Object class contains the Kernel module. All of the "object manipulation" logic is contained in it.
Object It is the default root for the first defined class in the program. Since the Kernel module holds the majority of the methods, all of its descendants use Object more as an interface (thus the name).
Example
class Example
end
puts Example.superclass
You can also try this code with Online Ruby Compiler
The above example demonstrates that a defined is a child class of the Object class with help of the superclass method. We can check the hierarchy of any defined class in ruby with the help of the ancestors' method. This method shows us all the classes or modules included in that particular class.
Example
puts String.ancestors
You can also try this code with Online Ruby Compiler
Now that you have understood the hierarchy, let us jump right into the Method lookup:
Method lookup
In Ruby, a lot of things take place in the background when a method is called. To determine what behaviour to interpret, the Ruby interpreter needs to know where this method is declared.
The method lookup is the name of this procedure. An object's ancestor chain is searched to determine where this method was declared.
Example
class Parentclass
def foo
puts "this is a parent class method"
end
end
class Childclass < Parentclass
end
You can also try this code with Online Ruby Compiler
When we type the above syntax in the program instance_methods will return all instance methods included in the Childclass class. But as we can see Childclass is empty so it will print all other inherited methods for the superclasses so in order to avoid this we will pass false as an argument into the instance_methods(false) so that it only shows the methods defined in this class.
Childclass.instance_methods(false) #will not print anything.
You can also try this code with Online Ruby Compiler
From the above syntax, it will return us the foo method only because it is the only method defined in the Parentclass class.
That's how the lookup technique works: if foo is not present in the Parentclass then the algorithm will try to look into the superclass of it until it finds it; if that particular method is not defined anywhere in the hierarchy then it will return false for the output.
That's how the method lookup works behind.
Constant lookup
A constant's definition must be found by the Ruby interpreter when it is referenced without any corresponding namespace. It uses a name resolution technique to accomplish this, just as it does to locate method definitions. Constants, however, are resolved very differently from methods.
Ruby first tries to resolve a constant reference within its lexical range.
This means that it first determines if the class or module that contains the constant reference defines the constant. In that case, it looks at the following enclosing class or module. This keeps on until no more enclosing classes or modules are present.
There are a few aspects of this constant lookup technique that deserve closer examination:
Constants specified in enclosing modules are preferred to those defined in included modules when searching.
A class's contained modules are searched before the class' superclass.
The hierarchy of class inheritance includes the Object class. Similar to top-level methods, top-level constants are automatically specified in Object and are defined at the top level, outside of any class or module. Therefore, when a top-level constant is referred from a class, it is resolved as part of the inheritance hierarchy search. However, after looking through the module's ancestors, an explicit check of Object is required if the constant is mentioned within a module declaration.
Object has the Kernel module as a common ancestor. As a result, top-level constants defined in the kernel can be overridden by actual top-level constants defined in the object.
Example
Demonstrates the constant name lookup mechanism and defines and resolves constants in six different scopes.
module Kernel
# Constants defined in Kernel
A = B = C = D = E = F = "defined in kernel"
end
# Top-level or "global" constants defined in Object
A = B = C = D = E = "defined at toplevel"
class Super
# Constants defined in a superclass
A = B = C = D = "defined in superclass"
end
module Included
# Constants defined in an included module
A = B = C = "defined in included module"
end
module Enclosing
# Constants defined in an enclosing module
A = B = "defined in enclosing module"
class Local < Super
include Included
# Locally defined constant
A = "defined locally"
# The list of modules searched, in the order searched
# [Enclosing::Local, Enclosing, Included, Super, Object, Kernel]
search = (Module.nesting + self.ancestors + Object.ancestors).uniq
puts A # Prints "defined locally"
puts B # Prints "defined in enclosing module"
puts C # Prints "defined in included module"
puts D # Prints "defined in superclass"
puts E # Prints "defined at toplevel"
puts F # Prints "defined in kernel"
end
end
You can also try this code with Online Ruby Compiler
All messages are dynamically bound to methods in Ruby. The receiver's singleton methods are sought after first, followed by those defined in the receiver's own class and then those defined in its superclasses (including any modules which may have been mixed in). By displaying ClassName.ancestors, which lists the ancestor classes and modules of ClassName, you can observe the search order.
So where do all these methods that resemble functions come from?
In Ruby, almost all classes are descended from the Object class. The methods described in the Kernel module are mixed in with the specification of class Object. Every item in the system can therefore access these methods. Even if you write a straightforward Ruby programme without any classes, you are still interacting with the Object class.
How can I alter a method's visibility?
By using the terms private, protected, and public, you can modify a method's visibility. They have an impact on the visibility of following methods when used during a class definition without parameters. They alter the named methods' visibility when combined with arguments.
Does a constant's value ever alter?
A variable whose name begins with an uppercase letter is considered a constant. Constants can be altered, however, they cannot be changed from within instance methods. An alert is sent when a constant is given a new value.
What distinguishes private from protected?
When a method's visibility is set to private, it can only have self as its recipient because it can only be called in function form without an explicit receiver. Only the class in which the method was declared or its subclasses may call a private method.
Conclusion
In this article, we learned about the method lookup and constant lookup hierarchy with the help of examples. We also learned about each of the superclasses or modules present in the hierarchy, this also confirms why ruby is called a pure object oriented language.
To learn about more ruby please look into the following articles: