Непоследовательное поведение «LoadError» с пространством имен / автозагрузкой «lib» - PullRequest
24 голосов
/ 16 августа 2011

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

/ Библиотека / response_set.rb:

module MyCompany
  class ResponseSet < Array
    ...
  end
end

/ спецификации / Библиотека / response_set_spec.rb

require 'spec_helper'

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

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

/Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_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/ruby-1.9.2-p136@my_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:183:in `block in const_missing'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `each'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `const_missing'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/rspec-core-2.5.1/lib/rspec/core/backward_compatibility.rb:20:in `const_missing'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_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/ruby-1.9.2-p136@my_project/gems/rspec-core-2.5.1/lib/rspec/core/configuration.rb:386:in `load'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_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/ruby-1.9.2-p136@my_project/gems/rspec-core-2.5.1/lib/rspec/core/configuration.rb:386:in `map'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/rspec-core-2.5.1/lib/rspec/core/configuration.rb:386:in `load_spec_files'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/rspec-core-2.5.1/lib/rspec/core/command_line.rb:18:in `run'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/rspec-core-2.5.1/lib/rspec/core/runner.rb:55:in `run_in_process'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/rspec-core-2.5.1/lib/rspec/core/runner.rb:46:in `run'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_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/ruby-1.9.2-p136@my_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:492:in `load_missing_constant'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:183:in `block in const_missing'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `each'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `const_missing'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:503:in `load_missing_constant'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:183:in `block in const_missing'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `each'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `const_missing'
    from (irb):1
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/railties-3.0.4/lib/rails/commands/console.rb:44:in `start'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_project/gems/railties-3.0.4/lib/rails/commands/console.rb:8:in `start'
    from /Users/my_stuff/.rvm/gems/ruby-1.9.2-p136@my_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' с похожим пространством имен, которые никогда не вызывали никаких проблем. Кто-нибудь может объяснить, что, черт возьми, здесь происходит?

Ответы [ 5 ]

27 голосов
/ 18 сентября 2011

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

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

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

Мы обошли это сейчас, удалив lib/** из нашего autoload_paths и добавления необходимых операторов require в application.rb.

4 голосов
/ 05 октября 2011

Брендан указал в правильном направлении, проверьте ваши 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 голосов
/ 17 августа 2011

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

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, это может быть причиной того, что при втором вызове в вашем примере нет ошибки ...

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

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

1 голос
/ 27 сентября 2012

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

Поскольку наши autoload_path включали в себя как lib, так и lib / **, Rails извлек все файлы из всех подкаталогов в lib. К сожалению, он игнорирует подразумеваемое пространство имен при загрузке из подкаталогов. Ожидается, что foo / base.rb определит Base против 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

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

0 голосов
/ 11 сентября 2014

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...