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");