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 Integer
s, but you just overwrote the Integer#*
method which knows how to multiply two Integer
s (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 Integer
s.
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 Integer
s. 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 Vektor
s 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:
Vektor
s are now immutable andVektor#*
returns a newVektor
instead of mutatingself
. 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 asVektor
s. You would be terribly surprised if2 * 3
didn't return6
but rather made2
have the value6
, wouldn't you? But that is exactly what your code was doing withVektor
s!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:
my_block.call(arg1, ...)
or:
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:
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.
If you redefine some methods and the original is loaded later your methods will be overridden back with their original versions.
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
The Authorization Mechanism You Have Provided Is Not Supported. Please Use Aws4-Hmac-Sha256
How to Understand Symbols in Ruby
How to Get Source Code of a Method Dynamically and Also Which File Is This Method Locate In
In Ruby on Rails, How to Format a Date With the "Th" Suffix, as In, "Sun Oct 5Th"
How to Solve Error "Missing 'Secret_Key_Base' For 'Production' Environment" (Rails 4.1)
Rails Sessions Current Practices
Error Installing Libv8: Error: Failed to Build Gem Native Extension
Ruby 2.4 and Rails 4 Stack Level Too Deep (Systemstackerror)
Unexpected Keyword_End Error, Yet Syntax Seems Fine
How to Remove Leading Whitespace Chars from Ruby Heredoc
Office 365 Rest API - Daemon Week Authentication