Easier way to require spec files in Sinatra - ruby

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

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 setttings.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"]

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.

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"
The rule of thumb
If a file needs another to run then require (or require_relative) it in the file that requires it. Then you get:
A kind of natural sandboxing by only running what is needed (maybe Distributer works perfectly and HouseDistributer has the error because of a monkey patch - how will you know if you require files that aren't actually needed?)
No need to handle requires elsewhere.
No need (or much less need) to know the order of requires.
No need to fiddle about with the load path (which has been a dubious need ever since require_relative was introduced).
Changing the load path and then using 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.

Related

Exclude all except one subfolder for Rubocop

Let's say we have a folder called bin and it includes another subfolder bin/scripts besides other folders and top-level files.
We are looking to scan only the bin/scripts/**/* files and ignore everything else in the bin folder.
This pattern bellow excludes everything in that folder
AllCops:
TargetRubyVersion: 3.1.2
NewCops: enable
Exclude:
- "bin/**/*"
Use ERB to Template Your Exclusions
This is covered by the RuboCop documentation on including/excluding files when used in conjunction with the section on pre-processing using ERB. There might be a more elegant way to do this, but using ERB to add excluded items based on an inverted regular expression using Enumerable#grep_v on a Dir#glob just seems easiest to me.
This was tested with Ruby 3.1.2 and RuboCop v1.30.1, and "just works." Your mileage may vary with other versions or approaches.
AllCops:
TargetRubyVersion: 3.1.2
NewCops: enable
Exclude:
<% Dir.glob('bin/**/*').grep_v(%r{bin/scripts/}).each do |path| %>
- <%= path %>
<% end %>
With the template above in .rubocop.yml and a tree structure of:
.
├── bin
│   ├── bar.rb
│   ├── baz
│   │   └── quux.rb
│   └── scripts
│   └── wuuble
│   └── zub.rb
└── lib
└── foo.rb
RuboCop only checks:
lib/foo.rb
bin/scripts/wuuble/zub.rb

Require paths for rspec tests in a Ruby Gem

I am creating a simple Ruby Gem which is currently laid out as per the example in the Making your own gem documentation.
My directory structure:
.
├── Gemfile
├── Gemfile.lock
├── lib
│   ├── go_run
│   │   ├── parser.rb
│   │   └── runner.rb
│   └── go_run.rb
└── spec
├── go_run_spec.rb
├── parser_spec.rb
└── runner_spec.rb
I originally called the Runner class in lib/go_run/runner.rb just Runner, but now, as per the documented example I have namespaced it under GoRun::Runner. The code works. The problem is that now running bundle exec rspec spec/parser_spec.rb does not. It fails with:
/home/smurf/dev/ruby/go_run/lib/go_run/parser.rb:3:in `<top (required)>': uninitialized constant GoRun (NameError)
The 3rd line of that file is:
class GoRun::Parser
I am requireing the library code in spec/parser_spec.rb using require 'go_run/parser'.
I tried including it with require_relative '../lib/go_run/parser', but that produced the same error.
Does anybody know what I am doing wrong?
Update: I have uploaded the complete code on this branch: https://github.com/henrytk/go_run/tree/stackoverflow-43155117
The problem is originating in lib/go_run/parser.rb rather than from the test itself. Whenever Ruby finds the GoRun::Parser definition, it goes looking for GoRun in the constant lookup table, but it won't be there, and so the program exits with an error.
Note that using lib/go_run.rb as an entry point also will not work, because go_run/parser.rb is required before GoRun is defined.
Part of the problem is using GoRun as both the project level namespace, and an entry point class.
There are a couple of idioms you should consider to fix this situation:
Make GoRun a top level module, used purely for namespacing. Move the logic that lives in the current logic into its own class, for example go_run/cli.rb. The go_run.rb file is then kept as a sort of manifest file, that requires the classes of your project.
Use the nested module- and class syntax. This will define the outer module if it isn't already.
Use a spec_helper.rb file that bootstraps your project using require 'go_run', to make sure everything is properly loaded before running your tests.

How to share FactoryGirl in a GEM with my Ruby project? [duplicate]

I have a gem that includes some Factories. The gem looks something like:
.
├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile
├── db
├── lib
│ ├── models
│ │ ├── users.rb
├── pkg
├── core.gemspec
├── spec
│ ├── factories
│ │ └── users.rb
│ ├── fixtures
│ ├── helpers
│ ├── integration
│ ├── spec_helper.rb
│ ├── support│ │
│ └── unit
│ └── users_spec.rb
└── tasks
Now i'm using the gem in another Ruby project (Grape) by adding something like gem 'core', git: 'https://url.git'.
Now everything is working fine as I can use User model from Grape project.
However I want to use the factories (users) so I can write further integration tests for Grape project.
In Grape project, in spec_helper.rb it looks like:
require 'rubygems'
require 'bundler/setup'
Bundler.require(:default, :development)
ENV['RACK_ENV'] ||= 'test'
require 'rack/test'
require File.expand_path('../../config/environment', __FILE__)
RSpec.configure do |config|
config.mock_with :rspec
config.expect_with :rspec
config.raise_errors_for_deprecations!
config.include FactoryGirl::Syntax::Methods
end
require 'capybara/rspec'
Capybara.configure do |config|
config.app = Test::App.new
config.server_port = 9293
end
Now my test 'users_spec.rb' looks like:
require 'spec_helper'
describe App::UsersController do
include Rack::Test::Methods
def app
App::API
end
describe "/users/me" do
context "with invalid access token" do
before(:each) do
get "/api/v2/users/me"
user = build(:user)
end
it 'returns 401 error code' do
expect(last_response.status).to eq(401)
expect(user).to eq(nil)
end
end
end
end
Now when I try to run the test using rspec spec/api/users_spec.rb I get :
I keep getting this error:
Failure/Error: user = build(:user)
ArgumentError:
Factory not registered: user
Any help would be appreciated as I've been struggling for this.
The problem is that you probably don't expose the spec folder (and herewith the factories) in the load path. Which, in general, is the right thing to do. Check you *.gemspec, you probably have something like:
s.require_paths = ["lib"]
This means only files under the lib directory can be required by other projects using your gem. See http://guides.rubygems.org/specification-reference/#require_paths=
So to solve your problem, you'd need to place a file inside the lib folder which 'knowns' where your factories are and requires those. So in you case, create a file lib/<your gem name>/factories.rb and add:
GEM_ROOT = File.dirname(File.dirname(File.dirname(__FILE__)))
Dir[File.join(GEM_ROOT, 'spec', 'factories', '*.rb')].each { |file| require(file) }
In the other Project load the factories with:
require '<your gem name>/factories'
Works fine for me. The only thing I havn't figured out yet is how to namespace your factories. Not sure if factory girl allows this.
An alternative to require-ing each factory file as suggested in the other answer, is to update the FactoryBot.definition_file_paths configuration.
In your gem defining the factories:
Create a file which will resolve the factory path:
# lib/my_gem/test_support.rb
module MyGem
module TestSupport
FACTORY_PATH = File.expand_path("../../spec/factories", __dir__)
end
end
In you app / gem using the factories from the other gem:
# spec/spec_helper.rb or similar
require "my_gem/test_support"
FactoryBot.definition_file_paths = [
MyGem::TestSupport::FACTORY_PATH,
# Any other paths you want to add e.g.
# Rails.root.join("spec", "factories")
]
FactoryBot.find_definitions
The advantage of the definition_file_paths solution is that other functionality like FactoryBot.reload will work as intended.

Why is the structure of my ruby project like this and why use load path?

I am using an old, 2010 book called "the rspec book" to learn rspec and cucumber. PLEASE DON'T tell me how old this book is and that ruby 1.8.7 and 1.9.3 are outdated. I have not yet seen any beginner book like this which uses latest ruby.
The current structure of my project named "codebreaker":
.
├── features
│   ├── codebreaker_starts_game.feature
│   ├── codebreaker_submits_guess.feature
│   ├── step_definitions
│   │   └── codebreaker_steps.rb
│   └── support
│   └── env.rb
└── lib
├── codebreaker
│   └── game.rb
└── codebreaker.rb
Here is the code and notes from the book:
/features/step_definitions/codebreaker_steps.rb
Given /^I am not yet playing$/ do
end
When /^I start a new game$/ do
Codebreaker::Game.new.start
end
/lib/codebreaker/game.rb
module Codebreaker
class Game
def start
end
end
end
If you run cucumber now, you’ll see the error uninitialized constant Codebreaker (NameError) because Cucumber isn’t loading game.rb yet. The conventional approach to this is to have
a file in the lib directory named for the top-level module of the app. In our case, that’s codebreaker.rb . Create that file now, with the following:
/lib/codebreaker.rb
require 'codebreaker/game'
Now add the following to features/support/env.rb:
$LOAD_PATH << File.expand_path( '../../../lib' , __FILE__)
require 'codebreaker'
Cucumber will load features/support/env.rb , which now requires
lib/codebreaker.rb , which, in turn, requires lib/codebreaker/game.rb
, which is where we defined the Codebreaker module with the Game with
an empty start () method. If you now run cucumber
features/codebreaker_starts_game.feature , you should see some
different results.
My question: Why do we have this env.rb and why does it not point directly to lib/codebreaker/game.rb ? Why go through lib/codebreaker.rb. It makes no sense to me. Please help.
As your app currently stands it wouldn't make any difference.
With a real project there would be many more files though and you would require them through lib/codebreaker.rb so that users of your library just need to require the one file.
You wouldn't want to duplicate that list of requires in env.rb, so your specs load lib/codebreaker.rb, just like any other user of your library.

Simple pattern to export rake tasks defined in gem to the outside world upon installation

This question follows the "Make rake task from gem available everywhere?" for which I'm not fully satisfied with the Railties approach since it induces a dependency on Rails3 which seems to me overkilling compared to what I want.
Also, I dislike the idea to create an extra binary as suggested in this question
So, assuming I have the below layout:
.
├── CHANGELOG.md
├── Gemfile
├── Gemfile.lock
├── LICENCE.md
├── README.md
├── Rakefile
├── my_gem.gemspec
├── lib
│   ├── my_gem
│   │   ├── common.rb
│   │   ├── loader.rb
│   │   ├── tasks
│   │   │   └── sometasks.rake
│   │   └── version.rb
│   └── my_gem.rb
where lib/my_gem/tasks/sometasks.rake has some nested rake tasks definition:
#.....................
namespace :n1 do
#.....................
namespace :n2 do
desc "Tasks n1:n2:t1"
task :t1 do |t|
puts "Task 1"
end
desc "Tasks n1:n2:t2"
task :t2 do |t|
puts "Task 2"
end
end # namespace n1:n2
end # namespace n1
How can I easily share these tasks in another external Rakefile with a simple syntax such as require "my_gem/sometasks" once the gem is installed?
I tested the following configuration in a separate directory with success yet I still think it's a complicated syntax. Any help to simplify the load / include/ require would be welcome:
add a GemFile containing
gem 'my_gem', :path => '~/path/to/my_gem'
add a Rakefile with the following content:
require "my_gem"
load "my_gem/tasks/sometasks.rake"
desk "Test my_gem"
task :toto do |t|
puts "task toto"
end
In the above configuration, it works:
$> bundle exec rake -T
rake n1:n2:t1 # Task n1:n2:t1
rake n1:n2:t1 # Task n1:n2:t2
rake toto # Test my_gem
Yet if I get rid of .rake extension in the Rakefile, I have a load error
rake aborted!
LoadError: cannot load such file -- my_gem/tasks/sometasks
Any hint?
I found a way, inspired from Bundler to offer a simplified interface (and [gem_tasks.rb](https://github.com/bundler/bundler/blob/master/lib/bundler/gem_tasks.rb):
create a file lib/my_gem/sometasks_tasks.rb with an install_tasks method
require 'rake'
module MyGem
class SomeTasks
include Rake::DSL if defined? Rake::DSL
def install_tasks
load 'my_gem/tasks/sometasks.rake'
end
end
end
MyGem::SomeTasks.new.install_tasks
In the application’s Rakefile, it is now enough to add require 'my_gem/sometasks_tasks'

Resources