Home » Ruby » What does 'Monkey Patching' exactly Mean in Ruby?

What does 'Monkey Patching' exactly Mean in Ruby?

Posted by: admin November 30, 2017 Leave a comment

Questions:

According to Wikipedia, a monkey patch is:

a way to extend or modify the runtime
code of dynamic languages […] without altering the original source
code.

The following statement from the same entry confused me:

In Ruby, the term monkey patch was
misunderstood to mean any dynamic
modification to a class and is often
used as a synonym for dynamically
modifying any class at runtime.

I would like to know the exact meaning of monkey patching in Ruby. Is it doing something like the following, or is it something else?

class String
  def foo
    "foo"
  end
end
Answers:

The short answer is that there is no “exact” meaning, because it’s a novel term, and different folks use it differently. That much at least can be discerned from the Wikipedia article. There are some who insist that it only applies to “runtime” code (built-in classes, I suppose) while some would use it to refer to the run-time modification of any class.

Personally, I prefer the more inclusive definition. After all, if we were to use the term for modification of built-in classes only, how would we refer to the run-time modification of all the other classes? The important thing to me is that there’s a difference between the source code and the actual running class.

In Ruby, the term monkey patch was
misunderstood to mean any dynamic
modification to a class and is often
used as a synonym for dynamically
modifying any class at runtime.

The above statement asserts that the Ruby usage is incorrect – but terms evolve, and that’s not always a bad thing.

Questions:
Answers:

The best explanation I heard for Monkey patching/Duck-punching is by Patrick Ewing in RailsConf 2007

…if it walks like a duck and talks like a duck, it’s a duck, right? So
if this duck is not giving you the noise that you want, you’ve got to
just punch that duck until it returns what you expect.

Questions:
Answers:

Monkey patching is when you replace methods of a class at runtime (not adding new methods as others have described).

In addition to being a very un-obvious and difficult to debug way to change code, it doesn’t scale; as more and more modules start monkey patching methods, the likelihood of the changes stomping each other grow.

Questions:
Answers:

You are correct; it’s when you modify or extend an existing class rather than subclass it.

Questions:
Answers:

This is monkey patching:

class Float
  def self.times(&block)
    self.to_i.times { |i| yield(i) }
    remainder = self - self.to_i
    yield(remainder) if remainder > 0.0
  end
end

Now I imagine this might be useful sometimes, but imagine if you saw routine.

def my_method(my_special_number)
  sum = 0
  my_special_number.times { |num| sum << some_val ** num }
  sum
end

And it breaks only occasionally when it gets called. To those paying attention you already know why, but imagine that you didn’t know about the float type having a .times class-method and you automatically assumed that my_special_number is an integer. Every time the parameter is a whole number, integer or float, it would work fine (whole ints are passed back except when there is a floating-point remainder). But pass a number with anything in the decimal area in and it’ll break for sure!

Just imagine how often this might happen with your gems, Rails plugins, and even by your own co-workers in your projects. If there’s one or two little methods in there like this and it could take some time to find and correct.

If you wonder why it breaks, note that sum is an integer and a floating-point remainder could be passed back; in addition, the exponential sign only works when types are the same. So you might think it’s fixed, because you converted bother numbers to floats … only to find that the sum can’t take the floating-point result.

Questions:
Answers:

One of the most powerful aspects of Ruby is the ability to re-open any class and change it’s methods.

Yes that’s right, you can actually reopen any class and change how it works. This includes the standard Ruby classes like

String, Array or Hash!

Now this is obviously as dangerous as it sounds. Being able to change the expected outcome of a method can cause all sorts of weird behaviour and difficult to track down bugs.

But nonetheless, the ability to “Monkey Patch” any class is extremely powerful. Ruby is like a sharp knife, it can be extremely effective, but it’s usually your own fault if you cut yourself.

First up, we’ll add a handy method to generate some Lorem Ipsum text:

class String
  def self.lipsum
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
  end
end

In this example I’ve reopened the String core class and added a lipsum Class Method.

String.lipsum
=> "Lorem ipsum dolor sit amet, consectetur adipiscing elit."

However, not only can we added methods to the core String class, we can also modify the behaviour of existing methods!

class String
  def upcase
    self.reverse
  end
end

In this example we’re hijacking the upcase method and calling the reverse method instead!

"hello".upcase
=> "olleh"

So as you can see, it’s incredibly easy to add or modify methods on an existing class, even when you don’t own that class or it is part of the core of Ruby.

When should you use Monkey Patching?

Rarely.

Ruby provides us with a wealth of powerful tools to work with. However, just because a tool is powerful, does not make it the right tool for the job.

Monkey Patching in particular is an extremely powerful tool. However, a powerful tool in the wrong hands will cause endless amounts of pain and suffering.

Whenever you Monkey Patch a class you are potentially creating a headache at some point in the future when things go wrong.

Classes that have been Monkey Patched are more difficult to understand and debug. If you’re not careful, the error message you will receive will likely give you very little clue as to what the problem actually is.

When you Monkey Patch a method you will potentially be breaking code downstream that is relying on that behaviour.

When you add a new method to an existing class using Monkey Patching you are potentially opening weird edge cases that you can’t possible foresee.

When is it ok to Monkey Patch?

Now with that being said, there’s no point in having powerful tools like Monkey Patching if you don’t actually make use of them.

There are cases where reopening a class does make sense.

For example, you often see Monkey Patches that simply add a convenience method that has no side effect. Ruby has a very beautiful syntax and so it can be tempting to Monkey Patch a class to turn some ugly method call into something that is more readable.

Or perhaps you need to Monkey Patch a class you own.

There are many cases where it’s fine to Monkey Patch, but it should definitely not be your first weapon of choice.

It will often be the case that Monkey Patching is just the lazy developer’s preference over actually refactoring or implementing a known design pattern for a particular problem.

Just because Monkey Patching offers you an easy solution, does not mean that you should always take that path.

http://culttt.com/2015/06/17/what-is-monkey-patching-in-ruby/

Questions:
Answers:

In Python monkeypatching is referred to a lot as a sign of embarrassment: “I had to monkeypatch this class because…” (I encountered it first when dealing with Zope, which the article mentions). It’s used to say that it was necessary to take hold of an upstream class and fix it at runtime instead of lobbying to have the unwanted behaviors fixed in the actual class or fixing them in a subclass. In my experience Ruby people don’t talk about monkeypatching that much, because it’s not considered especially bad or even noteworthy (hence “duck punching”). Obviously you have to be careful about changing the return values of a method that will be used in other dependencies, but adding methods to a class the way that active_support and facets do is perfectly safe.

Questions:
Answers:

Usually it is meant about ad-hoc changes, using Ruby open classes, frequently with low quality code.

Good follow-up on the subject:

http://www.infoq.com/articles/ruby-open-classes-monkeypatching