Ruby: как загрузить файл .rb в локальном контексте
Как эта простая задача может быть выполнена в Ruby?
У меня есть простой файл конфигурации
=== config.rb
config = { 'var' => 'val' }
Я хочу загрузить файл конфигурации из некоторого метода, определенного в файле main.rb
, чтобы локальные переменные из config.rb
стали локальными варами этого метода.
Что-то вроде этого:
=== main.rb
Class App
def loader
load('config.rb') # or smth like that
p config['var'] # => "val"
end
end
Я знаю, что я могу использовать глобальные vars в config.rb
, а затем undefine их по завершении, но я надеюсь, что там рубиновый путь)
Ответы
Ответ 1
Я НЕ рекомендую это делать, кроме как в контролируемой среде.
Сохраните модуль в файл с заданным именем, которое определяет методы initialize
и run_it
. В этом примере я использовал test.rb в качестве имени файла:
module Test
@@classvar = 'Hello'
def initialize
@who = 'me'
end
def get_who
@who
end
def run_it
print "#{@@classvar} #{get_who()}"
end
end
Затем напишите простое приложение для загрузки и выполнения:
require 'test'
class Foo
include Test
end
END {
Foo.new.run_it
}
# >> Hello me
Просто потому, что вы можете что-то сделать, это не значит, что вам нужно. Я не могу придумать причину, по которой я бы сделал это в производстве, и только показать ее здесь как любопытство и доказательство концепции. Сделать это доступным для неизвестных людей было бы хорошим способом взломать вашу машину, потому что код мог делать все, что могла сделать собственная учетная запись.
Ответ 2
Файл конфигурации.
{ 'var' => 'val' }
Загрузка конфигурационного файла
class App
def loader
config = eval(File.open(File.expand_path('~/config.rb')).read)
p config['var']
end
end
Ответ 3
Конечно, вы могли бы взломать решение, используя eval и File.read, но тот факт, что это сложно, должно дать вам сигнал о том, что это не рубиноподобный способ решения проблемы, которая у вас есть. Два альтернативных варианта использования yaml для вашего конфигурационного api или определения простого dsl.
Случай YAML является самым простым, вы просто должны иметь что-то вроде этого в main.rb:
Class App
def loader
config = YAML.load('config.yml')
p config['var'] # => "val"
end
end
и ваш файл конфигурации будет выглядеть так:
---
var: val
Ответ 4
Как говорили другие, для конфигурации лучше использовать YAML или JSON. Чтобы просмотреть файл
binding.eval(File.open(File.expand_path('~/config.rb')).read, "config.rb")
дел > binding.eval(File.read(File.expand_path('~/config.rb')), "config.rb")
Этот синтаксис позволит вам видеть имя файла в обратных трассах, что важно. См. Api docs [1].
Обновлена команда eval
, чтобы избежать утечки FD (файлового дескриптора). Я, должно быть, спал или, возможно, должен был спать в то время ночи, а не писать в stackoverflow.
[1] http://www.ruby-doc.org/core-1.9.3/Binding.html
Ответ 5
Мне просто нужно было сделать то же самое, что я хотел бы загрузить "Ruby DLL", где он возвращает анонимный класс (factory для экземпляров вещей). Я создал это, которое отслеживает уже загруженные элементы и позволяет загруженному файлу возвращать значение, которое может быть любым - полностью анонимным классом, модулем, данными и т.д. Это может быть модуль, который вы могли бы затем "включить" в объект после его загрузки, и он мог бы предоставить хост "атрибутов" или методов. вы также можете добавить элемент "выгрузить", чтобы очистить его от загруженного хеша и разыменовать любой объект, который он загрузил.
module LoadableModule
@@loadedByFile_ = {};
def self.load(fileName)
fileName = File.expand_path(fileName);
mod = @@loadedByFile_[fileName];
return mod if mod;
begin
Thread.current[:loadReturn] = nil;
Kernel.load(fileName);
mod = Thread.current[:loadReturn];
@@loadedByFile_[fileName] = mod if(mod);
rescue => e
puts(e);
puts(e.backtrace);
mod = nil;
end
Thread.current[:loadReturn] = nil;
mod
end
def self.onLoaded(retVal)
Thread.current[:loadReturn] = retVal;
end
end
внутри загруженного файла:
LoadableModule.onLoaded("a value to return from the loaded file");