Sasha Sasha - 1 year ago 74
Ruby Question

Constantize raises Uninitialized Constant Error

I'm working on this pretty thorny problem and decided I might be able to dynamically create a class that inherits from StandardError by doing this:

something = "JustForBelow"
error_class = "#{something}Error".constantize
error_class = StandardError.new


But I'm getting a really weird error (in my opinion), which is:

Uninitialized constant JustForBelowError


Aren't I initializing it right there?

(Essentially) same error comes up when I try this:

StandardError.const_get "#{something}Error"
# => NameError: uninitialized constant StandardError::JustForBelowClass


This feels really weird, because a) these are super random names; there aren't any conflicts, and b) I'm pretty sure I've used constantize like in the first example before. Any ideas what's going wrong?

Answer Source

ActiveSupport's constantize method just looks up a constant. It's a fancier version of const_get that does nice things like traverse a nested module structure.

To create a new error, you'll want to do something like this:

2.0.0-p247 :013 > Object.const_set("MyNewError", Class.new(StandardError))
 => MyNewError
2.0.0-p247 :014 > MyNewError.ancestors
 => [MyNewError, StandardError, Exception, Object, Kernel, BasicObject]

At which point, you can do "MyNewError".constantize and get back that new class object.

Edit Also note that const_get in your second example error is looking inside the namespace it was called on. In that case, inside of StandardError's scope.

For instance, if you have a class structure like:

module A
  class B
    CONSTANT = "hello world"
  end
end

Then you could get at that with "A::B::CONSTANT".constantize, or by doing Object.const_get("A").const_get("B").const_get("CONSTANT"). Same thing, just ActiveSupport made is smoother to do.