What Does 'Monkey Patching' Exactly Mean in Ruby

What does 'Monkey Patching' exactly Mean in Ruby?

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.

monkey patching the * operator

other.x[i]*=self

is the same as

other.x[i] = other.x[i] * self

or to make it really explicit

other.x().[]=(i)(other.x().[](i).*(self))

other.x[i] is an Integer, and self is also an Integer (in this case 3).
In your Integer method, you call other.x, but when you multiply some_integer * 3, then in your Integer#* method, other is 3, so you are calling 3.x which indeed doesn't exist.

Note that your Vektor#* method is now also broken, because it, too, needs to multiply two Integers, but you just overwrote the Integer#* method which knows how to multiply two Integers (and actually also knows how to multiply an Integer with any object that correctly implements the Numeric#coerce protocol) with one that only knows how to multiply an Integer and a Vektor, and thus no longer knows how to multiply two Integers.

Another problem is that in your Vektor#* method, you check for other being an instance of Numeric, but then the other way around you only implement multiplication for Integers. This makes your multiplication asymmetric, e.g. some_vektor * 1.0 will work, but 1.0 * some_vektor won't.

The correct implementation would be to not touch Integer at all, and simply implement the numeric coercion protocol. That will solve all of your problems: you don't have to monkey-patch anything, and your Vektors will automatically work with any Numeric in the core library, the standard library, the Ruby ecosystem and in fact even with ones that haven't even been written yet. Something like this:

class Vektor
def self.inherited(*)
raise TypeError, "#{self} is immutable and cannot be inherited from."
end

def initialize(*vektors)
self.x = if vektors.size.zero?
Array.new(3, 0)
else
vektors
end.freeze
end

singleton_class.alias_method :[], :new

alias_method :length, def size
x.size
end

def coerce(other)
[self, other]
end

def *(other)
case other
when Vektor
raise ArgumentError, "the vektors don't have the same length!" if size != other.size
x.zip(other.x).map {|a, b| a * b }.sum
when Numeric
self.class.new(*x.map(&other.method(:*)))
else
a, b = other.coerce(self)
a * b
end
end

protected

attr_reader :x # `x` should not be writeable by anybody!

private

attr_writer :x

freeze
end

You will notice that I made some changes to your code:

  • Vektors are now immutable and Vektor#* returns a new Vektor instead of mutating self. It is generally a good idea to keep your objects immutable as far as possible, but it is especially important (and expected, really) for "number-like" objects such as Vektors. You would be terribly surprised if 2 * 3 didn't return 6 but rather made 2 have the value 6, wouldn't you? But that is exactly what your code was doing with Vektors!
  • x is no longer exposed to everybody, and most importantly, no longer writeable by everybody.
  • I replaced all of your loops with higher-level iteration constructs. As a general rule, if you are writing a loop in Ruby, you are doing something wrong. There are so many powerful methods in Enumerable that you should never need a loop.

I also wrote some tests to demonstrate that without even touching any class outside of Vektor, we now support multiplication of int * vek, vek * int, float * vek, vek * float, rational * vek, vek * rational, complex * vek, and vek * complex, simply by implementing the Numeric#coerce coercion protocol.

require 'test/unit'
class VektorTest < Test::Unit::TestCase
def test_that_creating_an_empty_vektor_actually_creates_a_zero_vektor_of_dimension_3
v = Vektor.new
assert_equal 3, v.size
end

def test_that_square_brackets_is_an_alias_for_new
v = Vektor[]
assert_equal 3, v.size
end


def test_that_we_can_multiply_two_trivial_vektors
v1 = Vektor[2]
v2 = Vektor[3]
assert_equal 6, v1 * v2
end

def test_that_we_can_multiply_two_nontrivial_vektors
v1 = Vektor[2, 3, 4]
v2 = Vektor[5, 6, 7]
assert_equal 56, v1 * v2
end


def test_that_we_can_multiply_a_trivial_vektor_with_an_integer
v = Vektor[2]
assert_equal Vektor[6], v * 3 # this will fail because you haven't implemented equality!
end

def test_that_multiplying_a_trivial_vektor_with_an_integer_at_least_does_not_raise_an_exception
v = Vektor[2]
assert_nothing_raised { v * 3 }
end

def test_that_we_can_multiply_a_nontrivial_vektor_with_an_integer
v = Vektor[2, 3, 4]
assert_equal Vektor[6, 9, 12], v * 3 # this will fail because you haven't implemented equality!
end

def test_that_multiplying_a_nontrivial_vektor_with_an_integer_at_least_does_not_raise_an_exception
v = Vektor[2, 3, 4]
assert_nothing_raised { v * 3 }
end

def test_that_we_can_multiply_an_integer_with_a_trivial_vektor
v = Vektor[2]
assert_equal Vektor[6], 3 * v # this will fail because you haven't implemented equality!
end

def test_that_multiplying_an_integer_with_a_trivial_vektor_at_least_does_not_raise_an_exception
v = Vektor[2]
assert_nothing_raised { 3 * v }
end

def test_that_we_can_multiply_an_integer_with_a_nontrivial_vektor
v = Vektor[2, 3, 4]
assert_equal Vektor[6, 9, 12], 3 * v # this will fail because you haven't implemented equality!
end

def test_that_multiplying_an_integer_with_a_nontrivial_vektor_at_least_does_not_raise_an_exception
v = Vektor[2, 3, 4]
assert_nothing_raised { 3 * v }
end


def test_that_we_can_multiply_a_trivial_vektor_with_a_float
v = Vektor[2]
assert_equal Vektor[6.0], v * 3.0 # this will fail because you haven't implemented equality!
end

def test_that_multiplying_a_trivial_vektor_with_a_float_at_least_does_not_raise_an_exception
v = Vektor[2]
assert_nothing_raised { v * 3.0 }
end

def test_that_we_can_multiply_a_nontrivial_vektor_with_a_float
v = Vektor[2, 3, 4]
assert_equal Vektor[6.0, 9.0, 12.0], v * 3.0 # this will fail because you haven't implemented equality!
end

def test_that_multiplying_a_nontrivial_vektor_with_an_float_at_least_does_not_raise_an_exception
v = Vektor[2, 3, 4]
assert_nothing_raised { v * 3.0 }
end

def test_that_we_can_multiply_a_float_with_a_trivial_vektor
v = Vektor[2]
assert_equal Vektor[6.0], 3.0 * v # this will fail because you haven't implemented equality!
end

def test_that_multiplying_a_float_with_a_trivial_vektor_at_least_does_not_raise_an_exception
v = Vektor[2]
assert_nothing_raised { 3.0 * v }
end

def test_that_we_can_multiply_a_float_with_a_nontrivial_vektor
v = Vektor[2, 3, 4]
assert_equal Vektor[6.0, 9.0, 12.0], 3.0 * v # this will fail because you haven't implemented equality!
end

def test_that_multiplying_a_float_with_a_nontrivial_vektor_at_least_does_not_raise_an_exception
v = Vektor[2, 3, 4]
assert_nothing_raised { 3.0 * v }
end


def test_that_we_can_multiply_a_trivial_vektor_with_a_rational
v = Vektor[2]
assert_equal Vektor[6r], v * 3r # this will fail because you haven't implemented equality!
end

def test_that_multiplying_a_trivial_vektor_with_a_rational_at_least_does_not_raise_an_exception
v = Vektor[2]
assert_nothing_raised { v * 3r }
end

def test_that_we_can_multiply_a_nontrivial_vektor_with_a_rational
v = Vektor[2, 3, 4]
assert_equal Vektor[6r, 9r, 12r], v * 3r # this will fail because you haven't implemented equality!
end

def test_that_multiplying_a_nontrivial_vektor_with_an_rational_at_least_does_not_raise_an_exception
v = Vektor[2, 3, 4]
assert_nothing_raised { v * 3r }
end

def test_that_we_can_multiply_a_rational_with_a_trivial_vektor
v = Vektor[2]
assert_equal Vektor[6r], 3r * v # this will fail because you haven't implemented equality!
end

def test_that_multiplying_a_rational_with_a_trivial_vektor_at_least_does_not_raise_an_exception
v = Vektor[2]
assert_nothing_raised { 3r * v }
end

def test_that_we_can_multiply_a_rational_with_a_nontrivial_vektor
v = Vektor[2, 3, 4]
assert_equal Vektor[6r, 9r, 12r], 3r * v # this will fail because you haven't implemented equality!
end

def test_that_multiplying_a_rational_with_a_nontrivial_vektor_at_least_does_not_raise_an_exception
v = Vektor[2, 3, 4]
assert_nothing_raised { 3r * v }
end


def test_that_we_can_multiply_a_trivial_vektor_with_a_complex_number
v = Vektor[2]
assert_equal Vektor[6i], v * 3i # this will fail because you haven't implemented equality!
end

def test_that_multiplying_a_trivial_vektor_with_a_complex_number_at_least_does_not_raise_an_exception
v = Vektor[2]
assert_nothing_raised { v * 3i }
end

def test_that_we_can_multiply_a_nontrivial_vektor_with_a_complex_number
v = Vektor[2, 3, 4]
assert_equal Vektor[6i, 9i, 12i], v * 3i # this will fail because you haven't implemented equality!
end

def test_that_multiplying_a_nontrivial_vektor_with_an_complex_number_at_least_does_not_raise_an_exception
v = Vektor[2, 3, 4]
assert_nothing_raised { v * 3i }
end

def test_that_we_can_multiply_a_complex_number_with_a_trivial_vektor
v = Vektor[2]
assert_equal Vektor[6i], 3i * v # this will fail because you haven't implemented equality!
end

def test_that_multiplying_a_complex_number_with_a_trivial_vektor_at_least_does_not_raise_an_exception
v = Vektor[2]
assert_nothing_raised { 3i * v }
end

def test_that_we_can_multiply_a_complex_number_with_a_nontrivial_vektor
v = Vektor[2, 3, 4]
assert_equal Vektor[6i, 9i, 12i], 3i * v # this will fail because you haven't implemented equality!
end

def test_that_multiplying_a_complex_number_with_a_nontrivial_vektor_at_least_does_not_raise_an_exception
v = Vektor[2, 3, 4]
assert_nothing_raised { 3i * v }
end
end

Let's also add some methods that you generally always need to implement to make your objects work with the rest of the Ruby ecosystem:

class Vektor
def ==(other)
x == other.x
end

def eql?(other)
other.is_a?(Vektor) && self == other
end

def hash
x.hash
end

def to_s
"(#{x.join(', ')})"
end

def inspect
"Vektor#{x.inspect}"
end
end

If you absolutely must use monkey-patching, then it is important that you retain access to the old version of the method that you are monkey-patching. The tool to do this is the Module#prepend method. It would look something like this:

class Vektor
def self.inherited(*)
raise TypeError, "#{self} is immutable and cannot be inherited from."
end

attr_reader :x # `x` should not be writeable by anybody!

def initialize(*vektors)
self.x = if vektors.size.zero?
Array.new(3, 0)
else
vektors
end.freeze
end

singleton_class.alias_method :[], :new

alias_method :length, def size
x.size
end

def *(other)
case other
when Vektor
raise ArgumentError, "the vektors don't have the same length!" if size != other.size
x.zip(other.x).map {|a, b| a * b }.sum
when Numeric
self.class.new(*x.map(&other.method(:*)))
end
end

private

attr_writer :x

freeze
end

(Mostly identical, but without the coerce method and without the else clause in the case expression, and the x reader needs to be public.)

module IntegerTimesVektorExtension
def *(other)
return Vektor[*other.x.map(&method(:*))] if other.is_a?(Vektor)
super
end
end

And because monkey-patching core classes is really dangerous, we use a refinement, to make sure that the monkey-patch is only active where you explicitly activate the refinement with using IntegerTimesVektorRefinement.

module IntegerTimesVektorRefinement
refine Integer do
prepend IntegerTimesVektorExtension
end
end

Now, we can do something like this:

v = Vektor[2, 3, 4]

5 * v
# `*': Vektor can't be coerced into Integer (TypeError)

using IntegerTimesVektorRefinement

5 * v
#=> <#<Vektor:0x00007fcc88868588 @x=[10, 15, 20]>

In the dark old times, before Module#prepend existed, we had to resort to other dirty tricks to be able to keep the monkey-patched method around. However, as of the release of Ruby 2.0 on February, 24th 2013, which includes Module#prepend, these tricks are no longer necessary, and should not be used, nor taught. This includes the alias_method chain trick, which looks like this:

class Integer
alias_method :original_mul, :*

def *(other)
return Vektor[*other.x.map(&method(:*))] if other.is_a?(Vektor)
original_mul(other)
end
end

But, as mentioned: you should not do this.

The best solution is to implement the coerce protocol. Really not "just" the best solution but the only correct solution.

If, for some reason, you do not want to implement the coerce protocol, then the best solution is Module#prepend. Ideally with refinement, but please be aware that not all Ruby implementations implement refinements.

If you really, really, really, must do monkey-patching, and are using a ten-year old, unsupported, unmaintained, obsolete, outdated version of Ruby and thus cannot use Module#prepend, then there are still better solutions out there than alias_method, such as grabbing the instance method as a Method object and storing it in a local variable that you close over.

Using alias_method here is not necessary, if not outright wrong, and bad, outdated, obsolete practice.

Organizing monkey patches

It doesn't matter where you put the include call.

Calling String.include will always monkey patch the one String class that is used by all the strings in the entire object space. So best put the instruction at the top level as to not mislead readers of the code.

Monkey patching is always global.

It is a powerful feature and can be used for good.

If you are authoring a gem be aware that you're sharing a global namespace with others. The same is also true for top-level modules and even the gem name though. Shared namespaces are just a reality of shared code.

If you are looking for lexically scoped monkey patches look into the new refinement feature that was introduce with Ruby 2.

Refinements are an idea taken from Smalltalk's class boxes. Refinements are not without their own issues though, for example they lack support for introspection and reflection. Thus essentially making them stealth and unfit for production use.

If you are looking to limit the monkey patches to some string object only, consider either subclassing String or calling extend on an instance.

If monkey patching is permitted in both Ruby and Python, why is it more controversial in Ruby?

As a Python programmer who has had a taste of Ruby (and likes it), I think there is somewhat of an ironic parallel to when Python was beginning to become popular.

C and Java programmers would ‘bash’ Python, stating that it wasn't a real language, and that the dynamic nature of its types would be dangerous, and allow people to create ‘bad’ code. As Python became more popular, and the advantages of its rapid development time became apparent, not to mention the less verbose syntax:

// Java
Person p = new Person();
# Python
p = Person()

we began to see some more dynamic features appear in later versions of Java. Autoboxing and -unboxing make it less troublesome to deal with primitives, and Generics allow us to code once and apply it to many types.

It was with some amusement that I saw one of the key flexible features of Ruby – Monkey Patching, being touted as dangerous by the Python crowd. Having started teaching Ruby to students this year, I think that being able to ‘fix’ the implementation of an existing class, even one that is part of the system, is very powerful.

Sure, you can screw up badly and your program can crash. I can segfault in C pretty easily, too. And Java apps can die flaming death.

The truth is, I see Monkey Patching as the next step in dynamic and meta-programming. Funny, since it has been around since Smalltalk.

Proper Monkey Patching Using Modules in Ruby

Ok, so using this answer When monkey patching a method, can you call the overridden method from the new implementation?

I came up with:

require 'selenium-webdriver'

module CookieManagement
def get_and_save(url)
puts "You told me to get, so I am getting"
get(url)
end
end


class Selenium::WebDriver::Driver
include CookieManagement
end

Which I then require_relative instead of standard selenium-webdriver gem in the .rb files that need that functionality.

Monkey patch method on single object

Just define a method on the object:

class Thing
def greeting
'yo, man'
end
end

Thing.instance_methods(false)
#=> [:greeting]

object = Thing.new
#=> #<Thing:0x007fc4ba276c90>

object.greeting
#=> "yo, man"

Define two methods on object (which will be instance methods in object's singleton class.

def object.greeting
'hey, dude'
end

def object.farewell
'see ya'
end

object.methods(false)
#=> [:greeting, :farewell]
object.singleton_class.instance_methods(false) #=> [:greeting, :farewell]

object.greeting
#=> "hey, dude"
object.farewell
#=> "see ya"

new_obj = Thing.new
new_obj.greeting
#=> "yo, man"
new_obj.farewell
#NoMethodError: undefined method `farewell' for #<Thing:0x007fe5a406f590>

Remove object's singleton method greeting.

object.singleton_class.send(:remove_method, :greeting)

object.methods(false)
#=> [:farewell]
object.greeting
#=> "yo, man"

Another way to remove :greeting from the object's singleton class is as follows.

class << object
remove_method(:greeting)
end

Why does monkey patching methods on ActiveRecord::Base redefine unrelated methods on Model.count

The key to this question is in delegation.rb

Basically this has the follow method missing implementation for Relation (simplified slightly for brevity)

def method_missing(method,*args,&block)
scoping { @klass.public_send(method, *args, &block) }
end

(@klass is the active record class the relation belongs to)

The scoping method sets the class' current_scope for the duration of the block. This contains things like where clauses, sorts etc. This is what allows you to call class methods on relations and have those class methods operate on the scope defined by the relation.

In the book case this is still happening, however the scoping is happening to Book but the query is against User so the scoping doesn't change the result.

Ruby (Monkey Patching Array)

Ruby Array Class:

map { |item| block } → new_ary

A block is like a method, and you specify a block after a method call, for example:

[1, 2, 3].map() {|x| x*2} #<---block
^
|
method call(usually written without the trailing parentheses)

The block is implicitly sent to the method, and inside the method you can call the block with yield.

yield -> calls the block specified after a method call. In ruby, yield is equivalent to yield(), which is conceptually equivalent to calling the block like this: block().

yield(x) -> calls the block specified after a method call sending it the argument x, which is conceptually equivalent to calling the block like this: block(x).

So, here is how you can implement new_map():

class Array
def new_map
result = []

each do |item|
result << yield(item)
end

result

end
end

arr = [1, 2, 3].new_map {|x| x*2}
p arr

--output:--
[2, 4, 6]

This comment is a bit advanced, but you don't actually have to write self.each() to call the each() method inside new_map(). All methods are called by some object, i.e. the object to the left of the dot, which is called the receiver. For instance, when you write:

self.each {....}

self is the receiver of the method call each().

If you do not specify a receiver and just write:

each {....}

...then for the receiver ruby uses whatever object is assigned to the self variable at that moment. Inside new_map() above, ruby will assign the Array that calls the new_map() method to self, so each() will step through the items in that Array.

You have to be a little bit careful with the self variable because ruby constantly changes the value of the self variable without telling you. So, you have to know what ruby has assigned to the self variable at any particular point in your code--which comes with experience. Although, if you ever want to know what object ruby has assigned to self at some particular point in your code, you can simply write:

puts self

Experienced rubyists will crow and cluck their tongues if they see you write self.each {...} inside new_map(), but in my opinion code clarity trumps code trickiness, and because it makes more sense to beginners to write self there, go ahead and do it. When you get a little more experience and want to show off, then you can eliminate explicit receivers when they aren't required. It's sort of the same situation with explicit returns:

def some_method
...
return result
end

and implicit returns:

def some_method
...
result
end

Note that you could write new_map() like this:

class Array
def new_map(&my_block) #capture the block in a variable
result = []

each do |item|
result << my_block.call(item) #call the block
end

result

end
end

arr = [1, 2, 3].new_map {|x| x*2}
p arr

--output:--
[2, 4, 6]

Compare that to the example that uses yield(). When you use yield(), it's as if ruby creates a parameter variable named yield for you in order to capture the block. However, with yield you use a different syntax to call the block, namely (), or if their are no arguments for the block, you can eliminate the parentheses--just like you can when you call a method. On the other hand, when you create your own parameter variable to capture a block, e.g. def new_map(&my_block), you have to use a different syntax to call the block:

  1. my_block.call(arg1, ...)

or:


  1. myblock[arg1, ...]

Note that #2 is just like the syntax for calling a method--except that you substitute [] in place of ().

Once again, experienced rubyists will use yield to call the block instead of capturing the block in a parameter variable. However, there are situations where you will need to capture the block in a parameter variable, e.g. if you want to pass the block to yet another method.

how to monkey patch the puts method in ruby

You could override the puts method like so:

def puts(object)
super('')
super(object)
super('')
end

If you really wanted to you could monkey patch the method like below. Although your changes would probably have unintended effects.

module Kernel
def puts(object)
# code
end
end

You can read about monkey patching in the Ruby docs here.

Recommended approach to monkey patching a class in ruby

Honestly, I used to use the 1st form (reopening the class), as it feels more natural, but your question forced me to do some research on the subject and here's the result.

The problem with reopening the class is that it'll silently define a new class if the original one, that you intended to reopen, for some reason wasn't defined at the moment. The result might be different:

  1. If you don't override any methods but only add the new ones and the original implementation is defined (e.g., file, where the class is originally defined is loaded) later everything will be ok.

  2. If you redefine some methods and the original is loaded later your methods will be overridden back with their original versions.

  3. The most interesting case is when you use standard autoloading or some fancy reloading mechanism (like the one used in Rails) to load/reload classes. Some of these solutions rely on const_missing that is called when you reference undefined constant. In that case autoloading mechanism tries to find undefined class' definition and load it. But if you're defining class on your own (while you intended to reopen already defined one) it won't be 'missing' any longer and the original might be never loaded at all as the autoloading mechanism won't be triggered.

On the other hand, if you use class_eval you'll be instantly notified if the class is not defined at the moment. In addition, as you're referencing the class when you call its class_eval method, any autoloading mechanism will have a chance to locate class' definition and load it.

Having that in mind class_eval seems to be a better approach. Though, I'd be happy to hear some other opinion.



Related Topics



Leave a reply



Submit