Monkey Patching
Monkey patching is a technique used in programming where you modify or extend the behavior of libraries or classes at runtime. Several dynamically typed programming languages support this. However, I don't believe any other language does it as seamlessly as Ruby.
It is also culturally accepted (unlike in e.g. JavaScript where it will at the very least come with a high CVE vulnerability warning).
# To add a method just open the class and define it:
class String
def shout = upcase + "!"
end
"hello".shout
# => "HELLO!"
# Modifying an existing method is just as easy:
class String
def ==(other) = casecmp?(other)
end
"hello" == "HELLO"
# => true
// To add a method, define it on the class prototype:
String.prototype.shout = function() {
return this.toUpperCase() + "!";
};
"hello".shout();
// => "HELLO!"
// JavaScript doesn't allow modifying equality checks, so we'll
// have to settle for a case-insensitive equality method instead:
String.prototype.equalsIgnoreCase = function(other) {
return this.toLowerCase() === other.toLowerCase();
};
"hello".equalsIgnoreCase("HELLO");
// => true
As your gut might already be telling you, this can be a very powerful tool, but it also comes with some risks. Adding new methods is generally safe, but modifying existing ones is typically never done, except in rare cases (e.g. to fix a bug in a library you depend on).
Here's an example of how we use monkey patching in one of Mynewsdesk's codebases to improve URL compatibility in the cloudinary gem without having to go through the rigamarole of forking etc:
class Cloudinary::Utils
# The only change in implementation of this method is the addition of @ as a safe character.
# This change was made to allow userinfo in the URL for basic auth
def self.smart_escape(string, unsafe = %r{([^a-zA-Z0-9_.\-/:]+@)})
string.gsub(unsafe) { |m| "%#{m.unpack("H2" * m.bytesize).join("%").upcase}" }
end
end
Disclaimer: We are no longer updating this gem and are eventually going to replace it completely, hence the quick and dirty approach.
Always monkey patch responsibly! 🐒
History
Ruby has supported reopening classes and modifying them since its inception.
Smalltalk likely inspired Ruby here. It has a very similar object model and also allows modifying classes at runtime:
String >> shout
^ self asUppercase , '!'
'hello' shout
"=> HELLO!"