Reputation: 12439
I decided to experiment with defining methods inside methods in Ruby and wrote this:
def foo
def bar
puts "hello"
end
bar
end
This defined, I ran foo
and "hello" was printed as I expected. However I then tried foo.bar
- which printed out "hello" twice. Why?
Upvotes: 2
Views: 327
Reputation: 15010
foo.bar
equals foo().bar()
so you call first foo
which contains bar
so it is executed once here, then bar
is executed one more time. At the end bar
is called twice.
def foo
puts 'foo called'
def bar
puts 'bar called'
puts "hello"
end
bar
end
foo.bar
#=> foo called
#=> bar called
#=> hello
#=> bar called
#=> hello
What I don't understand is this means the result of foo
which is nil
can call bar
.
>> nil.bar
#=> hello
How is that possible ?
Edit: As explained in some answers, nested method are included in the class scope so weither bar is declared inside or outside foo does not make any difference.
Also Matz said that it may change from Ruby2.0
here is the example he wrote:
class Foo
def foo
def bar
#omited
end
bar # can be called
end
def quux
bar # cannot be called
end
end
edit-Ruby2.0: It finally did not get implemented. So it remains the same.
Upvotes: 4
Reputation: 31756
In Ruby, there is a hidden variable that doesn't get much attention. I don't know what it's called, but I call it the current class. This variable is the target of def
When you go into foo
, it doesn't change this variable, you're still in the same class. So when you define another method inside of foo, that method gets defined on the same class as foo was defined on.
In this case, you're defining foo on main, the toplevel object. Main's current class variable is set to object, so any methods you define here will be available inside any object.
Since the last thing you do is return the result of bar, and bar returns nil (b/c puts returns nil), then foo.bar
is nil.bar
. And since you defined bar on Object, even nil inherits this method.
The interesting part is that bar is public. Since foo is defined in main, and main sets its scope to private, I would have expected bar to also be private, but I guess that state must lost once you go to a different scope.
Upvotes: 2
Reputation: 5895
Did you mean to put class foo
? It seems that calling foo.bar calls the function foo and then the function bar. Calling nil.bar
prints out 'hello' too.
Upvotes: 1