我有一个关联扩展方法,如下所示:
class Bundle < ActiveRecord::Base
has_many :items do
def foo
end
end
我尝试使用延迟作业/ sidekiq delay()
方法,如下所示:
b.items.delay.foo
但我不能。您会看到,在调用延迟时,关联会立即评估到一组记录。该数组没有关联扩展方法。
所以我尝试检查b.items.proxy_association.methods
,令我惊讶的是,foo()
也不存在。
我的foo()
方法中有哪个对象?
答案 0 :(得分:1)
延迟作业的延迟功能检查对象是否响应排队的方法并返回NoMethodError
,因为没有为Items类定义该功能。扩展的工作方式是将模块添加到proxy_association的所有者类,在这种情况下,您将能够从Bundle::BundleItemsAssociationExtension.instance_methods
访问该函数。因此,即使传递关联对象,延迟方法也无法访问foo方法。我建议将方法移动到Items类,然后在其上调用延迟作业。
答案 1 :(得分: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.items
的'返回值',它是一个数组,是否响应方法{{ 1}},如果还没有,它会调用#bar
进行最后的尝试。
在您的情况下,Item#bar
是#delay
识别的方法,因此它非常像Array
,所以tmp1 = b.items.all; tmp2 = tmp1.delay; tmp2.foo
肯定无处可寻。
答案 2 :(得分:1)
谢谢那些回答的人。他们不是100%正确,但不是到目前为止。所以我会留下赏金,以便你们分享。
BundleItemsAssociationExtension
混合到ActiveRecord :: Relation对象中。所以当我打电话时:
bundle.items.scoped.methods # returns array containing my :foo
所以ActiveRecord :: Relation对象是包含我的扩展方法的对象。
作为旁注:事实证明我不能在Relation对象上使用Delayed Job,因为它包含一个无法序列化的匿名模块。所以最后我必须使用类方法来完成我想要实现的目标。