Это сложно. Это действительно не должно работать в Ruby, но оно работает.
def func
на верхнем уровне определяет глобальный метод. Но как это работает в Ruby? На самом деле он использует два хака:
- Он добавляет метод к
Object
, чтобы вы могли вызывать его из любого места (поскольку self
всегда является Object
) - Этоделает метод
private
, потому что правило для частных методов состоит в том, что у них не может быть «явного получателя», то есть вы должны вызывать их, не ставя перед ними объект и точку. Это предотвращает ошибки, например, если я думаю, что у класса Foo
есть собственный метод puts
, но на самом деле его нет, Foo.new.puts
вызовет ошибку, а не вызовет глобальный puts
.
Так что, если бы ваш код был просто
def func
1
end
1.func
Сбой, потому что вы пытаетесь вызвать закрытый метод для объекта 1
.
Но вот где это становится странным. Если вы определяете метод внутри другого метода, он определяет обычный метод экземпляра, как если бы он вообще не был вложенным
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
Это не было намеренным проектом в Ruby, носкорее запасной вариант для странной ситуации. ведущему конструктору не нравится, что это даже возможно
... текущее поведение определения вложенного метода бесполезно. Это должно быть сделано устаревшим, чтобы открыть будущую возможность (я бы проголосовал за предупреждение).
Ваш код ведет себя так странно, потому что вы делаете это на верхнем уровне, где self
просто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