2015-07-07 5 views
0

Да, я знаю, что могу использовать load вместо require. Но это нехорошее решение для моего варианта использования:Есть ли способ принудительно перезагрузить требуемый файл в Ruby?

Когда приложение загружается, оно requires конфигурационный файл. Каждая среда имеет свою собственную конфигурацию. Конфигурация устанавливает константы.

Когда приложение загружается, требуется только одно окружение. Однако во время тестирования он загружает файлы конфигурации несколько раз, чтобы убедиться, что нет синтаксических ошибок.

В тестовой среде один и тот же файл конфигурации может быть загружен более одного раза. Но я не хочу менять require на загрузку, потому что каждый раз, когда выполняется спецификация, он перезагружает конфигурацию. Это должно быть сделано через require, потому что, если конфигурация уже загружена, она вызывает предупреждения already initialized constant.

Чистое решение, которое я вижу, - это вручную сбросить флаг require для конфигурационного файла после любой конфигурации.

Есть ли способ сделать это в Ruby?

Редактировать: добавить код.

Когда приложение загружается она вызывает файл инициализации:

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 
+0

Вы понимаете, что причина, по которой вы не получаете «уже инициализированную константу» из 'require' is *, потому что * она пропускает загрузку файла. Это не любое другое свойство 'require', которое предотвращает предупреждение - если вы найдете способ переустановить флаг require, то вы * будете получать предупреждения о константах, точно так же, как использовать' load'. –

+0

Да, я знаю. 'Object.send: remove_const, const, если Object.const_defined? const' –

+1

Если вы сделаете это в сочетании с 'load', это решит вашу проблему? Если нет, почему бы и нет? Единственное возражение против «загрузки», которое я понимаю в вашем вопросе, - это предупреждения «уже инициализированные константы» - если есть еще одна причина, по которой вы не можете использовать 'load' в своих спецификациях, я не понимаю, что это такое? –

ответ

2

Да, это может быть сделано. Вы должны знать путь к файлам, которые вы хотите перезагрузить. Существует специальная переменная $LOADED_FEATURES, в которой хранится то, что было загружено, и используется require, чтобы решить, следует ли загружать файл при повторном запросе.

Здесь я предполагаю, что файлы, которые вы хотите повторно потребовать, имеют уникальный путь /myapp/config/ от их имени. Но, надеюсь, вы увидите, что это будет работать для любого правила о имени пути, которое вы можете закодировать.

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

И все. , ,

Некоторые предостережений:

  • require не хранить или следовать какой-либо дерева зависимостей, чтобы знать, что он «должен» быть загружен. Поэтому вам необходимо обеспечить полную цепочку require s, начинающуюся с команды require, которую вы запускаете в спецификации для повторной загрузки конфигурации и включая все необходимое для загрузки, покрывается удаленными путями.

  • Это не выгружает определения классов или константы, а просто перезагружает файлы. Фактически это буквально то, что делает require, оно просто вызывает load внутренне. Таким образом, все предупреждающие сообщения о переопределении констант также должны быть обработаны путем определения неопределенных констант, которые вы ожидаете увидеть в файлах.

  • Возможно, есть дизайн вашей конфигурации и спецификаций, который позволяет избежать этого.

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

Откровенно говоря, я не думаю, что это хорошая идея, независимо от того, какой подход вы выбрали, потому что вы откроете банку червей и имхо, нет действительно чистого способа сделать это ,

+0

Это очень интересный подход. Кажется, это хороший способ изолировать конфигурацию в тестовой среде, не загрязняя код приложения. –

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