Reputation: 11647
I have a spec_helper
that looks like this:
require 'pry'
require 'helpers/data_helper.rb'
require 'distributer.rb'
require 'house_distributer.rb'
require 'accounting_service.rb'
require 'mixer_worker.rb'
require 'mixer.rb'
require 'transaction_service.rb'
ENV['RACK_ENV'] = 'test'
RSpec.configure do |config|
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.warnings = true
config.order = :random
end
and a folder structure that looks like this:
.
├── Gemfile
├── Gemfile.lock
├── README.md
├── app.rb
├── config.ru
├── lib
│ ├── accounting_service.rb
│ ├── distributer.rb
│ ├── house_distributer.rb
│ ├── mixer.rb
│ ├── mixer_worker.rb
│ └── transaction_service.rb
├── public
│ ├── css
│ │ └── add_coins.css
│ ├── images
│ │ └── bitcoin_dawg.jpg
│ └── javascripts
│ └── add_coins.js
├── spec
│ ├── helpers
│ │ └── data_helper.rb
│ ├── lib
│ │ ├── accounting_service_spec.rb
│ │ └── transaction_service_spec.rb
│ └── spec_helper.rb
└── views
└── add_coins.erb
This does not work:
Dir["lib/*.rb"].each {|file| require file }
[1] pry(main)> Dir["lib/*.rb"]
=> ["lib/house_distributer.rb", "lib/distributer.rb", "lib/mixer.rb", "lib/accounting_service.rb", "lib/mixer_worker.rb", "lib/transaction_service.rb"]
I get this error message:
/Users/jwan/.rbenv/versions/2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- lib/house_distributer.rb (LoadError)
from /Users/jwan/.rbenv/versions/2.1.2/lib/ruby/2.1.0/rubygems/core_ext/kernel_require.rb:55:in `require'
What can I do to make this easier?
Also side note, distributer.rb
has to be loaded before house_distributer.rb
because of this:
class HouseDistributer < Distributer
end
Upvotes: 3
Views: 1022
Reputation: 48599
Some explanation of max pleaner's answer...
When you write:
require 'lib/house_distributer.rb'
ruby looks for the file in the directories assigned to the $LOAD_PATH
environment variable. $LOAD_PATH
is an array of Strings, where each String is a path to a directory. The directories in the $LOAD_PATH array are searched in order, and the first match wins.
If $LOAD_PATH
contains a directory called:
'/Users/7stud/ruby_programs'
Then the require
statement above will look for a file with the absolute path:
'/Users/7stud/ruby_programs/lib/house_distributer.rb'
You can check which directories are in your $LOAD_PATH
like this:
$ puts $LOAD_PATH
This is what I get:
/Users/7stud/.rvm/gems/ruby-2.4.0@global/gems/did_you_mean-1.1.0/lib
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/site_ruby/2.4.0
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/site_ruby/2.4.0/x86_64-darwin14
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/site_ruby
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/vendor_ruby/2.4.0
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/vendor_ruby/2.4.0/x86_64-darwin14
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/vendor_ruby
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/2.4.0
/Users/7stud/.rvm/rubies/ruby-2.4.0/lib/ruby/2.4.0/x86_64-darwin14
Obviously, your app's files are not in directories like those.
On the other hand, if you require
a file whose path starts with a /
or a .
-- for instance:
require './lib/house_distributer.rb'
then ruby skips $LOAD_PATH
, and in this case ruby looks for the file relative to the current working directory. Note however, that the current working directory may not be the directory containing the file with the require statement. For instance, if you execute your sinatra program from a different directory, say two levels up from the file containing the require
statement, then the two levels up directory will be the current working directory, and ruby will look for the required file relative to the two levels up directory.
Enter require_relative
. require_relative
will look for the file relative to the path of the current file--not the current working directory.
As a result, you should probably never use require
with a relative path and instead use require_relative
.
Note that you can also programmatically add paths to $LOAD_PATH any time you want:
$LOAD_PATH << '/Users/7stud/ruby_programs'
And if a file called dog.rb is
in that directory, I can require it like so:
require 'dog' #extension is unnecessary
Response to comment:
The simplest thing to do would be:
$LOAD_PATH << "/Users/jwan/Desktop/programming/interview_questions/gemini/jobcoin_mixer/"
But in sinatra, settings.root
is the path to your app directory, so do:
$LOAD_PATH.unshift settings.root
That way you can move your app to another directory without changing anything.
Or, you can remove lib/
from the front of every path that Dir[]
returned:
require 'pathname'
paths = [
"lib/house_distributer.rb",
"lib/distributer.rb",
"lib/mixer.rb",
"lib/accounting_service.rb",
]
new_paths = paths.map do |path|
pn = Pathname.new path
pn.relative_path_from(pn.parent).to_s
end
p new_paths
--output:--
["house_distributer.rb", "distributer.rb", "mixer.rb", "accounting_service.rb"]
Upvotes: 4
Reputation: 12251
The other 2 answers are technically correct and address the goal but not the aim. In other words - why are you doing that in the first place? For instance, why do this:
Also side note, distributer.rb has to be loaded before house_distributer.rb because of this:
class HouseDistributer < Distributer
end
And not this?
require_relative "distributer.rb"
class HouseDistributer < Distributer
end
And in the tests (cough) sorry, the specs:
# spec/house_distributer_spec.rb
require 'spec_helper'
require_relative "../lib/house_distributer.rb"
# spec/transaction_service_spec.rb
require 'spec_helper'
require_relative "../lib/transaction_service.rb"
And since transaction_service.rb
appears to need HouseDistributer
from lib/house_distributer.rb
…
# lib/transaction_service.rb
require_relative "house_distributer.rb"
If a file needs another to run then require
(or require_relative
) it in the file that requires it. Then you get:
require
files that aren't actually needed?)require_relative
was introduced).require
can break sandboxing and make specs work that should fail by loading a gem you have installed on your system but isn't defined as a dependency in the gemspec.I'd add, use bundler's sandboxing too, to avoid further errors and let it handle your load path, e.g.
bundle install --binstubs --path=vendor
(or vendor.noindex on a Mac) then:
bin/rspec
and whatever commandline args you need.
Upvotes: 2
Reputation: 26768
The file not found is because you use "lib/*.rb"
and not ./"lib/*.rb"
.
To ensure the dependencies are loaded in the correct order, you can do this:
move HouseDistributor to lib/distributor/house_distributor.rb
Require files like so:
Dir['./lib/**/*.rb']
.sort_by { |path| path.count("/") }
.each { |path| require path }
this uses **/*.rb
to do a recursive search and will sort the files by the count of "/" (their depth) before requiring
Just a note of caution, if you are doing a recursive require, keep in mind that you actually do want to require all those files. For example if you are are using ActiveRecord and have a schema.rb file, you probably don't want to require that.
Upvotes: 2