有没有办法强制在Ruby中重新加载所需的文件?

时间:2015-07-07 18:53:36

标签: ruby require

是的,我知道我可以使用load代替require。但对我的用例来说,这不是一个好的解决方案:

当应用程序启动时,它requires配置文件。每个环境都有自己的配置。配置设置常量。

应用启动时,只需要一个环境。但是,在测试期间,它会多次加载配置文件,以确保没有语法错误。

在测试环境中,可能会多次加载相同的配置文件。但我不想更改require以加载,因为每次规范运行时,它都会重新加载配置。这应该通过require来完成,因为如果配置已经加载,它会引发already initialized constant警告。

我能看到的最干净的解决方案是在任何配置规范之后手动重置配置文件的require标志。

有没有办法在Ruby中做到这一点?

编辑:添加代码。

当应用程序启动时,它会调用init文件:

init.rb:

require "./config/environments/#{ ENV[ 'RACK_ENV' ]}.rb"


config/environments/test.rb:

APP_SETTING = :foo


config/environments/production.rb:

APP_SETTING = :bar


spec/models/config.rb: # It's not a model spec...

describe 'Config' do
  specify do
    load './config/environments/test.rb'
  end

  specify do
    load './config/environments/production.rb'
  end

2 个答案:

答案 0 :(得分:2)

是的,可以做到。您必须知道要重新加载的文件的路径。有一个特殊变量$LOADED_FEATURES,用于存储已加载的内容,并由require用于决定是否在再次请求时加载文件。

这里我假设您要重新申请的文件名称中都有唯一的路径/myapp/config/。但希望您可以看到这适用于您可以编码的路径名称的任何规则。

$LOADED_FEATURES.reject! { |path| path =~ /\/myapp\/config\// }

就是这样。 。 。

一些警告:

  • require不存储或遵循任何类型的依赖树,以了解它应该"应该"装了。因此,您需要确保从您在规范中运行的require命令开始的require完整链,以重新加载配置,并包括您需要加载的所有内容,路径。

  • 这不会卸载类定义或常量,只是重新加载文件。事实上,这实际上是require所做的,它只是在内部调用load。因此,所有关于重新定义常量的警告消息也需要通过取消定义您希望在文件中定义的常量来处理。

  • 您的配置和规格可能设计无需执行此操作。

答案 1 :(得分:1)

如果你真的想这样做,这是一种不会泄漏到你的测试过程中的方法。为您要测试的每个配置文件分叉一个进程,通过IO.pipe将状态传回测试进程,并根据结果失败/继续测试。

你可以随心所欲地使用你发送给管道的东西......

这是一个快速而又肮脏的例子,向您展示我的意思。

配置

# foo.rb
FOO = "from foo"

另一个配置

# bar.rb
FOO = "from bar"

一些错误的配置

# witherror.rb
asdf

和你的"测试"

# yourtest.rb
def load_config(writer, config_file)
  fork do
    begin
      require_relative config_file
      writer.write "success: #{FOO}\n"

    rescue
      writer.write "fail: #{$!.message}\n"
    end
    writer.close
    exit # maybe this is even enough to NOT make it run your other tests...
  end
end

rd, writer = IO.pipe

load_config(writer, "foo.rb")
load_config(writer, "bar.rb")
load_config(writer, "witherror.rb")
writer.close

puts rd.read
puts rd.read
puts rd.read   
puts FOO

输出结果为:

success: from foo
success: from bar
fail: undefined local variable or method `asdf' for main:Object
yourtest.rb:24:in `<main>': uninitialized constant FOO (NameError)

如您所见,FOO常数不会泄漏到您的测试过程等。 当然,你只有一半的时间,因为还有更多的东西,确保只有一个进程运行测试等。

坦率地说,我不认为这是一个好主意,无论你选择何种方法,因为你会打开一堆蠕虫,而且那里没有真正干净的方法。