class MyClass
def one
def two
end
end
end
obj = MyClass.new
obj.one
puts obj.method(:two).owner #==> MyClass
Here i define method two inside another method one. Method one is called by the instance of MyClass (obj) . So the self is obj when the method two is defined. when i check the owner of method two it's MyClass
obj.instance_eval do
def three
end
end
puts obj.method(:three).owner #==> #<Class:#<MyClass:0x007f85db109010>>
In this snippet i do instance_eval on obj , so the self is again obj when the method three is defined . But when i check the owner of three , it's the singleton class of obj
why is this? is there anything else besides self which determines where the method definition goes ??
I would use Kernel#set_trace_func method,to explain you what is going on under the hood. Look first the below code and the output:
trace = lambda do |event,file,line,id,binding,klass|
p [event,File.basename(file),line,id,binding,klass]
end
set_trace_func trace
class MyClass
def self.bar;end
def one
def two
end
end
end
obj = MyClass.new
obj.one
obj.instance_eval do
def three
end
end
output:
-----------------
----------------
-----------------
-----------------
-----------------
----------------- # part A
["c-call", "test.rb", 9, :singleton_method_added, #<Binding:0x83ab2b0>, BasicObject]
["c-return", "test.rb", 9, :singleton_method_added, #<Binding:0x83aaeb4>, BasicObject]
["line", "test.rb", 10, nil, #<Binding:0x83aab80>, nil]
["c-call", "test.rb", 10, :method_added, #<Binding:0x83aa900>, Module]
["c-return", "test.rb", 10, :method_added, #<Binding:0x83aa07c>, Module]
----------------------------- # part B
["line", "test.rb", 16, nil, #<Binding:0x83a976c>, nil]
["c-call", "test.rb", 16, :new, #<Binding:0x83a9488>, Class]
["c-call", "test.rb", 16, :initialize, #<Binding:0x83a90a0>, BasicObject]
["c-return", "test.rb", 16, :initialize, #<Binding:0x83a8e20>, BasicObject]
["c-return", "test.rb", 16, :new, #<Binding:0x83a8b28>, Class]
---------------------------
---------------------------
--------------------------- # part C
["c-call", "test.rb", 11, :method_added, #<Binding:0x83a7de0>, Module]
["c-return", "test.rb", 11, :method_added, #<Binding:0x83a79f8>, Module]
--------------------------- # part D
["line", "test.rb", 18, nil, #<Binding:0x83a7034>, nil]
["c-call", "test.rb", 18, :instance_eval, #<Binding:0x83a6c10>, BasicObject]
["line", "test.rb", 19, nil, #<Binding:0x83a65f8>, nil]
["c-call", "test.rb", 19, :singleton_method_added, #<Binding:0x83a61d4>, BasicObject]
["c-return", "test.rb", 19, :singleton_method_added, #<Binding:0x83a5ef0>, BasicObject]
["c-return", "test.rb", 18, :instance_eval, #<Binding:0x83a5d4c>, BasicObject]
Explanation:
Look at the 5 lines below part A. It simply tells us the when Ruby will find def key word inside a class, it will add that method as an instance method to that class. This is being done by calling the hook method Module#method_added. The same explanation goes to two lines below part C.
Now what goes on inside obj.instance_eval {..} ?
Ok,this will be cleared if you look at the lines below part D. Look from last, first and second line.Inside the instance_eval block, def third, causes third to be added as a singleton_method of the object ob, by calling the hook method BasicObject#singleton_method_added.
This is how MRI has been written.
This is explained in a nice article by ruby-core contributor yugui: Three implicit contexts in Ruby. Basically, there is a default definition context, which is not the same as self. Methods that are not explicitly defined as singleton methods end up as instance methods of the default definition context. module and class definition bodies change the default definition context whereas def doesn't. instance_eval OTOH does change it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With