Импортированные из Ruby методы всегда закрыты?
Это лучше всего объяснить на примере:
file1.rb:
def foo
puts 123
end
file2.rb:
class A
require 'file1'
end
A.new.foo
выдаст ошибку "': вызван закрытый метод'foo'".
Я могу обойти это, делая A.new.send("foo")
но есть ли способ сделать импортированные методы общедоступными?
Изменить: Чтобы уточнить, я не путаю, включают и требуют. Кроме того, причина, по которой я не могу использовать нормальное включение (как правильно отметили многие), заключается в том, что это является частью настройки метапрограммирования. Мне нужно разрешить пользователю добавлять функциональность во время выполнения; например, он может сказать "run-this-app --include file1.rb", и приложение будет вести себя по-разному в зависимости от кода, который он написал в file1.rb. Извините, должен был объяснить более четко.
Изменить: После прочтения ответа Йорга я понял, что мой код не ведет себя точно так, как задумано, и он прекрасно отвечает на мой (ошибочный) вопрос. Я пытаюсь сделать что-то более похожее на str=(entire file1.rb as string); A.class_exec(str)
,
3 ответа
Глобальные процедуры в 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}
,
Это плохой способ сделать это в Ruby. Попробуйте использовать mixins через модули:
file1.rb:
module IncludesFoo
def foo
puts 123
end
end
file2.rb:
require 'file1.rb'
class A
include IncludesFoo
end
A.new.foo
# => 123