David Moles
David Moles

Reputation: 51229

RSpec raises NameErrors for code in subdirectory of /lib

I have a gem project with the following structure:

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

In lib/foo, bar.rb defines a module Foo::Bar, and inside that, a class Foo::Bar::Baz. Inside lib/foo/bar, qux.rb defines a class Foo::Bar::Qux.

spec_helper.rb sets up RSpec and Simplecov, and finishes with require 'foo/bar'. Both baz_spec.rb and qux_spec.rb start with require 'spec_helper'.

baz_spec.rb has the specs for Foo::Bar::Baz, and it works fine. qux_spec.rb, however, which has the specs for Foo::Bar::Qux, fails with:

/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.)

I've verified that it's not just a typo by moving the code for Foo::Bar::Baz out of lib/foo/bar.rb and into its own file, lib/foo/bar/baz.rb, after which baz_spec.rb also stops working.

It also doesn't seem to make a difference whether I declare the class as

class Foo::Bar::Qux
  ...

or as

module Foo
  module Bar
    class Qux
      ...

I'm using Ruby 2.2.0 with RSpec 3.2.2 on Mac OS X Yosemite.

Clearly there's something wrong with my requires, but as a Ruby novice I'm not seeing it. Any ideas?

Upvotes: 1

Views: 263

Answers (3)

David Moles
David Moles

Reputation: 51229

Solved: after taking a closer look at the other projects I was cargo-culting from, I realized I'd misunderstood require.

Unlike (apparently) its Rails equivalent, the out-of-the-box Kernel.require just loads .rb files (and extension libraries). So in the example above, require 'foo/bar' doesn't load files from the foo/bar directory, it just loads foo/bar.rb.

In order to load the files under foo/bar, including qux.rb, I had to go into bar.rb and explicitly load those files at the top of the module declaration:

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

    # ...module declaration continues...

Just one of the many scripting-language-heritage pitfalls waiting for those who come to Ruby from other more heavyweight languages, I suppose.

Upvotes: 1

Iuri G.
Iuri G.

Reputation: 10630

you need to add foo.rb file under ./lib and add require statements for each file in order you want them to get loaded. You can look at sample gem for reference: dogeify and an article that walks you through gem creation build your first gem.

Upvotes: 2

Alex
Alex

Reputation: 2528

If you're using ActiveSupport a single line like this will help you:

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

If you're now trying to use Foo::Bar::Qux, ActiveSupport will look for a file named foo/bar/qux.rb inside of the lib folder.

Upvotes: 1

Related Questions