Metaprogramming: Where does method dispatch look when we call an instance method that is defined inside a class method?
-
26-05-2021 - |
Question
I'm reading Metaprogramming Ruby and just want to clear something up about the following paraphrased code:
class MyClazz
def self.my_class_method(name)
define_method(name) {
# do stuff
}
end
my_class_method :foo
my_class_method :bar
end
# The code above generates instance methods:
# def foo
# do stuff
# end
# def bar
# do stuff
# end
Q1
My first question regards the two method calls at the end of the file: my_class_method :foo
and my_class_method :bar
. Am I right in thinking that they are both invoked automatically when a MyClazz object is instantiated?
Q2 When Ruby generates these methods (def foo
and def bar
), it will put them in MyClazz's eigenclass, even though they are instance methods. So does this mean that Ruby looks to the eigenclass for both class and instance methods when needed?
I just want to clear that up before I move too far into the book.
Solution
Answer 1: (short) They are invoked when ruby instantiate MyClass
instance (of type Class
).
(long) When Ruby interpreter sees a class definition (class MyClazz
) it instantiate an instance of this class and evaluate all the code inside class definition.
In your case MyClazz
is a constant that holds a refference to the object of class Class
. And when Ruby initialize it, it execute code inside class definition - defines singleton method my_class_method
of this Class
instance and executes method my_class_method
twice in the context of this Class
instance.
Answer 2: (short) Module#define_method
private method adds method to the method table of Class
instance (method table holds instance methods of the class). It does not affect eigenclass of istance object/class object.
(long) When you call instance method on the object, Ruby first looks this method in the eigenclass of this object, then in the superclass of eigenclass (it will be a Class
object of the object's class). But it will not look in the eigenclass of MyClazz
object.
Example:
obj = MyClazz.new
obj.foo # => ok
obj.foo
will look for foo
method definition in eigenclass of obj
object, then will look for instance methods of MyClass
(instance of class Class
), then in the superclass of MyClass
object (in your case this is Object
class) etc.
obj = MyClass.new
MyClass.my_class_method :baz
obj.baz # => ok
MyClass.my_class_method
will look for my_class_method
method definition in the eigenclass of MyClass
object (sidenote: eigenclass of a class sometimes is
called a metaclass) and it will find it here and will add baz
instance method to the class MyClass
.
OTHER TIPS
A1: Yes these methods are created upon instantiation.
A2: Russ Olsen does a great job of explaining this in his book Eloquent Ruby. The eigenclass (or singleton class) "sits between every object and its regular class". As such, when Ruby doesn't find the method it's looking for in the instance methods, it will begin traveling up the inheritance tree. The next stop is the eigenclass and from there the class itself.
Olsen also gives an interesting discussion about how all Class methods are actually singleton methods between the new class and the Class object.