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