rubyrefinements

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?


Solution

  • 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.