Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby method-calling hierarchy

Tags:

ruby

Inspired by this article, I was playing around with the Ruby method calling hierarchy and noticed something strange.

Given:

class B
  def foo  
    "- Instance method defined by B"
  end  

  def method_missing(method)
    puts "- method_missing (#{method}) on b. Redirecting to b.foo\n"
    foo
  end
end

b = B.new

def b.foo  
  "- Method defined directly on an instance of B\n" + super  
end

def b.method_missing(method)
  "- method_missing (#{method}) on b. Calling super\n" + super
end

puts "Calling 'bar' on b of type #{b.class}:"
puts b.bar

Running it gives:

Calling 'bar' on b of type B:
- method_missing (bar) on b. Redeirecting to b.foo
- method_missing (bar) on b. Calling super
- Method defined directly on an instance of B
- instance method defined by B

My question is:

Since I am calling b.bar (on the object), how come the class's instance method is called before the objet's instance method is called?

I would have expected b.method_missing(method) to be called first, then the class's instance method_missing(method) (since I am calling super? but super is class hierarchy...) which the does the redirect from bar to foo. Also, how come, after being redirected to foo, the instance's missing_method is called? We were just told that we're being redirected...

I think I don't understand the concept of how Ruby allows to define an instance method on an instance of a class (new to me), as opposed to defining it as an instance method of a the class (classic languages).

I hope this question makes sense, maybe my head is still spinning from last night...

like image 912
dynex Avatar asked Oct 09 '22 02:10

dynex


1 Answers

The problem is that your output does not reflect the actual order of execution. Let's take a look at the output:

method_missing (bar) on b. Redeirecting to b.foo
method_missing (bar) on b. Calling super

At first glance, this gives the impression that B#method_missing is called before the singleton method b.method_missing which raises the two questions you describe. Actually though, b.method_missing is correctly called first. This gets clear if you look at how the statement puts b.bar is evaluated:

  1. b has no method bar, so b.method_missing is called with the string 'bar' as an argument.
  2. b.method_missing in turn calls the method B#method_missing through use of the super keyword.
  3. B#method_missing uses puts to output the first line seen above
  4. the singleton method b.foo is called
  5. the outermost puts is performed, which outputs the string in b.method_missing along with the return value of b.foo
like image 159
Niklas B. Avatar answered Oct 13 '22 10:10

Niklas B.