What does 'Monkey Patching' exactly Mean in Ruby?

僤鯓⒐⒋嵵緔 提交于 2019-11-26 19:51:56

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.

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.

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.

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

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.

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.

Update 10 years later: I would amend the last sentence to say "is relatively safe". Extending a core library class with new methods can lead to problems if somebody else gets the same idea and adds the same method with a different implementation or method signature, or if people confuse extended methods for core language functionality. Both cases often happen in Ruby (especially regarding active_support methods).

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

Explanation of the concept without code:

Discussion of the exact concept is waaaay too academic and nuanced than it needs to be. Let's keep it simple with the following example:

The Normal Operation of a Car

How do you normally start a car? It’s simple: you turn the ignition, the car starts, and voila you’re off to the races!

Monkey Patching a Car

But what if someone else made the car and what if you want to change how it operates?

You don’t need to go to the car manufacturing factory to make those changes: you can simply “monkey-patch” it, by getting under the bonnet and surreptiously and sneakily rewiring things and adding a few incideniaries here and there. You have to really know what you are doing when you do this otherwise the results could be quite explosive – and maybe that’s exactly what you want?

Fabrizzio, where are you going?

Boom!

"Keep your source code close, but your monkey patches closer."

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!