Today, Monseuir Eigenclass saved the day for me. I had an interesting gist sent to me from a colleague where he was leveraging const_missing; he had a passing spec but noticed some strange behaviour on production where his call to super in const_missing was returning nil.
Eventually this snippet was used to see what the stack trace was like between both scenarios, a case of spotting the 'ugly duckling' as it were.
The first attempt was a varient on Module.module_eval and I noticed that even the Object.module_eval version failed miserably in IRB. That's to Rails' various bindings however, this does work in a Rails environment
Object.module_eval do
class << self
def const_missing _const
puts "HELL YEAH"
raise _const
rescue => ex
puts ex.message
puts ex.backtrace
super
end
end
end
module AAAA
end
AAAA::TEST
The trace shed some light on this mystery and it was the inclusion of a certain mixin, partials
# bad boy...
module Cart
include Plugin::Partials
set_dynamic_const_missing("Cart")
end
# trace
exception class/object expected
shipping_costs.rb:20:in `raise'
shipping_costs.rb:20:in `const_missing'
gems/activesupport-3.2.13/lib/active_support/dependencies.rb:514:in `load_missing_constant'
gems/activesupport-3.2.13/lib/active_support/dependencies.rb:192:in `block in const_missing'
gems/activesupport-3.2.13/lib/active_support/dependencies.rb:190:in `each'
gems/activesupport-3.2.13/lib/active_support/dependencies.rb:190:in `const_missing'
partials.rb:40:in `block in set_dynamic_const_missing'
shipping_costs.rb:36:in `const_missing'
This mixin essentially defined a method on the base _klass's Class, which happens to be Module since our base _klass is a named Module, via self.class.send(:define_method, :const_missing) { #... }.
I has a sneaky feeling that this was polluting Module causing every defined Module to define said method, and can be disastrous when it is a method such as const_missing.
It wasn't long before I whipped up a quick test where instead I had him define the method on the base _klass's Eigenclass instead. You'll find a working example below
Since :bonjour is defined on the base _klass's Eigenclass, notice how Baz is unable to call the method — and this is exactly how we needed it.
I highly recommend reading 'Ruby's Eigenclasses Demystified' by Andrea Singh and 'A Complete Ruby Class Diagram' by Banisterfiend.