3

我有一个关联扩展方法,如下所示:

class Bundle < ActiveRecord::Base
  has_many :items do
    def foo
    end
  end

我尝试使用延迟作业/sidekiqdelay()方法,如下所示:

b.items.delay.foo

但我不能。您会看到,当调用延迟时,关联立即评估为记录数组。该数组没有关联扩展方法。

所以我尝试检查b.items.proxy_association.methods,令我惊讶的foo()是,也不在那里。

foo()我的方法位于哪个对象中?

4

3 回答 3

1

在此处collection_association.rb

#has_many calls this eventually
def build
  wrap_block_extension
  ...
end

接着

#here model refers to you model
#block_extension is the block you write with in the has_many definition(def foo blah blah)
def wrap_block_extension
  ...
  model.parent.const_set(extension_module_name, Module.new(&block_extension))
  options[:extend].push("#{model.parent}::#{extension_module_name}".constantize)
end

def extension_module_name
    @extension_module_name ||= "#{model.to_s.demodulize}#{name.to_s.camelize}AssociationExtension"
end

我没有仔细阅读源代码,但我认为我们可以从这里得到一个有根据的猜测:b.items返回一个对象(我忘记了类名,一些代理或其他东西,对不起),当方法丢失时,它会看起来到其他地方,包括options[:extend],所以在这种情况下,它给了我们我们想要的东西b.items.foo

但是当你调用 时b.items.bar,它不会找到任何相关的东西,所以它会首先检查b.itemsArray 的“返回值”是否响应 method #bar,如果仍然没有,它会要求Item#bar最后一次尝试。

在你的情况下,#delay是一种被 识别的方法Array,所以它真的很像tmp1 = b.items.all; tmp2 = tmp1.delay; tmp2.foo,所以foo肯定是无处可寻。

于 2013-07-16T03:22:06.983 回答
1

感谢那些回答的人。它们不是 100% 正确的,但到目前为止还不是。所以我会留下赏金,让你们分享。

BundleItemsAssociationExtension混合到 ActiveRecord::Relation 对象中。所以当我打电话时:

bundle.items.scoped.methods # returns array containing my :foo

所以 ActiveRecord::Relation 对象是包含我的扩展方法的对象。


作为旁注:事实证明我不能在 Relation 对象上使用延迟作业,因为它包含一个无法序列化的匿名模块。所以最后我必须使用类方法来做我想要实现的事情。

于 2013-07-16T03:33:20.897 回答
1

延迟作业的延迟函数检查对象是否响应排队的方法并返回 a NoMethodError,因为该函数没有为 Items 类定义。扩展的工作方式是向 proxy_association 的所有者类添加一个模块,在这种情况下,您将能够从Bundle::BundleItemsAssociationExtension.instance_methods. 因此,即使您传递关联对象,延迟方法也无法访问 foo 方法。我建议将该方法移至 Items 类,然后在其上调用延迟作业。

于 2013-07-15T22:12:00.290 回答