Подтвердить что ты не робот

Непоследовательное поведение "LoadError" с использованием "lib" namespacing/autoloading

Мы только что создали новый файл в 'lib', который породил серию головных болей с ошибками загрузки.

/lib/response_set.rb:

module MyCompany
  class ResponseSet < Array
    ...
  end
end

/spec/lib/response_set_spec.rb

require 'spec_helper'

describe MyCompany::ResponseSet do
  describe "..." do
    ...
  end
end

Запуск этой спецификации в Rspec дает нам следующую ошибку, когда она попадает в первый "описать":

/Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:492:in `load_missing_constant': Expected /Users/my_stuff/projects/my_project/lib/response_set.rb to define ResponseSet (LoadError)
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:183:in `block in const_missing'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `each'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `const_missing'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/backward_compatibility.rb:20:in `const_missing'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-expectations-2.5.0/lib/rspec/expectations/backward_compatibility.rb:6:in `const_missing'
    from /Users/my_stuff/projects/my_project/spec/lib/response_set_spec.rb:4:in `<top (required)>'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/configuration.rb:386:in `load'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/configuration.rb:386:in `block in load_spec_files'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/configuration.rb:386:in `map'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/configuration.rb:386:in `load_spec_files'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/command_line.rb:18:in `run'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/runner.rb:55:in `run_in_process'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/runner.rb:46:in `run'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/runner.rb:10:in `block in autorun'

ОДНАКО! Мы использовали много других файлов в течение долгого времени, которые имеют идентичную структуру. Например, здесь еще один, который работает отлично с момента его создания:

/lib/smart_set.rb

module MyCompany
  class SmartSet < Array
    ...
  end
end

И/spec/lib/smart_set_spec.rb

require 'spec_helper'

describe MyCompany::SmartSet do
  describe "..." do
    ...
  end
end

Этот файл имеет идентичную структуру, но не создает никаких проблем.

ResponseSet (класс проблем), по-видимому, имеет проблемы с загрузкой по какой-либо заметной причине. В консоли rails, в первый раз, когда я пытаюсь создать его, я получаю сообщение об ошибке, но потом я могу создать его потом:

Loading development environment (Rails 3.0.4)
ruby-1.9.2-p136 :001 > rs = MyCompany::ResponseSet.new
LoadError: Expected /Users/my_stuff/projects/my_project/lib/response_set.rb to define ResponseSet
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:492:in `load_missing_constant'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:183:in `block in const_missing'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `each'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `const_missing'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:503:in `load_missing_constant'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:183:in `block in const_missing'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `each'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `const_missing'
    from (irb):1
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/railties-3.0.4/lib/rails/commands/console.rb:44:in `start'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/railties-3.0.4/lib/rails/commands/console.rb:8:in `start'
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/railties-3.0.4/lib/rails/commands.rb:23:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'
ruby-1.9.2-p136 :002 > rs = MyCompany::ResponseSet.new
 => [] 

Кроме того, добавив

require 'response_set'

в верхней части response_set_spec.rb позволяет выполнять эти тесты. Но ничего не нужно для smart_set_spec.rb.

В application.rb следующее:

config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
config.autoload_paths += Dir["#{config.root}/app/models/**/"]

Теперь я понимаю, что Rails имеет свое мнение о том, как структура файлов должна соответствовать структуре пространства имен для этих типов вещей, и мы изменили наши модули и файлы в этом направлении. Это ПОСМОТРЕТЬ, чтобы устранить проблему (хотя мы некоторое время наблюдали некоторые другие странные ошибки загрузки, когда мы запускали полный набор тестов - они таинственно исчезли). Тем не менее все здесь сбиты с толку и не слишком раздражены тем, что Rails настолько непоследовательна, и нам хотелось бы знать, почему. Как вы можете видеть, есть два файла, которые идентичны по отношению к пространству имен и структуре файлов, и обрабатываются совершенно по-разному. На самом деле у нас есть около десятка других файлов на верхнем уровне "lib" с похожим пространством имен, которые никогда не вызывали никаких проблем. Может кто-нибудь объяснить, что здесь происходит?

4b9b3361

Ответ 1

У нас была аналогичная проблема, которая после копания оказалась вызвана изменениями в Rails 3.x и autoload_paths.

Наш случай появился только в тесте (RAILS_ENV=test). Когда Rails загружал контроллеры, он искал поиск каждой модели, соответствующей сопоставлению с контроллером (из-за параметров ActionController:: Base.wrap_parameters, установленных в инициализаторе). В конце концов он опустился до метода, упомянутого выше (load_missing_constant). Поскольку наши autoload_paths включали как lib, так и lib/**, Rails вытащил все файлы из всех подкаталогов в lib. К сожалению, он игнорирует подразумеваемое пространство имен при загрузке из подкаталогов. Он ожидал, что foo/base.rb определит Base vs. Foo::Base. Это, по-видимому, основной недостаток: load_missing_constant вызывает search_for_file, который возвращает любой файл, имя которого совпадает (например, в моем примере, foo/base.rb был возвращен по совпадению base.rb).

Трудно сказать, является ли это ошибкой в ​​Rails, поскольку она нарушает отображение пространства имен в каталог, предполагаемое Ruby, или неправильное использование autoload_paths.

Мы уже работали над этим, удалив lib/** из нашего autoload_paths и добавив необходимые необходимые инструкции в application.rb.

Ответ 2

Я просмотрел источник рельсов, и есть

if file_path && ! loaded.include?(File.expand_path(file_path)) # We found a matching file to load
  require_or_load file_path
  raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless local_const_defined?(from_mod, const_name)
  return from_mod.const_get(const_name)
elsif ...

в load_missing_constant. Я могу догадаться, что, когда require_or_load вызывается до raise, это может быть причиной того, что во втором вызове в вашем примере нет ошибки...

Было бы интересно увидеть минимальный пример, в котором два файла с одинаковой структурой ведут себя по-разному. На вашем месте я сделаю копию приложения и продолжаю удалять части из него, пока присутствует непоследовательное поведение, чтобы увидеть минимальный непоследовательный пример.

P.S. Я поставил аналогичный вопрос здесь: http://www.ruby-forum.com/topic/2376956

Ответ 3

Указывается в правильном направлении Бренданом, проверьте свои autoload_paths. У меня была аналогичная ошибка, и это было преступником для меня в application.rb:

config.autoload_paths += Dir["#{Rails.root}/app/models/[a-z]*"]

Мое приложение не понравилось, когда я начал использовать модули для моделей вместе с подкаталогами. Я поменял мой, чтобы автоматически загружать немодульные каталоги:

config.autoload_paths += Dir["#{Rails.root}/app/models/aaaaaaaaa"]
config.autoload_paths += Dir["#{Rails.root}/app/models/bbbbbbbbb"]

Ответ 4

Я страдал от той же самой проблемы, когда классы с именами были автоматически загружены автозагрузкой. Это ключ к его решению (из ответа Брендана):

Поскольку наши autoload_paths включали в себя как lib, так и lib/**, Rails вытащил все файлы из всех поддиректорий под lib. К сожалению, он игнорирует подразумеваемое пространство имен при загрузке из подкаталогов. Он ожидал, что foo/base.rb определит Base vs. Foo:: Base. Это, по-видимому, основной недостаток: load_missing_constant вызывает файл search_for_file, который возвращает любой файл, имя которого совпадает (например, в моем примере, foo/base.rb было возвращено по совпадению base.rb).

И я решил это, переместив все мои классы с именами, названные Events::Something, в app/components/events/* и создав файл app/components/events.rb со следующим содержимым:

%w(file1 file2 ...).each do |file|  
  require "events/#{file}"
end

И хотя это немного ручно, оно работает как для приложений, так и для консольных и тестовых режимов.

Ответ 5

Еще один момент, который стоит отметить, заключается в том, что у вас есть правильный путь в папке служб.

По правильному пути я имею в виду

ENGINE_NAME/приложение/услуги/ENGINE_NAME/your_service.rb

Я случайно забыл папку engine_name внутри служб и получил это странное поведение.