【问题标题】:How to monkey patch a ruby class inside a method [closed]如何在方法中修补 ruby​​ 类 [关闭]
【发布时间】:2013-07-14 00:21:59
【问题描述】:

我无法在方法体内对类进行修补。

在方法定义中, 我试图以两种方式使用一个类:

1] 创建一个实例并在我正在使用的类中使用方法的原始定义

2] Monkey 修补 (pverride) 类中的方法,现在使用具有新方法定义的实例。

基本上我会在我的程序中使用上述两个类的实例。

挑战是我要覆盖的方法在初始化期间被调用,因此我必须在创建类的实例之前覆盖它。

这是一个小模型:

class A

  def initialize
   do_something
  end

  def do something
     #implementation
  end

end

现在,我想在同一方法中使用 A 两次,但一次是使用修改后的 do_something 版本 这就是我正在尝试的方式:

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

我得到 (ERROR: CLASS DEF IN METHOD BODY) 我试图在其中修补类,因为我试图在方法内更改类。

如何在方法中实现猴子修补类?

谢谢

【问题讨论】:

  • 您收到上述错误了吗?哪个错误?它是 c,而不是类的 C。
  • 对不起,如果您浏览了代码,您可能会错过代码中标记的错误。为了清楚起见再写一遍
  • 有点不相关,但我很确定 // 不会在 ruby​​ 中标记评论
  • 我更改了问题的一些措辞以使其更清楚。希望这可以重新打开,因为它是一个真正的编程问题,并且我需要一个真正的用例。谢谢
  • 这个问题没有什么不清楚的地方!!!

标签: ruby ruby-on-rails-3 metaprogramming monkeypatching


【解决方案1】:

您可以使用Module#class_evalModule#instance_eval 和其他一些元编程实用程序/方法来执行相同的操作,而不是使用class Clazz; blabla; end 重新打开Clazz 并对其进行修补。而且由于这些方法接受的块不会创建新的绑定范围,因此在元编程实践中更方便。

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

以下是my_method调用的输出:

>> 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

【讨论】:

  • 谢谢@Arie。您能否解释一下为什么有必要添加单例实例,因为 A 类已经被猴子修补以使用新的方法定义。我不清楚“class
  • @codeObserver 我以为您想对特殊实例的实例方法进行更改(从创建开始并持续到实例的整个生命周期)。如果为特殊的new_instance 恢复方法而不使用猴子补丁,new_instance 将使用恢复后方法的原始实现。单例实例的猴子补丁使新方法可以在new_instance 的整个生命周期中使用。如果你只是想在创建过程中改变行为,你可以省略单例类的补丁。
  • @codeObserver class &lt;&lt; new_instance; self end 返回new_instance 的特征类(或单例类,~Google it~)。这是一个与new_instance 相关的特殊类。该类中定义的所有方法仅对new_instance 可见。所以这就是猴子修补给定实例而不是给定类的所有实例的方法。
  • 我不知道我的问题是否适合这个线程,但我认为#class_eval 对我不起作用,因为我想重新定义使用类常量的类的方法:在您的示例,A.class_eval 关闭了#my_method 的范围,而class A 将关闭了 A 类的范围。因此,如果在我重新定义的方法中,我尝试访问 A 中定义的(类)常量,我将#class_eval 出现“未初始化的常量”错误。这就是为什么,我仍然想知道,我怎样才能在方法中对 A 类进行猴子修补,就像我重新打开 class A 一样?
猜你喜欢
  • 2011-12-15
  • 2017-05-24
  • 2012-05-07
  • 1970-01-01
  • 1970-01-01
  • 2021-09-03
  • 1970-01-01
  • 2012-04-26
  • 1970-01-01
相关资源
最近更新 更多