feralmosquito feralmosquito - 25 days ago 6
Ruby Question

Why do Ruby refinements only modify classes, not modules?

The Ruby docs on refinements state:


Refinements only modify classes, not modules so the argument must be a class.


Why is this?

It's possible to monkey-patch a module:

module MyModule
def my_method
"hello"
end
end

include MyModule
puts my_method # => hello

module MyModule
def my_method
"goodbye"
end
end

puts my_method # => goodbye


I'm sure this isn't a good idea, but it might not be as bad if you could limit the scope of such a monkey patch. So why can't you?

Answer

The refine in Ruby is intended to deal with issues of monkey-patching and inheritance, where the intention is to limit the monkey patching to instances of classes within a specific namespace.

These same inheritance issues don't apply to modules in the same way, since modules can be extended or included within other modules (as opposed to classes) using mixins.

This would allow namespace limitation of the monkey-patching by creating a new module which extends and overrides the original within it's own namespace.

If to use your example:

module MyModule
  def my_method
    "hello"
  end
end

include MyModule

puts my_method
# => hello

module MyOtherModule
  extend MyModule

  puts my_method # will print: hello

  def my_method
    "goodbye"
  end
  extend self

  puts my_method # will print: goodbye
end
# => hello
# => goodbye

puts my_method
# => hello

As you can see, We managed to limit the 'monkey-patch' to the MyOtherModule namespace without using refine.

Since we aren't using instances of MyModule (MyModule is NOT a class), this approach works perfectly.

The same is not possible with classes since, among other reasons, class instances might not be limited to the namespace of the module within which they are used... Hence, for Class instances, refine should be used.