【问题标题】:how do use define_method on an instance of an object on objects of unknown type?如何在未知类型对象上的对象实例上使用 define_method?
【发布时间】:2012-12-31 04:51:28
【问题描述】:

所以,我有点想做类似于 rspec / mocha 的 mock 的事情,但只针对两个对象,而不是全部。这是我目前所拥有的:

def mock(obj, method_to_mock, value)
    obj.class << obj do
        define_method(method_to_mock) do
            return value
        end
    end
end

我从这篇文章中得到了这样写的想法:https://stackoverflow.com/a/185969/356849

那么我可以做这样的事情:

mock(self.instantiated, :sections, sections)

它会用我的 Section 对象数组sections 覆盖我存储在self.instantiatedsections 中的对象。

我这样做的原因是因为我正在存储对象的序列化和加密版本,并且我希望能够对对象进行解密和反序列化,然后恢复所有关系,以便我可以在我的视图中查看该对象,就好像它是从数据库中读取的一样。但这并不重要,而且大部分都已经完成了。

所以,我希望能够做到这一点:

mock(&lt;Instance of object&gt;, :&lt;method of object that is going to be overridden, to avoid db access&gt;, &lt;the stuff to return when the overridden method is invoked)

目前,我在obj.class &lt;&lt; obj do 行遇到错误:

NoMethodError: undefined method `obj' for #<MyObject::Encrypted:0x7f190eebcd18>

想法?


更新

将第二行改为class &lt;&lt; obj,现在无限循环。

from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/connection_adapters/abstract/connection_pool.rb:351:in `retrieve_connection_pool'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/connection_adapters/abstract/connection_pool.rb:351:in `retrieve_connection_pool'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/connection_adapters/abstract/connection_pool.rb:325:in `retrieve_connection'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/connection_adapters/abstract/connection_specification.rb:123:in `retrieve_connection'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/connection_adapters/abstract/connection_specification.rb:115:in `connection'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/base.rb:1305:in `columns'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/base.rb:1318:in `column_names'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/searchlogic-2.4.28/lib/searchlogic/named_scopes/ordering.rb:35:in `ordering_condition_details'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/searchlogic-2.4.28/lib/searchlogic/named_scopes/ordering.rb:26:in `method_missing'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/searchlogic-2.4.28/lib/searchlogic/named_scopes/or_conditions.rb:28:in `method_missing'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/activerecord-2.3.15/lib/active_record/base.rb:2002:in `method_missing_without_paginate'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/gems/will_paginate-2.3.16/lib/will_paginate/finder.rb:170:in `method_missing_without_attr_encrypted'
from /home/me/.rvm/gems/ruby-1.8.7-p371@project/bundler/gems/attr_encrypted-a4b25f01d137/lib/attr_encrypted/adapters/active_record.rb:50:in `method_missing'
from /home/me/Work/GravityLabs/project/app/models/proposal/encrypted.rb:119:in `mock'
from /home/me/Work/GravityLabs/project/app/models/proposal/encrypted.rb:79:in `instantiate'
from /home/me/Work/GravityLabs/project/app/models/proposal/encrypted.rb:58:in `each'
from /home/me/Work/GravityLabs/project/app/models/proposal/encrypted.rb:58:in `instantiate'

【问题讨论】:

  • class &lt;&lt; self 是进入self的单例类的特殊表达式。试试class &lt;&lt; objclass是关键字,不是方法。
  • 现在我已经做到了,它无限循环。 o.o 我的猜测是,现在它为obj 的每个实例定义了新方法,而不仅仅是一个。 :-\

标签: ruby-on-rails ruby mocking metaprogramming


【解决方案1】:

obj.class &lt;&lt; obj do 毫无意义。

你可能想说的是

def mock(obj, method_to_mock, value)
  (class << obj; self; end).class_eval do
    define_method(method_to_mock) do
      return value
    end
  end
end

(class &lt;&lt; obj; self; end).class_eval 语法是打开 obj 的单例类返回该单例类,然后在该单例类上调用 class_eval 传递块。

在您的语法中,obj.class:class 消息作为接收器发送到obj,该接收器返回对 obj 类(而不是其单例类)的引用,然后您尝试在其上调用 &lt;&lt; 方法传递将 obj do...end 评估为 arg 的结果。因为obj 不是self (MyObject::Encrypted:0x7f190eebcd1) 的方法,所以你会得到NoM​​ethodError。

在现代 ruby​​ 中,不要说相对晦涩的 (class &lt;&lt; obj; self; end) 来获取单例类,您可以使用 singleton_class 方法,如下所示:obj.singleton_class.class_eval do ... end

【讨论】:

  • 感谢您的解释!每个对象都有一个单例类吗?我记得我在 Java 时代的单例只是一个只能实例化一次的类。
  • 在 ruby​​ 中,每个对象都有一个单例类,有时也称为元类或特征类。 ruby 单例类本质上是一个匿名类,插入到对象与其基类之间的祖先链中。我们称之为“类方法”的东西实际上是类的单例方法(请记住,类只是 ruby​​ 中的对象)。 Fuller explanation here 这与 Singleton 不同,Singleton 是一个只能有一个实例的对象。
【解决方案2】:
def mock(obj, method_to_mock, value=nil)
  obj.define_singleton_method(method_to_mock) do value end
end  

【讨论】:

  • 哎呀!应该提到我正在使用 ruby​​ 1.8.7 / rails 2.3.15。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-22
  • 2015-06-12
  • 2019-03-23
相关资源
最近更新 更多