Monkey patch monkey patch programming method and its application in Ruby

Time:2021-12-5

What ismonkey patch (Monkey Patch)? In a dynamic language, functions are added and changed without modifying the source code.

Purpose of using monkey patch:
1. Additional function
2. Function change
3. Fix program errors
4. Add a hook to execute some other processing while executing a method, such as printing logs, realizing AOP, etc,
5. Cache, when the amount of calculation is large and the results after settlement can be used repeatedly, replacing the method after one calculation can improve the processing speed.

RubyAll classes are open classes, that is, you can add any content after the class definition, which makes it particularly easy to use monkey patch in ruby. In addition, ruby also provides the function of operating methods, classes and modules, which makes it easier for us to use monkey patch. The basic functions provided by ruby are as follows:
      Alias: alias the method
      Include: the method of introducing other modules
      remove_ Method: cancel the method in this class
      Undef: cancel method  
     
Using monkey patch in Ruby
The scene I encountered was like this:

Our company uses the third-party library fog for EC2 operation. Many commands, such as creating an instance, need to set the instance type parameter. In fog, all types of EC2 are defined in the flares array of fog / AWS / Models / compute / flares.rb. If the set type is not in the flags array, fog will be regarded as an invalid parameter and an error will be reported.

Later, Amazon released a new instance type D2. Although the third-party community of ruby is very active, the development community of fog has not been added to flavors.rb in time; Our work urgently needs to use D2 type examples.

After the background is explained, let’s see what solutions are available.

Method 1: we can submit a pull request to fog to add a new type.

But this method won’t work. The dependency of knife-ec2 on the version of fog must be 1.25. *, but fog has been updated to 1.31.0, and the structure of fog has changed greatly since 1.27.0. Obviously, we can’t wait for the knife-ec2 upgrade to support the new version of fog, so submitting a pull request to update fog can’t solve the problem.

Method 2: manually update the old version of fog. Since the latest version of fog cannot be used, we can manually edit the fog of version 1.25 and package it into gem for use. This method is easier to operate than the previous method, but it brings problems that are not easy to maintain. For a small change, adding your own code to a third-party library is always not “clean enough”.

Finally, under the guidance of my colleagues, I adopted the third method, monkey patch. I added a file lib / project to my ruby project_ NAME/monkey_ Patches / flavors.rb, and then add the following code to the file to modify fog / AWS / Models / compute / flavors:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
require 'fog/aws/models/compute/flavors'
 
class Object
 
 def redef_without_warning(const, value)
  mod = self.is_a?(Module) ? self : self.class
  mod.send(:remove_const, const) if mod.const_defined?(const)
  mod.const_set(const, value)
 end
end
 
module Fog
 module Compute
  class AWS
   NEW_FLAVORS = FLAVORS + [
    {
     :id => "d2.xlarge",
     ...
    },
    {
     :id => "d2.2xlarge",
     ...
    },
    {
     :id => "d2.4xlarge",
     ...
    },
    {
     :id => "d2.8xlarge",
     ...
    }
   ]
 
   redef_without_warning :FLAVORS, NEW_FLAVORS
 
  end
 end
end

summary
By adding a monkey patch to our code, we have successfully added new instance types to fog dynamically. Our company can finally use fog to create D2 type machines; Moreover, this method changes the least amount of code and is easier to maintain.

Monkey patch is not a perfect solution. It will introduce some traps. So this technique is still controversial in the field of software engineering. However, I still think monkey patch is a good zero time solution.