Written March 2, 2014. Tagged Ruby on Rails.
Be aware that if you use SimpleDelegator with Rails, you may see autoloading issues.
Specifically, if you have something like
class MyThing < SimpleDelegator
end
class MyThing::Subthing
end
then that class won't be autoloaded. In a Rails console:
>> MyThing
=> MyThing
>> MyThing::Subthing
NameError: uninitialized constant Subthing
>> require "my_thing/subthing"
=> true
>> MyThing::Subthing
=> MyThing::Subthing
Both Rails (code) and SimpleDelegator (code) hook into const_missing
. Rails does it for autoloading. Since the SimpleDelegator
superclass is earlier in the inheritance chain than Module
(where Rails mixes it in), this breaks Rails autoloading.
How do you get around this?
You could stop using SimpleDelegator
.
An explicit require
won't work well – I think what happens is that the Rails development reloading magic undefines the constant when the file is changed. An explicit require_dependency
does appear to work:
class MyThing < SimpleDelegator
require_dependency "my_thing/subthing"
end
Or you could override the const_missing
you get from SimpleDelegator
to do both what it used to do, and what Rails does:
class RailsySimpleDelegator < SimpleDelegator
# Fix Rails autoloading.
def self.const_missing(const_name)
if ::Object.const_defined?(const_name)
# Load top-level constants even though SimpleDelegator inherits from BasicObject.
::Object.const_get(const_name)
else
# Rails autoloading.
::ActiveSupport::Dependencies.load_missing_constant(self, const_name)
end
end
end
class MyThing < RailsySimpleDelegator
end
But keep in mind that this may vary with Rails versions and may break on Rails updates.
The ::Object.const_get(const_name)
thing is explained in the BasicObject
docs.
This is a tricky problem. Perhaps the best solution would be for Rails itself to monkeypatch SimpleDelegator
. That fixes the autoloading gotcha but may cause others – I once had a long debugging session when I refused to believe that Rails would monkeypatch the standard lib ERB
(but it does, for html_safe
).
This blog post is mainly intended to describe the problem – I'm afraid I don't know of a great solution. If you have any insight, please share in a comment.