Setup:
We got an initializer (in config/initializers) that requires a file where we define our extensions.
This file does something like this:
module UbiquoExtensions
end
:SomeClass.include!(UbiquoExtensions::SomeClass)
# Classes that overwrite behaviour need to be fully loaded first <- not my comment, it's the original developer's
Rails.application.config.after_initialize do
AnotherClass.send(:include, UbiquoExtensions::AnotherClass)
end
extension files look like this:
module UbiquoExtensions
module SomeClass
def self.included(klass)
klass.module_eval do
scope :some_extra_scope, where(something: true)
end
end
end
end
The problem:
I added another extension for a class (Locale
). This works fine from most of the code (both original and extended methods work). But it does not work when called from another extension (eg. AnotherClass
). When accessing Locale
from the extension, it seems it points to the Locale
extension:
But both original and extended methods are undefined (undefined method ... for UbiquoExtensions:...
).
I have tried putting Locale.send :include, UbiquoExtensions::Locale
both inside and outside of Rails.application.config.after_initialize
and both before and after the dependant class. I have also checked that when I reach AnotherClass.send(:include, UbiquoExtensions::AnotherClass)
, while initializing, I have access to all methods just fine.
SomeClass
andAnotherClass
. Classes in Ruby are used for vertical inheritance and a class is never used to extend another. Modules are used for horizantal inheritance and can be extended by other modules or included in classes.scope
is just syntactic sugar for declaring class methods and if you want to create a module that provides class methods just declare them as instance methods of the module and use include instead of extend. If you want to declare both class and instance methods use the innerClassMethods
module pattern (orclass_methods
from ActiveSupport::Concern). stackoverflow.com/questions/33326257/…module_eval
doesn't actually change the module nesting (only the class and module keywords do) so you get wierd and wonky module lookups. That was why I recommended you don't do this and instead declare your monkey patches in a more sane way. justinweiss.com/articles/…