【发布时间】:2021-10-31 07:17:18
【问题描述】:
说,我有:
class Test
def initialize(m)
@m = m
end
def test
@m
end
end
如何暂时使Test的所有实例(现有的和新的)的方法#test返回113,然后再恢复原来的方法?
这听起来很简单,但我找不到实现它的好方法。可能是因为我对 Ruby 的了解太少。
到目前为止我发现的是:
# saving the original method
Test.send(:alias_method, :old_test, :test)
# redefining the method
Test.send(:define_method, :test) { 113 }
# restore the original method
Test.send(:alias_method, :test, :old_test)
这是什么工作,但据我了解,它还会重新定义现有的 #old_test(如果存在的话)?.. 感觉就像是一种 hack,而不是正确使用元编程?..
- 是否可以在不使用别名的情况下执行此类操作(但也可以不修改
Test的源代码)? - 如果没有(或者如果有更简单/更好的方法),如果你可以修改
Test的源代码,你会怎么做?
如果您能描述实现同一目标的多种方法,即使是那些困难或不切实际的方法,我将不胜感激。只是想了解一下 Ruby 中元编程的灵活性和局限性:)
非常感谢????
附:我开始这一切的原因:
我正在使用 gem rack-throttle 来限制以 /api 开头的请求,但其他 url 不应该受到影响。我想测试所有这些以确保它有效。为了测试节流,我也必须将中间件添加到测试环境中。我已经成功地对其进行了测试(使用 minitest),但是不应限制所有其他测试 ApiController 的测试,因为如果我们需要在每个请求后等待 1 秒,它会使测试花费更长的时间。
我决定在 minitest 的 #setups 中用 { true } 修补 RequestSpecificIntervalThrottle#allowed? 以暂时禁用所有这些测试的节流,然后在 #teardowns 中重新启用它(否则测试节流的测试本身会失败)。如果你能告诉我你将如何处理这个问题,我将不胜感激。
但是现在我已经开始深入研究元编程,我也只是好奇如何实现这一点(暂时重新定义一个方法),即使我实际上并不打算使用它。
【问题讨论】:
-
暂时重新定义方法的原因是什么?从表面上看(不知道原因),这似乎是异常处理或具有替代路径的条件语句更合适的情况。
-
@MichaelB 我现在已经编辑了问题——添加了原因:)
-
存根可以满足您的需要吗?无需“重新定义”该方法,您可以在需要发生不同事情的测试中将其存根。
-
Rspec 支持
allow_和expect_any_instance_of(Class).to receive(method_name)relishapp.com/rspec/rspec-mocks/docs/working-with-legacy-code/… -
@JohnP @melcher 顺便告诉你。我使用
Test.any_instance.stubs(:test).returns(113)完成了它,它运行得非常好,这基本上是你建议我的。非常感谢
标签: ruby metaprogramming class-eval instance-eval