2012-01-10 3 views
5

Это лучше всего объяснить на примере:Рубины импортированные методы всегда частные?

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).

+0

Вы попробовали 'A.new.instance_eval {foo}'? Это не работает для меня (ruby 1.9.2). – knut

+0

Вы сбиваете с толку 'require with' include'? –

+0

@knut я сделал. Оно работает. – alexloh

ответ

7

Глобальные процедуры в 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 д кода какой-то образом контекстном или пространства имен внутри класса, который, конечно, неверно. 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}.

+0

Спасибо! Этот ответ действительно завершен и очищает недоразумения, которые у меня были, - которые требуют, чтобы C включал текст с текстом в скобках. Есть ли способ добавить содержимое file1.rb как методы класса A динамически?Как содержимое, так и местоположение файла1 известны только во время выполнения, и пользователь не знает об A. Или я могу найти список методов или модулей, определенных в файле? – alexloh

10

Это плохой способ сделать это в Ruby. Попробуйте использовать Примеси через модули вместо:

file1.rb:

module IncludesFoo 
    def foo 
    puts 123 
    end 
end 

file2.rb:

require 'file1.rb' 

class A 
    include IncludesFoo 
end 

A.new.foo 
# => 123 
+0

Оба «file1.rb» и его содержимое поступают из пользовательского ввода и загружаются динамически. В основном пользователь может ввести путь во время выполнения, и приложение загрузит соответствующий рубиновый файл, который изменит существующие функции без перезагрузки и т. Д. Извините, должно быть, это стало яснее. – alexloh

Смежные вопросы