5

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?

inger
  • 19,574
  • 9
  • 49
  • 54

1 Answers1

5

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.

Simon Perepelitsa
  • 20,350
  • 8
  • 55
  • 74
  • Very good to know - it's amazing I haven't run into this after years of Ruby exposure.. Yes I'm interested in why IRB makes them public it's trying to be friendlier- is it? :) I can see why normally they are private --to avoid confusion like "def f;end; class A;end; A.new.f", or something like that? – inger Mar 04 '13 at 17:08
  • @Semyon Can you explain why `irb` methods are public? And why in `irb` `myfun` and `self.myfun` refers to same method? – Rahul Tapali Mar 04 '13 at 17:17
  • For the record: tried with Pry.. That's consistent with the standalone script: top-level methods are private (so respond_to? returns false). – inger Mar 04 '13 at 17:17
  • @codeit - just to answer the latter question "why myfun and self.myfun refers to same method" - AFAIK that's just like that in Ruby when it comes to calling any method: if self is omitted it's still impied. (OTOH: def self.myfun.. would be entirely different: a class method) – inger Mar 04 '13 at 17:21
  • I believe it is just a coincidence (due to implementation) and IRB did not intend that. It matters only in standalone programs. ("Why" is another question :-) – Simon Perepelitsa Mar 04 '13 at 17:28
  • @inger You mean there is no difference in defining with `self` and without `self` methods in `irb`. – Rahul Tapali Mar 04 '13 at 17:29
  • @SemyonPerepelitsa when you define a method in `irb` and `script` (say `def a; puts "hello"; end`) When you call `a` or `self.a` in `irb` it will refers to same method. But not in `script`. Why is that? Why the class of `a` and `self.a` are different? – Rahul Tapali Mar 04 '13 at 17:39
  • They do refer to the same method in both scenarios. In a script it is just private: you can't call it with explicit receiver (self.a). I don't know what you mean by "class" of the methods. – Simon Perepelitsa Mar 04 '13 at 17:50
  • I have expanded my answer, check it out if you're still curios about IRB implementation. – Simon Perepelitsa Apr 14 '13 at 23:51