问题
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
回答1:
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
来源:https://stackoverflow.com/questions/17666061/how-to-monkey-patch-a-ruby-class-inside-a-method