Глобальные процедуры в 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
).
На sidenote: это действительно плохой стильпомещать вызовы к 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}
.