-1

I would like to print the class and method any time a method gets redefined in a Rails app, even if it happens in an included gem. I understand there is a hook called method_added that gets called back on redefining a method, but I do not know how to use it to catch anything that gets redefined.

How do I use method_added?

I added this to boot.rb:

class Module
  def method_added(name)
    puts "adding #{self.name.underscore.capitalize}  #{name}\n"
  end
end

But that seems to be catching every single method in every single class?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
marathon
  • 7,881
  • 17
  • 74
  • 137
  • 3
    Please define *precisely* what you mean by "monkey patched". Also, how does the `method_added` hook not satisfy your requirement? – Jörg W Mittag May 07 '17 at 08:58
  • `method_added` sounds like it satisfies my requirements. I don't know how to use it. That is my question. By "monkey patched" I mean redefined. As in there's a method `x` in class `Foo` and some other piece of code, possibly in another gem redefines method `x`. – marathon May 07 '17 at 09:20

1 Answers1

6

You can use the Module#method_added hook to keep a record of all methods that are being defined, and check whether you have already seen the same method before:

require 'set'

module LoggingMethodAdded
  def method_added(meth)
    @methods ||= Set.new
    puts "Monkey patching #{meth} in #{self}!" if @methods.include?(meth)
    @methods << meth
    super
  end
end

class Module
  prepend LoggingMethodAdded
end

class Foo; def foo; end end

class Foo; def foo; end end
# Monkey patching foo in Foo!

module Bar; def foo; end end

module Bar; def foo; end end
# Monkey patching foo in Bar!

This, however, will only work for methods that are added after you have loaded your hook method. The obvious alternative would be to check the already defined methods instead of recording them yourself:

def method_added(meth)
  puts "Monkey patching #{meth} in #{self}!" if (instance_methods(false) | private_instance_methods(false)).include?(meth)
  super
end

But this doesn't work: it is not exactly specified when the method_added hook gets executed; it might get executed after the method has been defined, which means the check will always be true. (At least that's what happened in my tests.)

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653