Reputation: 17920
I need to save a reference of a class to a hash:
@hash['foo'] = bar if bar.is_a?(Class)
The above piece of code lives in my /lib
directory, it is not autoloaded every time, and bar
is usually an autoloaded class. In order to avoid getting the "A copy of xxx has been removed from the module tree but is still active" error when I reload!
my code, I am trying to resolve bar
to the global scope, i.e.: add ::
before the class name (Baz
is becoming ::Baz
).
I am not sure how I can dynamically perform this without converting the class to a string, prepending ::
, and then converting it back to a class.
Upvotes: 2
Views: 236
Reputation: 114218
When assigning a constant to a hash, the constant is resolved upon assignment: (not hash specific, this is just how constants work)
hash = {}
A = 1
hash[:a] = A
#=> 1 # <- 1 is being assigned, not A
A = 2
hash[:a]
#=> 1
One way to solve it is to store the constant's name:
hash = {}
A = 1
hash[:a] = 'A'
#=> 'A'
and to resolve it via const_get
/ constantize
:
A = 2
Object.const_get(hash[:a])
#=> 2
This also works for nested constants:
hash[:pi] = 'Math::PI'
Object.const_get(hash[:pi])
#=> 3.141592653589793
If your object happens to be a named class (or module), you can retrieve its name via Module#name
:
hash[:lazy_enum] = Enumerator::Lazy.name
#=> "Enumerator::Lazy"
Object.const_get(hash[:lazy_enum])
#=> Enumerator::Lazy
A different approach is to use a proc which references the constant in its block:
hash = {}
A = 1
hash[:a] = -> { A }
#=> #<Proc:0x00007fc4ba05f510@(irb):10 (lambda)>
the constant will be resolved when invoking the block:
A = 2
hash[:a].call
#=> 2
Upvotes: 5