Глобальные процедуры в Ruby на самом деле не являются глобальными процедурами. Это методы, как и все остальное. В частности, когда вы определяете, как выглядит глобальная процедура, вы фактически определяете частный метод экземпляра Object
. Поскольку каждый фрагмент кода в Ruby оценивается в контексте объекта, это позволяет вам использовать эти методы, как если бы они были глобальными процедурами, поскольку self
является получателем по умолчанию, а self
— это объект, класс которого наследуется от Object
.
Итак, это:
# file1.rb
def foo
puts 123
end
на самом деле эквивалентно
# file1.rb
class Object
private
def foo
puts 123
end
end
Теперь у вас есть "глобальная процедура" с именем foo
, которую вы можете вызвать следующим образом:
foo
Причина, почему вы можете называть это так, заключается в том, что этот вызов на самом деле эквивалентен
self.foo
а self
— это объект, который включает Object
в свою наследственную цепочку, поэтому он наследует закрытый метод foo
.
[Примечание: если быть точным, приватные методы нельзя вызывать с явным получателем, даже если этим явным получателем является self
. Итак, если быть по-настоящему педантичным, это фактически эквивалентно self.send(:foo)
, а не self.foo
.]
A.new.foo
в вашем file2.rb
— отвлекающий маневр: вы могли бы также попробовать Object.new.foo
, [].foo
или 42.foo
и получить тот же результат.
Между прочим: puts
и require
сами являются примерами таких "глобальных процедур", которые на самом деле являются приватными методами в Object
(точнее, это приватные методы в Kernel
, которые смешаны с Object
).
На заметку: это действительно плохой стиль помещать вызовы require
внутри определения класса, потому что это создает впечатление, что код require
d каким-то образом ограничен областью действия или пространством имен внутри класса, что, конечно же, неверно. require
просто запускает код в файле, не более того.
Так что пока
# file2.rb
class A
require 'file1.rb'
end
вполне допустимый код, он также очень сбивает с толку. Гораздо лучше использовать следующий семантически эквивалентный код:
# file2.rb
require 'file1.rb'
class A
end
Таким образом, читателю кода будет совершенно ясно, что file1.rb
никоим образом не ограничен областью действия или пространством имен внутри A
.
Кроме того, обычно рекомендуется не указывать расширение файла, т. е. использовать require 'file1'
вместо require 'file1.rb'
. Это позволяет заменить файл Ruby, например, собственным кодом (для MRI, YARV, Rubinius, MacRuby или JRuby), байт-кодом JVM в файле .jar
или .class
(для JRuby), байт-кодом CIL в файле .dll
(для IronRuby) и так далее, без необходимости изменять какие-либо из ваших require
вызовов.
И последнее замечание: идиоматический способ обойти защиту доступа — использовать send
, а не instance_eval
, то есть использовать A.new.send(:foo)
вместо A.new.instance_eval {foo}
.
person
Jörg W Mittag
schedule
10.01.2012
A.new.instance_eval{foo}
? У меня не работает (ruby 1.9.2). - person knut   schedule 10.01.2012require with
include`? - person Andrew Grimm   schedule 11.01.2012