I have a gem project with rspec configured. My folder structure looks like this -
lib/
- foo/
- foo.rb
- alpha.rb
spec/
- spec_helper.rb
integration/
- alpha_spec.rb
The files foo.rb and alpha.rb have the following structures in terms of class definitions.
# lib/foo/foo.rb
class Foo
# do stuff
end
and
# lib/foo/alpha.rb
require_relative 'foo'
class Foo
class Alpha
# do stuff
end
end
The main takeaway here is that Alpha is a nested class that requires 'foo.rb' directly. (Someone using it would require foo/alpha from their script)
My spec_helper.rb file simply requires my library by loading foo/alpha -
# spec/spec_helper.rb
# Check if `Foo` class is already defined
Object.const_defined?('Foo') ? puts "IS DEFINED" : "IS NOT DEFINED"
require 'foo/alpha'
To my surprise, the constant Foo was already loaded even before requiring alpha/foo, as the output returned IS DEFINED.
Because of this, my require statement tries to load alpha.rb which in turn requires foo.rb and that errors out with
foo.rb:1:in `<top (required)>': Foo is not a class (TypeError)
According to this thread, that type of error is raised by RSpec when a class (Foo) is already defined.
How is this happening? Does RSpec attempt to do some magic behind the scenes and load my library? How do I get around this?
EDIT: I also removed the --require spec_helper line from my .rspec file so it's only getting loaded manually when I run the test.
Related
I have a gem where all classes that include a module are like this
require 'concurrent_rails/adapters/future'
module ConcurrentRails
class Promises
include ConcurrentRails::Adapters::Future
and this is the module
module ConcurrentRails::Adapters
module Future
extend ActiveSupport::Concern
class_methods do
....
this works fine but I'm trying to keep the definition of classes and modules in a single line:
module ConcurrentRails::Adapters::Future
extend ActiveSupport::Concern
class_methods do
...
when I change the definition like above, without touching the promises.rb file, I get this error:
future.rb:3:in `<top (required)>': uninitialized constant ConcurrentRails::Adapters (NameError)
I tried require the file before, on the concurrent_rails.rb definition but nothing worked.
If it helps, here is the source
As a general rule, I'd advise against nested class/module definitions. Sure, it saves a line of code/indentation, but you run in to this sort of issue.
For example, see this rubocop rule:
Class: RuboCop::Cop::Style::ClassAndModuleChildren
Overview
This cop checks the style of children definitions at classes and
modules. Basically there are two different styles:
The compact style is only forced for classes/modules with one child.
Examples:
EnforcedStyle: nested (default)
# good
# have each child on its own line
class Foo
class Bar
end
end
EnforcedStyle: compact
# good
# combine definitions as much as possible
class Foo::Bar
end
Defining nested modules is problematic, as it only works in ruby if the parent module has already been defined. For example:
irb(main):001:1* module Foo::Bar
irb(main):002:0> end
Traceback (most recent call last):
1: from (irb):1
NameError (uninitialized constant Foo)
Whereas this works fine:
irb(main):001:1* module Foo
irb(main):002:2* module Bar
irb(main):003:1* end
irb(main):004:0> end
Additionally, even if the parent module has already been defined, you can still run into other issues with module nesting in ruby.
So, in short: module ConcurrentRails::Adapters::Future didn't work, because ConcurrentRails::Adapters hadn't been defined yet.
You could resolve this by explicitly defining that "parent module" first, however, I wouldn't advise 3+ layer nested module definitions regardless.
I have a gem called private_lib.
The file lib/private_lib.rb contains the following:
require 'private_lib/version'
require 'private_lib/handicap'
require 'private_lib/traversal_cap'
module PrivateLib
end
The lib/private_lib/handicap.rb file contains the following
# module for handicap functions
class Handicap
include TraversalCap
-- other code
end
and the file lib/private_lib/traversal_cap.rb contains the following
module TraversalCap
def some_method
end
-- other code
end
I also have a test file spec/handicap_spec.rb which contains the following
require "spec_helper"
describe Handicap do
include TraversalCap
-- some tests that access the ```Handicap``` class
-- some tests that access directly the traversal_cap some_method.
end
When I run rspec spec/handicap_spec, I get the following error
private_lib/lib/private_lib/handicap.rb:3:in `<class:Handicap>': uninitialized constant Handicap::TraversalCap (NameError)
from private_lib/lib/private_lib/handicap.rb:2:in `<top (required)>'
Why isn't the handicap class seeing the traversal_cap module?
It is because of the order you require the files.
At the time the line require 'private_lib/handicap' is run it reads the handicap.rb file and hits the line where you include TraversalCap. But you haven't yet run require 'private_lib/traversal_cap' at this point so the module is undefined.
Quick fix is to change the order of the require calls, or alternatively put require 'private_lib/traversal_cap' at the top of the handicap file.
I'm pretty new to Ruby and facing a pretty basic problem i guess. I'm probably missing out on some basic concepts and constructs. So this is what i'm trying to do,
I'm writing a sinatra project, and i have a classes which are written in different files. The structure looks something of this sort,
project_name
- api.rb
- base.rb
- settings.rb
In my api.rb file i have defined a class and some methods, it also calls some methods form base.rb and base.rb calls some methods from settings.rb
In api.rb
require 'sinatra'
require 'json'
require 'uri'
require 'base' --> This is the base.rb which is resulting in error
module XX
class Api
def some_method
base = Base.new
base.setup
# some more code
end
end
end
In base.rb, it has the following code
require 'settings'
module XX
class Base
def setup
# some code
end
def some_method
#some code
end
end
end
When i just run ruby api.rb, i'm getting an error in the require statement, unable to load such file-- base (LoadError).
What is it that i'm missing here? Also, how is it that ruby know whether it a gem or a file required..does it check to see if the require is a file in the project and then goes on to check for a gem ? How is this process done in ruby?
Any help is much appreciated!
There was a related question but I think it's for an older version of thor because it does not work anymore.
Basically the situation is I have a thor class. This class has many standard thor commands. What I wanted to do was break out the logic of those commands into other classes so I can test the code easier.
So I created a Command class that does not inherit from thor. It looks something like this:
command.rb
class Command
include Thor::Base
include Thor::Actions
# initializer has only a few arguments, all app specific and not related to thor
def foo
run 'which ps'
end
end
When I run foo from my main thor class the output is nothing at all, it's empty. Here's the main thor class:
cli.rb
require 'thor'
class CLI < Thor
def hello
require 'command'
Command.new().foo
end
end
If I cut the code from foo and place it into hello and move the include Thor::Actions into the main thor class then it works.
Both files are really name spaced under the same module but I removed that from the code examples here to minimize cruft.
How can I get this to work so I can use all of the action module's methods in a non-thor class?
I tried messing around with source/destinations roots and destination stack which was in the related question but they don't work anymore it seems.
You can see everything in the file structure at github.com/ddd1600/simple_angel
I'm getting alot of errors while trying to create a ruby application (soon to be a gem) "the correct way", viz. by thoroughly dividing the logic into classes and "loader files" and all of that. Point is, I know how to do this code the simpler way, without obeying OO principles, but I want to do it "correctly".
So, first of all, the file structure is as follows---
root folder = ~/Develop/simple_angel
inside /simple_angel
- /lib
- Gemfile
- Rakefile
- simple_angel.gemspec
inside /lib
- simple_angel.rb
- /simple_angel
inside /lib/simple_angel
- company.rb
- search.rb
- version.rb
But, here are some basics.
Here is what I'm calling to run this program from the terminal (PATH when running is ~/Develop/simple_angel)
ruby -Ilib lib/simple_angel/search.rb
Here is search.rb
#these 'requires' are supposed to be loaded in lib/simple_angel.rb, so here I show
#them commented out
#
#require 'rubygems'
#require 'httparty'
#require 'json'
#require 'company'
module SimpleAngel
class Search
SEARCH_URL = "http://api.angel.co/1/startups"
def search(user_input)
response = HTTParty.get("#{SEARCH_URL}/#{user_input}")
parsed_response = JSON.parse(response.body)
Company.new(parsed_response)
end
end
s = SimpleAngel::Search.new
s = Search.new
x = s.search(6702)
p x
end
Here is the "loader" file, lib/simple_angel.rb (PS: what is a more formal title for this sort of file?)
require 'httparty'
require 'json'
require 'simple_angel/search'
require 'simple_angel/version'
require 'simple_angel/company'
module SimpleAngel
end
Lastly, when I (again), run "ruby -Ilib lib/simple_angel/search.rb" (with all of search.rb's 'requires' commented out (^&^), this is my error message:
[ddouglas#coders:~/Develop/simple_angel on master]
% ruby -Ilib lib/simple_angel/search.rb
lib/simple_angel/search.rb:15:in `search': uninitialized constant SimpleAngel::Search::HTTParty (NameError)
from lib/simple_angel/search.rb:24:in `<module:SimpleAngel>'
from lib/simple_angel/search.rb:8:in `<main>'
^&^ - now that we're all up to speed here, I might as well include the error that happened when I left search.rb's "requires" in place
% ruby -Ilib lib/simple_angel/search.rb ✹
/usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file -- company (LoadError)
from /usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from lib/simple_angel/search.rb:6:in `<main>'
I can't say I understand the point the class is trying to make regarding splitting up your files.
The first error (uninitialized constant SimpleAngel::Search::HTTParty) is because from within SimpleAngel::Search, you call HTTParty. Try changing that to ::HTTParty to specify the root namespace.
The big picture issue, looking back on this issue, was that I probably just needed to activate my /lib folder within rails via this addition to config/application.rb
config.autoload_paths += Dir["#{config.root}/lib/**/"]
from there, code can be directly loaded via the file_name.rb => FileName class system from anywhere within rails