【问题标题】:RSpec raises NameErrors for code in subdirectory of /libRSpec 为 /lib 子目录中的代码引发 NameErrors
【发布时间】:2015-05-20 07:05:23
【问题描述】:

我有一个结构如下的 gem 项目:

foo-bar
├── lib
│   └── foo
│       ├── bar
│       │   └── qux.rb
│       └── bar.rb
└── spec
    ├── spec_helper.rb
    └── unit
        ├── baz_spec.rb
        └── qux_spec.rb

lib/foo 中,bar.rb 定义了一个模块Foo::Bar,在其中定义了一个类Foo::Bar::Baz。在lib/foo/barqux.rb 内部定义了一个类Foo::Bar::Qux

spec_helper.rb 设置 RSpec 和 Simplecov,并以require 'foo/bar' 结束。 baz_spec.rbqux_spec.rb 都以 require 'spec_helper' 开头。

baz_spec.rb 具有Foo::Bar::Baz 的规格,并且工作正常。但是,具有 Foo::Bar::Qux 规范的 qux_spec.rb 失败并显示:

/Users/me/foo-bar/spec/unit/qux_spec.rb:6:in `<module:Bar>': uninitialized constant Foo::Bar::Qux (NameError)
    from /Users/me/foo-bar/spec/unit/qux_spec.rb:4:in `<module:Foo>'
    from /Users/me/foo-bar/spec/unit/qux_spec.rb:3:in `<top (required)>'
    from /Users/me/.rvm/gems/ruby-2.2.0/gems/rspec-core-3.2.2/lib/rspec/core/configuration.rb:1226:in `load'
    ...
    (etc.)

我通过将Foo::Bar::Baz 的代码从lib/foo/bar.rb 移出到它自己的文件lib/foo/bar/baz.rb 中验证了这不仅仅是一个错字,之后baz_spec.rb 也停止工作。

我是否将类声明为似乎也没有什么区别

class Foo::Bar::Qux
  ...

或作为

module Foo
  module Bar
    class Qux
      ...

我在 Mac OS X Yosemite 上使用 Ruby 2.2.0 和 RSpec 3.2.2。

很明显,我的requires 有问题,但作为一个 Ruby 新手,我没有看到它。有什么想法吗?

【问题讨论】:

  • 可能的解决方案取决于您使用的框架类型。那么,您使用的是 Rails 还是 ActiveSupport?
  • 两者都不是。好吧,我想 ActiveSupport 在那里,因为我的 gemspec 中有 ActiveJob,但它还没有被使用。

标签: ruby rspec rspec3 ruby-2.2


【解决方案1】:

您需要在./lib 下添加foo.rb 文件,并为每个文件添加require 语句,以便加载它们。您可以查看示例 gem 以供参考:dogeify 和一篇指导您完成 gem 创建的文章 build your first gem

【讨论】:

    【解决方案2】:

    如果您使用的是ActiveSupport,这样的一行代码将对您有所帮助:

    ActiveSupport::Dependencies.autoload_paths << "./lib"
    

    如果您现在尝试使用Foo::Bar::Qux,ActiveSupport 将在lib 文件夹中查找名为foo/bar/qux.rb 的文件。

    【讨论】:

    • 我不建议使用 ActiveSupport 自动加载功能,因为它引入了您可能不需要的依赖项,并且也无法控制文件加载的顺序。此外,如果您使用的是最新版本,请考虑使用 ruby​​ 的内置自动加载功能 (ruby-doc.org/core-2.1.0/Module.html#method-i-autoload)
    【解决方案3】:

    已解决:在仔细查看了我参与 cargo-culting 的其他项目后,我意识到我误解了 require

    与(显然)它的 Rails 等效项不同,开箱即用的 Kernel.require 只加载 .rb 文件(和扩展库)。所以在上面的例子中,require 'foo/bar' 不会从foo/bar 目录加载文件,它只是加载foo/bar.rb

    为了加载foo/bar 下的文件,包括qux.rb,我必须进入bar.rb 并在模块声明的顶部显式加载这些文件:

    module Foo
      module Bar
        Dir.glob(File.expand_path('../bar/*.rb', __FILE__), &method(:require))
    
        # ...module declaration continues...
    

    我想,这只是等待那些从其他更重量级语言转向 Ruby 的人所面临的众多脚本语言传统陷阱之一。

    【讨论】:

      猜你喜欢
      • 2011-02-28
      • 2013-01-08
      • 1970-01-01
      • 2013-06-02
      • 2023-03-05
      • 2022-11-07
      • 2011-02-17
      • 1970-01-01
      • 2011-12-02
      相关资源
      最近更新 更多