使用'lib'命名空间/自动加载不一致的"LoadError"行为

Nic*_*k M 24 namespaces ruby-on-rails-3

我们刚刚在'lib'中创建了一个新文件,它产生了一系列涉及加载错误的麻烦.

/lib/response_set.rb:

module MyCompany
  class ResponseSet < Array
    ...
  end
end
Run Code Online (Sandbox Code Playgroud)

/spec/lib/response_set_spec.rb

require 'spec_helper'

describe MyCompany::ResponseSet do
  describe "..." do
    ...
  end
end
Run Code Online (Sandbox Code Playgroud)

在Rspec中运行此规范会在到达第一个'describe'时出现以下错误:

/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'
Run Code Online (Sandbox Code Playgroud)

然而!我们长期使用许多其他文件具有相同的结构.例如,这是另一个自创建以来一直工作正常的:

/lib/smart_set.rb

module MyCompany
  class SmartSet < Array
    ...
  end
end
Run Code Online (Sandbox Code Playgroud)

和/spec/lib/smart_set_spec.rb

require 'spec_helper'

describe MyCompany::SmartSet do
  describe "..." do
    ...
  end
end
Run Code Online (Sandbox Code Playgroud)

此文件具有相同的结构,但完全没有问题.

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
 => [] 
Run Code Online (Sandbox Code Playgroud)

另外,添加

require 'response_set'
Run Code Online (Sandbox Code Playgroud)

在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/**/"]
Run Code Online (Sandbox Code Playgroud)

现在,我理解Rails对文件结构如何匹配这些类型的命名空间结构有一些意见,我们已经为此重组了我们的模块和文件.它看起来已经解决了这个问题(虽然我们在运行完整的测试套件时会看到一些其他奇怪的负载错误 - 这些已经神秘地消失了).尽管如此,这里的每个人都感到困惑,并且对Rails如此不一致并且我们想知道原因并不感到有点恼火.正如您所看到的那样,就命名空间和文件结构而言,有两个文件完全相同,完全不同.事实上,我们在"lib"的顶层有大约十几个其他文件,这些文件具有类似的命名空间,从未引起任何问题.谁能解释这里到底发生了什么?

Bre*_*dan 27

我们有一个类似的问题,经过挖掘,结果是由Rails 3.x和autoload_paths.的变化引起的.

我们的案例只出现在test(RAILS_ENV=test)中.当Rails加载控制器时,它忙于寻找每个控制​​器的匹配模型(由于在初始化器中设置了ActionController :: Base.wrap_parameters).最终它结束了上面提到的方法(load_missing_constant).由于我们的autoload_paths包含lib和lib/**,因此Rails从lib下的所有子目录中提取所有文件.不幸的是,从子目录加载时似乎忽略了隐含的命名空间.它期望foo/base.rb定义Basevs Foo::Base..这似乎是核心缺陷:load_missing_constant所调用search_for_file返回其名称匹配(例如,在我的例子,被送回富/ base.rb因为它匹配base.rb)的任何文件.

很难说这是否是Rails中的错误 - 因为它违反了Ruby假定的命名空间到目录的映射 - 或者滥用了autoload_paths.

我们现在解决了这个问题,方法是lib/**从我们中删除autoload_paths并向application.rb添加必要的require语句.

  • Rails开始惹恼我. (8认同)
  • 我不得不做同样的事情.我认为你在关于根本问题的正确轨道上......惊讶没有人抱怨这个. (2认同)
  • @Zabba 6个月进入rails dev并且我厌倦了它.我想要一个真正的语言/框架. (2认同)
  • 似乎问题包括lib /和lib/**.通过使用lib/**,每个嵌套文件夹都被视为根搜索文件夹,因此lib/foo/base.rb中的文件在逻辑上应该包含Base.如果你只包含lib /那么foo/base将包含Foo :: Base. (2认同)

Ale*_*xey 4

我查看了 Rails 源代码,有一个

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 ...
Run Code Online (Sandbox Code Playgroud)

方法中的子句load_missing_constant。我可能猜想,正如require_or_load之前所调用的那样raise,这可能是您的示例中第二次调用时没有错误的原因......

看到一个最小的例子会很有趣,其中两个具有相同结构的文件的行为不同。在您的位置,我将复制该应用程序,并在出现不一致行为时不断从中删除某些部分,以查看最小的不一致示例。

PS我在这里提交了类似的问题:http ://www.ruby-forum.com/topic/2376956