RSpec 为 /lib 的子目录中的代码引发 NameErrors

RSpec raises NameErrors for code in subdirectory of /lib

我有一个具有以下结构的 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,并且在其中定义了一个classFoo::Bar::Baz。在lib/foo/bar,qux.rb里面定义了一个classFoo::Bar::Qux.

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

开头

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

/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 声明为

似乎也没有什么区别
class Foo::Bar::Qux
  ...

module Foo
  module Bar
    class Qux
      ...

我在 Mac OS X Yosemite.

上使用 Ruby 2.2.0 和 RSpec 3.2.2

显然我的 requires 有问题,但作为一个 Ruby 新手,我没有发现它。有什么想法吗?

如果您正在使用 ActiveSupport,像这样的单行将帮助您:

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

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

您需要在 ./lib 下添加 foo.rb 文件并为每个文件添加 require 语句以便加载它们。您可以查看示例 gem 以供参考:dogeify and an article that walks you through gem creation build your first gem.

已解决:仔细查看了我 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 的人的众多脚本语言继承陷阱之一。