Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby self and method definitions

Tags:

ruby

self

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 ??

like image 202
orange Avatar asked Dec 01 '13 11:12

orange


2 Answers

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.

like image 103
Arup Rakshit Avatar answered Sep 21 '22 18:09

Arup Rakshit


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.

like image 27
Jörg W Mittag Avatar answered Sep 22 '22 18:09

Jörg W Mittag