Code & Clay – Notes to self. Mainly Ruby/Rails.

Monkey-patching locally

In Ruby, classes are open. They can be modified at any time. You can add new methods to existing classes and even re-define methods. Rubyists call this monkey-patching.

In the example below, I’ve added String#word_count to return the number of words in a string.

class String
  def word_count
    self.split.count
  end
end
"Don't put your blues where your shoes should be.".word_count # => 9

However, this change is global. What happens if someone else defines their own version of String#word_count based on different rules? Say, they might want to count the individual parts of a contraction, or ignore digits and any non-word characters.

Refinements allow us to monkey-patch classes and modules locally.

Here, I’ve created a new module and used Module#refine to add word_count to String.

module MyStringUtilities
  refine String do
    def word_count
      self.split.count
    end
  end
end

The refinement isn’t available in the global scope:

"Take my shoes off and throw them in the lake.".word_count # => NoMethodError: undefined method `word_count' for…

To activate the refinment, I need to use using:

using MyStringUtilities

"Take my shoes off and throw them in the lake.".word_count # => 10
"A pseudonym to fool him".word_count # => 5

If I activate the refinement within a class or module, the refinement is available until the end of the class or module definition.

Below, the refinement is not available at the top level because it is scoped to MyClass.

module MyStringUtilities
  refine String do
    def word_count
      self.split.count
    end
  end
end

class MyClass
  using MyStringUtilities

  def self.number_of_words_in(string)
    string.word_count
  end
end

"Out on the wily, windy moors".word_count #=> NoMethodError

MyClass.number_of_words_in("Out on the wily, windy moors") #=> 6

If you really want to get to grips with Ruby development and gain a solid understanding of Object Oriented Design, I thoroughly recommend Sandi Metz's Practical Object Oriented Design in Ruby. It's the perfect introduction to OOP and pragmatic Ruby. You can buy it here.

“Meticulously pragmatic and exquisitely articulate, Practical Object Oriented Design in Ruby makes otherwise elusive knowledge available to an audience which desperately needs it. The prescriptions are appropriate both as rules for novices and as guidelines for experienced professionals.”

Katrina Owen, Creator, Exercism

Essential Reading: Learn Rails 6