Rails - including module containing a class gives Uninitialized Constant error - ruby-on-rails

My application has lib/project/errors which contains a bunch of Exception classes, one of which is ServiceException
module Project
module Errors
class ServiceException < Exception
def initialize(message = nil)
super message
end
end
end
end
I am trying to use this in my GameService:
module GameMan
class GameService
Blah blah
def validate(score)
raise Project::Errors::ServiceException.new('blah')
end
end
end
This works,
however I hate writing the full module path everywhere. Is there a way to avoid this?
I have tried
module GameMan
class GameService
include Project::Errors
Blah blah
def validate(score)
raise ServiceException.new('blah')
end
end
end
This gives
uninitialized constant ServiceException error.
I have
config.autoload_paths +=
%W(#{config.root}/lib #{config.root}/app/services)
already set inapplication.rb``
What am I doing wrong?

It is all about constants lookup.
ServiceException is defined in the scope of Project::Errors. When you reference ServiceException without prefixing Project::Errors it looks for the class defined in the outer scope, and failing, because there is none.
You should be using the full path.

include Project::Errors
Replace above line to following line
include Project::Errors::ServiceException

Related

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

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'

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.

Ruby - Check if controller defined

I am using Solidus with Ruby on Rails to create a webshop and I have multiple modules for that webshop.
So, I defined a me controller into an module called 'solidus_jwt_auth' with the followin code:
module Spree
module Api
class MeController < Spree::Api::BaseController
def index
...
end
def orders
...
end
def addresses
...
end
end
end
end
I want to extend this in another module called 'solidus_prescriptions' so I created a decorator for this with the following code me_decorator:
if defined? Spree::Api::MeController.class
Spree::Api::MeController.class_eval do
def prescriptions
...
end
def create_prescription
...
end
private
def prescription_params
params.require(:prescription).permit(
*Spree::CustomerPrescription.permitted_attributes
)
end
end
end
And for this I wrote unit tests in solidus_prescription module and integration tests in webshop. The unit tests are working fine, but the integration tests are giving the following error:
Error:
MeEndpointsTest#test_me/prescriptions_post_endpoint_throws_an_error_when_wrong_params:
AbstractController::ActionNotFound: The action 'create_prescription' could not be found for Spree::Api::MeController
test/integration/me_endpoints_test.rb:68:in `block in '
Which means that he can not find the MeController defined in another module. How can I make the check if the MeController is defined since the code bellow does not help me with anything:
if defined? Spree::Api::MeController.class
end
This worked in the end:
def class_defined?(klass)
Object.const_get(klass)
rescue
false
end
if class_defined? 'Spree::Api::MeController'
....
end
if defined? should do exactly what you want it to do in theory. The problem is you're checking if defined? Spree::Api::MeController.class. The #class of your class is Class. So what you're really getting is if defined? Class which will always be true!
This issue is most likely not that the conditional is failing but that it's never getting read. Rails lazy loads most of the code you write, meaning the file is not read until it's called somewhere in execution.
The decorator module should just contain the methods you want to add, without the conditionals or the use of class_eval. Then in the original class you can include it.
module Spree
module Api
class MeController < Spree::Api::BaseController
include MeDecorator
end
end
end
If for any reason you're not certain MeDecorator will be defined, don't use defined?, because defined? MeDecorator will not actually go looking for it if it's not defined and load the necessary file. It will return nil if the constant has no value. Just rescue a NameError
module Spree
module Api
class MeController < Spree::Api::BaseController
begin
include MeDecorator
rescue NameError => e
logger.error e
end
end
end
end

Rails mistakenly nesting called namespace into caller namespace

Bar calls Foo and Rails thinks that Foo must be within the Bar namespace
module Foo
class Lofatook
def oops
puts 'buckets of fun'
end
end
end
module Bar
class Thedoor
def theyhaveacavetroll
Foo::Lofatook.new.oops
end
end
end
Bar::Thedoor.new.theyhaveacavetroll
This works fine as raw ruby. But when I put Foo in lib/foo/lofatook.rb and Bar in app/models/bar/thedoor.rb it throws this error:
uninitialized constant Bar::Foo
We are using the golden lib loading hammer of
config.autoload_paths += Dir["#{config.root}/lib/"]
config.autoload_paths += Dir["#{config.root}/lib/**/"]
with all its glorious drawbacks.
But this should not mean it mistakenly assumes Foo must be part of Bar?
Problem was its not enough to have
module Foo
class Lofatook
end
end
in lib/foo/lofatook.rb. You must also have lib/foo.rb
module Foo
end
Thanks to #ma_il, using ::Foo::Lofatook raised the error uninitialized constant Foo which pointed me in the right direction

Unable To access custom exceptions defined in a module

In My lib folder, I have a file image_helper_exception.rb. The path is lib/dibs_exception/image_exception/image_helper_exception.rb
In this file I have all my Image Exceptions defined
module DibsException
module ImageException
class ImageHelperException < Exception; end
class InvalidEntityException < Exception; end
class InvalidImageTypeException < Exception; end
class InvalidImageVersionException < Exception; end
class InvalidImageUrlException < Exception; end
class ImageNotFoundException < Exception; end
end
end
I raise applications elsewhere in my application when certain things are missing. The path to the file image_resize.rb where it is being used is helpers/image_resize.rb
The image_resize.rb looks something like this.
module Helpers
class ImageResize
require 'RMagick'
require 'aws/s3'
require 'rvg/rvg'
require 'mechanize'
include Magick
def initialize(abc)
if abc
raise DibsException::ImageException::InvalidEntityException.new("Entity is invalid.")
#The above line raises a error
#NameError: uninitialized constant DibsException::ImageException::InvalidEntityException
else
raise DibsException::ImageException::ImageHelperException.new("ImageHelperException.")
#The above line does not raise a error
end
end
end
I would like to add lib gets loaded in application.rb.
I am not able to understand why(Helpers::ImageResize.new(true)) is raising this error : NameError: uninitialized constant DibsException::ImageException::InvalidEntityException
But this works fine as expected : Helpers::ImageResize.new(false)
Why is only the first class loaded. Can some one help ? require does not work.
Ruby does not support function overloading.
second initialize overwrite the first one while parsing.
soo only the second function exists.
This should do the magic.
def initialize(xyz=nil)
raise DibsException::ImageException::InvalidEntityException.new("Entity is invalid.") if(xyz.blank?)
raise DibsException::ImageException::ImageHelperException.new("ImageHelperException.")
#This line DOES NOT raise error like above. It works fine.
end

Resources