加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ruby – 将类/模块恢复到处女状态

发布时间:2020-12-17 02:24:53 所属栏目:百科 来源:网络整理
导读:我想知道RSpec如何实现这个目标: expect( MyObject ).to receive(:the_answer).and_return(42) RSpec是否利用define_singleton_method将the_answer注入MyObject? 那可能有用.让我们假装这是MyObject的样子: class MyObject def self.the_answer 21 * 2 en
我想知道RSpec如何实现这个目标:

expect( MyObject ).to receive(:the_answer).and_return(42)

RSpec是否利用define_singleton_method将the_answer注入MyObject?

那可能有用.让我们假装这是MyObject的样子:

class MyObject
    def self.the_answer
        21 * 2
    end
end

注入的the_answer方法可能包含以下内容:

@the_answer_called = true
42

然而,RSpec如何将类恢复到其未冻结状态?它如何恢复它以便MyObject.the_answer“真正”再次返回42? (通过21 * 2)

自写这个问题以来,我认为它没有.在RSpec停止运行之前,类方法仍然被模拟.

然而,(这是问题的关键)如何撤消define_singleton_method?

我认为最简单的方法是在使用define_singleton_method之前运行old_object = Object.dup,但是,如何将old_object恢复为Object?

当我想要做的就是恢复一个类方法时,这也不会让我觉得有效.虽然目前这不是一个问题,但也许将来我也希望将MyObject中的某些实例变量保持不变.是否有一种非hacky方式复制代码(21 * 2)并通过define_singleton_method重新定义为the_answer而不是完全替换Object?

欢迎所有想法,我绝对想知道如何制作Object === old_object.

解决方法

RSpec不会复制/替换实例或其类.它动态删除实例方法并定义一个新方法.以下是它的工作原理:(你可以对类方法做同样的事情)

给定您的类MyObject和一个实例o:

class MyObject
  def the_answer
    21 * 2
  end
end

o = MyObject.new
o.the_answer #=> 42

RSpec首先使用Module#instance_method保存原始方法.它返回UnboundMethod

original_method = MyObject.instance_method(:the_answer)
#=> #<UnboundMethod: MyObject#the_answer>

然后它使用Module#remove_method删除方法:(我们必须在这里使用send,因为remove_method是私有的).

MyObject.send(:remove_method,:the_answer)

并使用Module#define_method定义一个新的:

MyObject.send(:define_method,:the_answer) { 'foo' }

如果您现在调用the_answer,则会立即调用新方法:

o.the_answer #=> "foo"

在示例之后,RSpec删除了新方法

MyObject.send(:remove_method,:the_answer)

并恢复原始的(define_method接受块或方法):

MyObject.send(:define_method,:the_answer,original_method)

调用方法按预期工作:

o.the_answer #=> 42

RSpec的实际代码要复杂得多,但你明白了.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读