Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Module.methods() and respond_to? works differently in irb than in script?

I'm trying to use reflective methods in Ruby, and running into a behavior that I find really surpising.

The following examples seems to work differently in IRB and when called a ruby script:

Example 1:

def myfun; end
p respond_to?(:myfun)

In IRb, this says 'true', In script: 'false'.

Example 2:

ml = methods
def myfun; end
p methods - ml

In IRb, this says [:myfun]. In script: [].

I found this under 1.8, 1.9 MRI, JRuby 1.5.6, etc - so I assume this is normal.

Why is the difference?

I was pretty sure 'respond_to?' is the way to see if a method is available - why is that not working in the above case?

like image 547
inger Avatar asked Mar 04 '13 16:03

inger


1 Answers

This function - method on "main" object - is defined as private in ruby script. You can check this easily:

ml = private_methods
def myfun; end
p private_methods - ml #=> [:myfun]
p respond_to?(:myfun, true) #=> true

If you call it explicitly on self you will get an error:

self.myfun
# NoMethodError: private method ‘myfun’ called for main:Object

On the other hand, in IRB your method is defined as public. Under the hood it looks something like this:

class Object
  def irb_binding
    # this is where your entered code is evaluated
    def myfun; :ok; end # you can define methods in other methods
    self.myfun # and they are public by default
  end
end

p irb_binding # :ok

IRB could easily evaluate at the top level but instead it creates a separate environment with the method so that local variables are not shared:

require "irb"
foo = :ok
IRB.start
#>> foo
# NameError: undefined local variable or method `foo' for main:Object

I think methods being public is just a coincidence due to the implementation and it doesn't matter much. These methods are temporary anyway.

like image 162
Simon Perepelitsa Avatar answered Sep 28 '22 03:09

Simon Perepelitsa