Импортированные из 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

Как насчет load("file1", A)? ( Ссылка RDoc)

Другие вопросы по тегам