Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you detect that monkey patching has occurred in Ruby?

How do you check that monkey patching has been done to a specific class in Ruby? If that is possible, is it also possible to get the previous implementation(s) of the attribute that's been patched?

like image 745
readonly Avatar asked Dec 02 '08 21:12

readonly


People also ask

What is monkey patching in Ruby?

In Ruby, a Monkey Patch (MP) is referred to as a dynamic modification to a class and by a dynamic modification to a class means to add new or overwrite existing methods at runtime. This ability is provided by ruby to give more flexibility to the coders.

Where do monkey patches go?

There is no set rule on this. Technically you can open it (the class; and add your method) anywhere. I usually make a special file called monkey_patches. rb and put it in config/initializers or in a misc folder in my Rails app so if theres ever a conflict I know where to look.

What is monkey patching and is it ever a good idea?

Monkey patching is a technique used to dynamically update the behavior of a piece of code at run-time. A monkey patch (also spelled monkey-patch, MonkeyPatch) is a way to extend or modify the runtime code of dynamic languages (e.g. Smalltalk, JavaScript, Objective-C, Ruby, Perl, Python, Groovy, etc.)


2 Answers

There are the hooks method_added and method_undefined. Garry Dolley has written an Immutable module that prevents monkey patching.

like image 74
user37011 Avatar answered Nov 14 '22 08:11

user37011


I found this blog posting that touches on how to use method_added to track monkey patching. It's not too hard to extend it to track the methods that were patched.

http://hedonismbot.wordpress.com/2008/11/27/monkey-business-2/:

By using open classes, we can re-define method_added for instances of Class and do some custom stuff every time a method is defined for any class. In this example, we’re re-defining method_added so that it tracks where the method was last defined.

#!/usr/bin/env ruby                                                                                                                                                           

class Class
    @@method_history = {}

    def self.method_history
        return @@method_history
    end

   def method_added(method_name)
       puts "#{method_name} added to #{self}"
       @@method_history[self] ||= {}
       @@method_history[self][method_name] = caller
   end

   def method_defined_in(method_name)
       return @@method_history[self][method_name]
   end
end
like image 34
readonly Avatar answered Nov 14 '22 08:11

readonly