“我该如何清洁?”;“重新连接”;Ruby中的分离方法?

“我该如何清洁?”;“重新连接”;Ruby中的分离方法?,ruby,Ruby,假设我有这门课: class Foo def destroy_target(target) Missile.launch(target) end end 我想暂时消除Foo的破坏力,例如用于测试目的,所以我这样做: backup = Foo.instance_method(:destroy_target) class Foo def destroy_target(target) Pillow.launch(target) end end 我的问题是:如何将原

假设我有这门课:

class Foo
  def destroy_target(target)
    Missile.launch(target)
  end
end
我想暂时消除
Foo
的破坏力,例如用于测试目的,所以我这样做:

backup = Foo.instance_method(:destroy_target)

class Foo
  def destroy_target(target)
    Pillow.launch(target)
  end
end
我的问题是:如何将原始方法“重新附加”到
Foo
,就好像它一开始从未被重写一样

我意识到我可以做到这一点:

class Foo
  def destroy_target(target)
    backup.bind(self).call(target)
  end
end
但显然这不是最优的,因为我现在正在包装原始函数。我希望能够无限次地分离和重新连接该方法,而不增加任何开销

用另一种方式问;如何将
DetachedMethod
正确地附加到类,即不定义调用分离方法的新方法


注意:我对临时更改类功能的其他方法不感兴趣。我特别想知道如何用不同的方法替换一个方法,然后干净地恢复原始方法。

这似乎有效:

Foo.instance_exec {
  define_method(:destroy_target, backup)
}
但我不完全确定这是否没有副作用。如果有人确实知道的话,我将非常感谢你的评论

如果
Foo
是这样定义的一个模块,那么这似乎也可以工作:

module Foo
  extend self

  def destroy_target(target)
    Missile.launch(target)
  end
end

我测试了您的第一个示例,它似乎运行良好。我没有发现任何副作用,但这并不意味着没有副作用

你考虑过了吗?

上课 它可能会带来比您的示例更标准、更容易理解的优势

对于模块 如果
Foo
是一个
模块
,您不能直接调用
refine Foo
,您将得到一个
TypeError:错误的参数类型模块(预期类)

但是,您可以优化它的
单例类

module Foo
  def self.destroy_target(target)
    Missile.launch(target)
  end
end

module PillowLauncher
  refine Foo.singleton_class do
    def destroy_target(target)
      Pillow.launch(target)
    end
  end
end

module Test
  using PillowLauncher
  Foo.destroy_target('Tatooine')
  #=> PILLOW -> Tatooine
end

Foo.destroy_target('Tatooine')
#=> MISSILE -> Tatooine
我不确定你的笔记:

我对临时改变的其他方式不感兴趣 类的功能。我特别想知道如何 用其他方法替换一个方法,然后恢复原始方法 方法干净


我建议的代码似乎两者都能做到。

backup=Foo.instance\u方法(:destroy\u target)
没有
s
你想为一个特定的
Foo
实例或类范围(即所有
Foo
s)替换方法吗?
定义方法
似乎正确,但我会使用
Foo.send(:define_method,:destroy_target,backup)
–它看起来没有那么侵入性。改进看起来很酷,但显然它们只适用于类(而不是模块)。在我当前的用例中,我需要它来处理模块。在Ruby版本2.3之前,它们还必须在顶级激活,也就是说,正如您所演示的,它们不能在类或模块内激活。我用2.1.5和2.2.1测试了这段代码。我实际上对它的工作感到惊讶,因为文档只提到了顶级的改进。
module Foo
  def self.destroy_target(target)
    Missile.launch(target)
  end
end

module PillowLauncher
  refine Foo.singleton_class do
    def destroy_target(target)
      Pillow.launch(target)
    end
  end
end

module Test
  using PillowLauncher
  Foo.destroy_target('Tatooine')
  #=> PILLOW -> Tatooine
end

Foo.destroy_target('Tatooine')
#=> MISSILE -> Tatooine