How to monkey patch a ruby class inside a method

metaprogramming, monkeypatching, ruby, ruby-on-rails-3

Solution

Instead of using `class Clazz; blabla; end` to reopen `Clazz` and monkey patch it, you can use `Module#class_eval`, `Module#instance_eval` and some other meta-programming utilities/methods to do the same trick. And because that block accepted by these methods doesn't create new binding scopes, it is more convenient in meta-programming practice.

def my_method
  puts ">> creating orig_instance"
  orig_instance = A.new

  puts ">> dump orig_instance"
  orig_instance.do_something

  new_do_something = lambda do
    puts "Modified A#do_something"
  end

  # monkey patch class A so that the modified version of do_something get called
  # during initialization of new_instance
  A.class_eval do
    alias_method :old_do_something, :do_something
    define_method :do_something, new_do_something
  end

  puts ">> creating new_instance"
  new_instance = A.new

  puts ">> dump before do_something gets restored"
  new_instance.do_something
  orig_instance.do_something

  # add singleton method for the special instance
  # so that the instance always calls the modified do_something
  new_instance_singleton = class << new_instance; self end
  new_instance_singleton.send :define_method, :do_something, new_do_something

  # restore the modified do_something
  # so that orig_instance and all other instances (except new_instance) have the original definition
  A.class_eval do
    alias_method :do_something, :old_do_something
  end
  puts ">> dump for final result"
  new_instance.do_something
  orig_instance.do_something
end

And the following is the output of `my_method` call:

>> creating orig_instance
Original A#do_something
>> dump orig_instance
Original A#do_something
>> creating new_instance
Modified A#do_something
>> dump before do_something gets restored
Modified A#do_something
Modified A#do_something
>> dump for final result
Modified A#do_something
Original A#do_something

Problem

I am unable to monkey patch a class inside a method body. Within a method definition, I am trying to use a class in two ways: 1] Create an instance and use the orignal definition of a method in the class I am using 2] Monkey patch (pverride) a method in the class and now use an instance with the new method definition. Basically I would be using both of the above instances of a class in my program. The challenge is the method I am overriding gets called during initialization so I have to override it before I create the instance of the class. Here is a small mock-up: ``` class A def initialize do_something end def do something #implementation end end ``` Now , I want to use A in the same method twice , but once by using a modified version of do_something This is how I am trying to do it: ``` def my_method orig_instance = A.new #patch the class Class A # ERROR: CLASS DEF IN METHOD BODY class << self alias_method :old_do_something, :do_something def self.do_something # new implementation end end new_instance = A.new #restore method class << self alias_method :do_something,:old_do_something def self.do_something # new implementation end end end # end of method ``` I get the (ERROR: CLASS DEF IN METHOD BODY) where I try to monkey patch the class, since I am trying to change the class inside of a method. How do I achieve monkey patching the class in a method ? Thanks

Original source