I have some code that can be simplified to the following. It works on Ruby 2.3.3 and breaks on 2.3.4. It's slightly odd and I'd welcome suggestions on how to rewrite it as well as explanations as to why it breaks.
require 'forwardable'
class Dummy
class << self
TEST = {
a: Dummy.new,
b: Dummy.new
}
extend Forwardable
def_delegators :TEST, :[]
private :new
end
end
puts Dummy[:a]
Ruby 2.3.3
#<Dummy:0x007fbd6d162380>
Ruby 2.3.4
NameError: uninitialized constant TEST
The goal was to only initialize TEST
once and have .new
be private. Memoizing a @test
variable inside a []
method doesn't work because new
is private by the point the hash is created.
EDIT
Removing Forwardable from the equation fixes things, but I'm still curious as to why and thoughts on how to improve it.
class Dummy
class << self
TEST = {
a: Dummy.new,
b: Dummy.new
}
def [](key)
TEST[key]
end
private :new
end
end
puts Dummy[:a]
Ruby 2.3.3 and 2.3.4
#<Dummy:0x007fbd6d162380>
require 'forwardable'
class Dummy
Test = {
a: Dummy.new,
b: Dummy.new
}
class << self
extend Forwardable
def_delegators :"::Dummy::Test", :[]
private :new
end
end
puts Dummy[:a]
Ruby is open source. There was a bug #12478, fixed in that commit. The commit’s message explicitly states that it changes the behaviour while dealing with non-module objects.
Symbols are not converted to String
s anymore and dealed separately, :TEST
is not expanded on Dummy
level and the constant could not be resolved in different context.
It does not make any sense to declare the constants on singleton classes (check with your old code):
Dummy.constants
#⇒ []
Dummy.singleton_class.constants
#⇒ [:TEST]
The constant was successfully resolved in the legacy implementation exactly the same way as multiplying two negatives gives a positive result: the errors negated. The code was not working properly, it occasionally failed twice in unexpected, but appreciated way, producing the correct result.