Ruby - NameError: uninitialized constant. Using class from a different module - ruby-on-rails

I have these 2 files in a large system, both are located in PackA
people.rb
module People
class HobbyValueObject
end
end
job.rb
module People
class Job
end
class CityValueObject
end
end
I am trying to use CityValueObject in a different module like this,
Module is in PackB
work.rb
module Profile
class Work
....
def calculateTaxes
...
a = People::CityValueObject....
end
end
end
But it's giving me an error saying,
NameError: uninitialized constant People::CityValueObject
Did you mean? People::HobbyValueObject
Why is not being able to fine CityValueObject but can find HobbyValueObject just fine?
How do I make it find the object that I am intending to use?
I am not explicitly declaring any requires or includes

I was able to resolve this by adding require at the top while using full path file name.
require './packs/people/app/public/people/job'

Related

uninitialised constant after upgrade of Gemfile

I've just updated my Gemfile.
At the beginning I thought problem came from Zeitwerk (from 2.4.2 to 2.5.4) but I've downgraded it and I still have an issue on my spec. I've isolated that the problem does not come from RSpec and dependencies.
Actually, RSpec does not found a class which is defined within another file and does not match the file name/class name.
Important point: Filter::MyStandardError is found.
# app/services/filter/my_standard_error.rb
module Filter
class MyStandardError < StandardError; end
class MySpecificError < MyStandardError; end
# ...
end
# app/services/filter/my_tested_service.rb
module Filter
class MyTestedService
def initialize
raise ::Filter::MySpecificError
end
end
end
RSpec.describe Filter::MyTestedService do
subject { described_class.new }
it 'raises an error'
expect{subject}.to raise_error(::Filter::MySpecificError)
end
end
And I got the error:
NameError:
uninitialized constant Filter::MySpecificError
I got the Changelog but breaking changes are not used on my configuration.
Does anybody have an idea for this one?
You do not need to add app/services to the autoload paths, that is done automatically by Rails. I'd suggest to remove that configuration to keep things simple/idiomatic.
The implementation of app/services/filter.rb should not be needed. Your application is doing something that is not right, we just need to find it.
Could you please delete app/services/filter.rb, throw Rails.autoloaders.log! in config/application.rb, trigger the error, and share the traces?
After reading one-file-one-constant-at-the-same-top-level
I found this to fix my issue
# app/services/filter.rb
class Filter
class MyStandardError < StandardError; end
class MySpecificError < MyStandardError; end
end
# app/services/filter/my_tested_service.rb
class Filter
class MyTestedService
def initialize
raise ::Filter::MySpecificError
end
end
end
I still don't know why it was working before..
You cannot define two constants at the same level in the same file. It is one constant, one file. This has not changed in Zeitwerk upgrades. You need one file for the standard error, and another file for the specific error.

Use anonymous class generated by struct in ruby tests

So, here we have a file with struct,
module CoreDomain
Corporation = Struct.new(...)
end
and we have such a test file,
require 'test_helper'
module CoreDomain
class CorporationTest < ActiveSupport::TestCase
def test_corporation_struct_creation
corp_struct = CoreDomain::Corporation.new(...)
assert_equal ..., ...
end
end
end
when I trying to execute the test I get this error.
NameError: uninitialized constant CoreDomain::Corporation
Question - where I am getting wrong?
What I think is going actually going on here is that you're fooling the autoloader.
Since your test is nested inside module CoreDomain when you get to CoreDomain::Corporation.new(...) it won't trigger the autoloader to start looking for the CoreDomain constant. The classic autoloader worked by hacking its way onto Object.const_missing so was very prone to these kind of errors.
The solution is to just reference the constant before you reopen the module:
require 'test_helper'
CoreDomain::Corporation # take that you stupid autoloader!
module CoreDomain
class CorporationTest < ActiveSupport::TestCase
def test_corporation_struct_creation
corp_struct = Corporation.new(...)
assert_equal ..., ...
end
end
end
Or just remove the test class from the module.
Maybe there was some misunderstanding, but all I need to do is to add this line
require_relative '../../core/domain/corporation.rb'
to the top of test_corporation.rb file

How to access a class defined in module B from module A

Error message:
NameError: uninitialized constant FinancialFunctions::ComputationSteps::TwoBranchesPerPeriod::Strategy1
I'd like Rails to automatically look through the included Strategies module and see the class defined within it instead of just looking within TwoBranchesPerPeriod.
This is not a one-time problem and is part of a broader architecture restructuring. Solving this problem will help in many places throughout the codebase.
There are two specific solutions I'm not looking for:
Having a require or load statement at the top of the file, as I want Rails autoloading to be the core solution for this problem and not have to require several files.
Explicitly state the scope when initializing the class. I'm aware that simply calling Strategies::Strategy1.new will work, but this solution does not work for me because of the length of several classes. I'd like to simply define the dependencies at the top of the file with include statements, and have access to the methods/classes defined in the included modules.
I'm open to autoload statements in a file such as strategies.rb but from what I've test this does not work either.
app/lib/financial_functions/computation_steps/two_branches_per_period.rb:
module FinancialFunctions
module ComputationSteps
module TwoBranchesPerPeriod
include Strategies
def a_method
#The below line is resulting in the error
# NameError: uninitialized constant FinancialFunctions::ComputationSteps::TwoBranchesPerPeriod::Strategy1
a=Strategy1.new
end
end
end
end
/app/lib/financial_functions/computation_steps/strategies/strategy1.rb:
module FinancialFunctions
module ComputationSteps
module Strategies
class Strategy1
def initialize
puts "this should work"
end
end
end
end
I'd like to simply define the dependencies at the top of the file with
include statements, and have access to the methods/classes defined in
the included modules.
Your expectations are not realistic in Ruby. Ruby does not have a package system and includes is not equivalent to imports in languages such as Python or ES6.
As stated in the comments by #maxpleaner includes only copies the methods of the module.
So to do this you would have to define new constants in the consuming class/module:
module FinancialFunctions
module ComputationSteps
module TwoBranchesPerPeriod
include Strategies
Strategy1 = ComputationSteps::Strategies::Strategy1
def a_method
Strategy1.new
end
end
end
end
You can do this dynamically through the Module#included hook:
module Foo
module Bar
end
module Baz
end
def self.included(base)
self.constants.each do |c|
base.constant_set(c, self.constant_get(c))
end
end
end
module X
includes Foo
end
irb(main):002:0> X::Bar
=> Foo::Bar
But aside from being an fun example of meta-programming I don't really think its that wise as the there is no way to handle namespace conflicts. You could also construct a makeshift equivalent of an imports construct:
module Importer
def import(*constants, from:)
constants.each do |c|
const_set(c, from.const_get(c))
end
end
end
module Foo
module Bar
module A
end
module B
end
end
end
class Thing
extend Importer
import :A, :B, from: Foo::Bar
end
irb(main):001:0> Thing::A
=> Foo::Bar::A
irb(main):002:0> Thing::B
=> Foo::Bar::B
But this also smells like an example of fighting the language and will probably introduce more problems then it solves.

Accessing helpers and models from rails engine initializer

I'm trying to make a Ruby on Rails engine, and I want the initializer to be able to have access to the helpers and models.
I'll write below an example, part of the code, and the error that I have. It may not be the recommended way, because I can see that in some cases I'm repeating myself, but it's the first engine I make.
file lib/my_engine/engine.rb
module MyEngine
require 'my_engine/functions'
class Engine < ::Rails::Engine
isolate_namespace MyEngine
config.autoload_paths += %W( #{config.root}/lib )
end
class GlobalVars
attr_accessor :foo
def initialize
#foo = MyEngine::Functions.new
end
end
class << self
mattr_accessor :GLOBAL
mattr_accessor :USER_CONFIG
self.GLOBAL = MyEngine::GlobalVars.new
# add default values of more config vars here
self.USER_CONFIG = 'default config'
end
def self.setup(&block)
yield self
end
end
file lib/my_engine/functions.rb
module MyEngine
require '../../app/helpers/my_engine/options_helper'
class Functions
include MyEngine::OptionsHelper
attr_accessor :some_link
def initialize
#some_link = get_option('dummy')
end
end
end
There is also a controller named OptionsController in app/controllers/my_engine, and OptionsHelper in app/helpers/my_engine/options_helper.rb:
module MyEngine
module OptionsHelper
def get_option(name)
MyEngine::Option.new
end
end
end
When I try to run the dummy application, this error occurs:
/app/helpers/my_engine/options_helper.rb:4:in `get_option': uninitialized constant MyEngine::Option (NameError)
If I change to just Option.new, I have this error:
/app/helpers/my_engine/options_helper.rb:4:in `get_option': uninitialized constant MyEngine::OptionsHelper::Option (NameError)
For ::MyEngine::Option.new, I have:
/app/helpers/my_engine/options_helper.rb:4:in `get_option': uninitialized constant MyEngine::Option (NameError)
For ::Option.new, I have:
/app/helpers/my_engine/options_helper.rb:4:in `get_option': uninitialized constant Option (NameError)
The dummy application has nothing in it. All helpers and models defined above are in the engine.
Before this, I had other errors because it couldn't access the helper, or the Functions class. I had to add require and include to make it work even if they are placed in the same directory. Also, to work, I had to move GlobalVars from its own file inside engine.rb.
Can somebody show me what I'm doing wrong?
After I used required for every class, I ended with ActiveRecord::ConnectionNotEstablished, and it seems that not everything is loaded and available at that point when the GLOBAL object is created.
So I moved the code that was using the models in a separate init method. Then, I added an after initialize event:
config.after_initialize do
MyEngine.GLOBAL.init
end
I see a possible problem: because you are inside module MyEngine it might be possible that actually rails is looking for MyEngine::MyEngine::Option, so I see two approaches:
just write Option: this will look for MyEngine::Option
write ::MyEngine::Option this will look in the global namespace and find MyEngine::Option
Secondly, if that does not help, even though your path seems correct, but you can always explicitly require "my_engine/option" at the top of the file. I am not entirely sure the autoloading in an engine works in quite the same way, and I tend to, in my engine file, require almost everything (to make sure it works).
In my engine.rb I do
require_relative '../../app/models/my_engine/option'
maybe this will help, but it is not a nice solution.

Uninitialized Constant when adding helper module to Model Test

I'm trying to do something fairly simple - add a module with helper methods to a Model test, but I keep getting the following error
uninitialized constant NeighborhoodTest::NeighboorhoodTestHelper
The module is located in test/helpers/neighborhood_test_helper.rb
module NeighborhoodTestHelper
def create_polygon
points = self.geolocate
boundary = Geokit::Polygon.new(points)
end
.
.
end
Per the recommendation in this SO answer, did the following inside test/models/neighborhood_test.rb:
require 'test_helper'
require 'helpers/neighborhood_test_helper'
class NeighborhoodTest < ActiveSupport::TestCase
include NeighboorhoodTestHelper
def setup
#crime = crimes(:arrest)
#neighborhood = neighborhoods(:one)
end
test "neighborhood should contain crime" do
neighborhood_boundary = #neighborhood.create_polygon
crime_location = #crime.geolocate
assert neighborhood_boundary.contains?(crime_location)
end
end
I also tried this SO that didn't work. Anyone know why this approach doesnt work in Models?
tests/some_helper.rb
module SomeHelper
def method1
-----------
-----------
end
def method2
-----------
-----------
end
end
tests/test_helper.rb
require some_helper.rb
You can now access method1 and method2 in any of your test cases. Hope it helps you .
I ran into this today with rspec 3.8, and with other helper tests working just fine, I was very curious to know what made this one spec so special.
In my case, it turned out the spec file name, for whatever reason, was given the same file name as the helper module itself. When trying to load the constant it was looking in the spec file, since it had taken the place of "my_helper" and in turn ignoring the actual module. Adding a _spec at the end of the spec file name allowed this error to go away, and that spec ran as intended after.
I know this is a simple issue, but if you have hundreds upon hundreds of spec files, you may not be constantly looking at the file names.

Resources