RSpec raises NameErrors for code in subdirectory of /lib - ruby

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?

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.

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.

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.

Related

How does Ruby's $LOAD_PATH get affected in other files when changed in one file?

I have the following project directory structure:
.
├── bin
├── lib
│   ├── foo
│   │   ├── bar.rb
│   │   └── baz.rb
│   └── foo.rb
└── test
foo.rb:
require 'foo/bar'
bar.rb:
require 'foo/baz'
baz.rb:
puts "baz"
When I run foo.rb by adding the lib directory to the load path, Ruby prints out:
"baz"
I added the lib directory to the load path in foo.rb. How does it get added to load path in foo/bar.rb as well?
as other pointed out already LOAD_PATH is global.
You should not be relying on tricks like this though. In your case the right thing to do is to use require_relative
$LOAD_PATH is a global variable shared between all files in a single ruby process. So when one file modifies it, any code executed after the modification will also use the modified version.
You can read more about global variables here

require error in ruby gem

I'm working on a Ruby gem and I'm getting an odd error. I have previously released this gem without too much trouble. I added some methods / refactored some code and wanted to release a subsequent version (from 1.1 to 1.2).
For reference, the name of the gem is Intervallum, (the word 'interval' in Latin).
I'm getting a 'require' error that's been stumping me.
The folder tree is:
.
├── Gemfile
├── LICENSE
├── README.md
├── intervallum-[version].gem
├── intervallum.gemspec
└── lib
├── intervallum
│   ├── module.scroll.rb
│   └── module.spell.rb
└── intervallum.rb
In lib/intervallum.rb I tried Dir.glob, Dir['./lib/intervallum/*'] and require '../intervallum/lib/intervallum/module.spell.rb' and what happens for each one is:
Locally, after gem build intervallum.gemspec and gem install intervallum-[version].gem, I boot up irb and require intervallum and it works fine.
push to RubyGems
remove the local copy
install from RG
load up in irb I get load errors or it cannot find class of a helper class
I'm not sure why this keeps occurring, or if there's something that I'm missing as to why this keeps occurring but any advice would be much appreciated.
The problem is that you are not including those files in the gem when it gets packaged. Your gemspec specifies that only the lib/intervallum.rb file will be included in the gem:
s.files = ["./lib/intervallum.rb"]
Change that line to include all files in lib. It's also a good idea to include the gemspec.
s.files = Dir['lib/**/*', 'intervallum.gemspec']

RSpec can't find nested formatter

I am trying to run rspec only for Ruby (not Rails), for a simple Ruby file. I'm following Tut+ TDD Testing with Ruby.
I have a competition directory with a lib folder and spec folder.
├── lib
│   ├── competition.rb
│   └── team.rb
└── spec
└── competition_spec.rb
When I run rspec, I got this error. I could've sworn the rspec work before. I don't know what happened.
competition :> rspec spec
/Users/akh88/.rvm/gems/ruby-1.9.3-p547/gems/rspec-core-> 3.0.2/lib/rspec/core/formatters.rb:167:in `find_formatter': Formatter 'nested' unknown - maybe you meant 'documentation' or 'progress'?. (ArgumentError)
My competition_spec.rb
require_relative "../lib/competiiton.rb"
require_relative "../lib/team.rb"
describe Competition do
let(:competition) {Competition.new}
let(:team) {Team.new}
context "having no questions" do
before { competition.questions = [] }
it "doesn't accept any teams" do
expect do
team.enter_competition(competition)
end.to raise_error Competition::Closed
end
end
end
My rvm default Ruby version is 1.9.1 on Mac OSX 10.9.4.
The nested formatter was used in RSpec 1. This was renamed documentation in RSpec 2.
Maybe you have specified nested on the command line or in a .rspec file? Then you need to specify --format documentation instead.
Have you set config.formatter = nested somewhere, probably your spec_helper.rb file? Remove it.
You could have updated the RSpec gem from v1 (the command to run tests changed from spec to rspec though so that's hard to miss). You can check versions with gem list rspec.
Alternatively, you could be missing the load of a custom formatter you happened to call nested.
My .rspec file only had
--color
and I was still getting this error.
I explicitly set it to
--format documentation --color
And now it works.

Test a ruby gem binary

I am developing a ruby gem which will have a binary.
I am trying to develop the binary but i am worried its not finding my requires because the gem isnt installed as a gem is there a way to test the binary without packaging it as a gem?
#!/usr/bin/env ruby
require "middleman_ember_scaffold/load_paths"
# Start the CLI
MiddlemanEmberScaffold::Cli::Base.start
sits in a file named mse and ive added my bin folder of gem to path
.
└── middleman_ember_scaffold
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin
│   └── mes
├── lib
│   ├── middleman_ember_scaffold
│   │   ├── cli.rb
│   │   ├── load_paths.rb
│   │   └── version.rb
│   └── middleman_ember_scaffold.rb
└── middleman_ember_scaffold.gemspec
4 directories, 10 files
when i run mes i get
/Users/justin/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file -- middleman_ember_scaffold/load_paths (LoadError)
from /Users/justin/.rvm/rubies/ruby-1.9.3-p362/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from /Users/justin/middleman-generator/middleman_ember_scaffold/bin/mes:7:in `<main>'
i'd like to be able to run and develop mes without re-packaging everytime i make a change.
Probably a "better" way would be to do the following:
$ ruby -I./lib bin/mes
It does the same as changing your load path, but it only does it for the command you're executing.
Use RUBYLIB Environment Variable
The problem you're facing is that your source directory isn't getting some of magic applied to installed gems, and therefore doesn't have your lib directory in the $LOAD_PATH. While there are other ways to deal with this, for testing I'd recommend just adding your lib directory to the RUBYLIB environment variable. For example:
RUBYLIB="/path/to/middleman_ember_scaffold/lib:$RUBYLIB"
export RUBYLIB
bin/mes
should work for any Bourne-compatible shell. If you're running Bash, and don't have anything else stored in RUBYLIB, you might even be able to shorten the invocation to:
RUBYLIB="/path/to/middleman_ember_scaffold/lib" bin/mes
Either way, once Ruby knows what directories it should add to the $LOAD_PATH everything should work just fine.

Ideal Ruby project structure

I'm after an overview/clarification of the ideal project structure for a ruby (non-rails/merb/etc) project. I'm guessing it follows
app/
bin/ #Files for command-line execution
lib/
appname.rb
appname/ #Classes and so on
Rakefile #Running tests
README
test,spec,features/ #Whichever means of testing you go for
appname.gemspec #If it's a gem
Have I got something wrong? What parts have I missed out?
I think that is pretty much spot on. By default, Rubygems will add the lib directory to the loadpath, but you can push any directory you want onto that using the $: variable. i.e.
$:.push File.expand_path(File.dirname(__FILE__) + '/../surfcompstuff')
That means when you have say, surfer.rb in that dir, you can require "surfer" anywhere and the file will be found.
Also, as a convention, classes and singletons get a file and modules get a directory. For instance, if you had the LolCatz module and the LolCatz::Moar class that would look like:
lib/
appname.rb
lolcatz/
moar.rb
That is why there is an lib/appname folder because most libraries are in the appname namespace.
Additionally, if you try running the command newgem --simple [projectname] that'll quickly generate a scaffold for you with just the bare essentials for a Ruby project (and by extension a Ruby Gem). There are other tools which do this, I know, but newgem is pretty common. I usually get rid of the TODO file and all the script stuff.
See the following example from http://guides.rubygems.org/what-is-a-gem/
% tree freewill
freewill/
├── bin/
│ └── freewill
├── lib/
│ └── freewill.rb
├── test/
│ └── test_freewill.rb
├── README
├── Rakefile
└── freewill.gemspec
I attempt to mimic the Rails project structure because my team, which usually deals with Rails, will understand the structure better than another configuration. Convention over Configuration - bleeding over from Rails.
If you use bundler, running this command bundle gem app_name will give you the same directory structure.
If you want to use rspec instead of unit tests you can then run this command rspec --init
(Just make sure you cd app_name first)

Resources