This is complicated. This really shouldn't work in Ruby, but it does.
def func at the top-level defines a global method. But how does that work in Ruby? It actually uses two hacks:
- It adds the method to
Object so that you can call it from anywhere (because self is always an Object)
- It makes the method
private, because the rule for private methods is that they can't have an "explicit receiver" i.e. you have to call them without putting an object and a dot before them. This is to prevent mistakes like if I think that a class Foo has its own puts method but it actually doesn't, Foo.new.puts will raise an error, rather than call the global puts.
So if your code were just
def func
1
end
1.func
It would crash because you're trying to call a private method on the object 1.
But here's where it gets weird. If you define a method inside another method, it defines a normal instance method as if it were not nested at all
class A
def outer
def inner
3
end
end
end
x = A.new
y = A.new
x.outer
y.inner # calls the method defined by x
This wasn't really an intentional design in Ruby, but more of a fallback for a weird situation. The lead designer doesn't like that it's even possible
...the current behavior of nested method definition is useless. It should be made obsolete to open up the future possibility (I'd vote for warning).
Your code behaves so strangely because you're doing it at the top level where self is just an Object:
def func # normal private method in Object
def func # adds a normal instance method to the current class i.e. Object
1
end
end
Object.private_methods.include?(:func) # true
func # returns :func, but also now re-defines func to be a normal method on Object
Object.private_methods.include?(:func) # false
func.func # same as 1.func, which is OK because it's not private