I have an association extension method like the following:
我有一个关联扩展方法如下:
class Bundle < ActiveRecord::Base
has_many :items do
def foo
end
end
I tried to use delayed job/sidekiq delay()
method like the following:
我尝试使用延迟作业/sidekiq delay()方法,如下所示:
b.items.delay.foo
But I can't. You see, when delay is called, association evaluated immediately to an array of records. That array does not have the association extension methods.
但我不能。您可以看到,当调用延迟时,关联会立即对一组记录进行评估。该数组没有关联扩展方法。
So I tried inspecting b.items.proxy_association.methods
, and to my surprise, foo()
is not there either.
所以我尝试检查b.items.proxy_association。方法,令我惊讶的是,foo()也不存在。
Which object does my foo()
method sit in?
我的foo()方法位于哪个对象中?
3 个解决方案
#1
1
Delayed Job's delay function checks for whether the object responds to the method that is queued and would return a NoMethodError
because the function is not defined for the Items class. The ways extensions work is by adding a module to the owner class of the proxy_association, in this case you will be able to access the function from Bundle::BundleItemsAssociationExtension.instance_methods
. Therefore, the delay method won't be able to access foo method even if you pass an association object. I would suggest moving the method to the Items class and then calling delayed job on it.
延迟Job的delay函数检查对象是否响应队列中的方法,并返回NoMethodError,因为这个函数没有为Items类定义。扩展的工作方式是向proxy_association的所有者类添加一个模块,在这种情况下,您将能够从Bundle: BundleItemsAssociationExtension.instance_methods访问函数。因此,即使您传递了一个关联对象,延迟方法也不能访问foo方法。我建议将方法移动到Items类,然后调用它上的deferred job。
#2
1
Here in collection_association.rb
在collection_association.rb
#has_many calls this eventually
def build
wrap_block_extension
...
end
And then
然后
#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
I didn't go through the source code, but I think we can get an educated guess from here: b.items
returns an object(I forgot the class name, some proxy or something, sorry), which when method missing happens, it'll look into somewhere else, including options[:extend]
, so in this case it gives us what we want by b.items.foo
.
我没有仔细阅读源代码,但是我认为我们可以从这里得到一个有根据的猜测:b。项返回一个对象(我忘记了类名、某个代理或其他东西,不好意思),当出现方法丢失时,它将查找其他地方,包括选项[:extend],因此在本例中,它给出了我们所需的b.items.foo。foo。
But when you call b.items.bar
, it won't find anything relative, so it'll first check if the 'return value' of b.items
, which is an Array, responds to method #bar
, and if still not, it calls Item#bar
for a final try.
但当你打电话给b项时。bar,它不会找到任何相关的东西,所以它会首先检查b的“返回值”。项目,它是一个数组,响应方法#bar,如果仍然没有,它调用项目#bar进行最后的尝试。
In your case, #delay
is a method recognized by Array
, so it's really like tmp1 = b.items.all; tmp2 = tmp1.delay; tmp2.foo
, so foo
is surely nowhere to be found.
在你的例子中,#delay是一个被数组识别的方法,所以它非常像tmp1 = b。tmp2 = tmp1.delay;tmp2。foo,所以foo肯定无处可寻。
#3
1
Thanks those who answered. They are not 100% correct but not by far. So I'll leave the bounty so it is shared by you guys.
谢谢那些回答。他们不是百分之百正确,但也不是绝对正确。我留下赏金,让你们分享。
The BundleItemsAssociationExtension
is mixed into the ActiveRecord::Relation object. So when I call:
BundleItemsAssociationExtension混合到ActiveRecord::关系对象中。所以当我叫:
bundle.items.scoped.methods # returns array containing my :foo
So the ActiveRecord::Relation object is the one containing my extension methods.
所以ActiveRecord::关系对象是包含我的扩展方法的对象。
As a side note: it turns out I can't use Delayed Job on Relation object, because it includes an anonymous module that can't be serialized. So in the end I have to use class method to do what I want to achieve.
附带说明:我不能在关系对象上使用延迟的作业,因为它包含一个不能序列化的匿名模块。所以最后我要用类方法来实现我想要的。
#1
1
Delayed Job's delay function checks for whether the object responds to the method that is queued and would return a NoMethodError
because the function is not defined for the Items class. The ways extensions work is by adding a module to the owner class of the proxy_association, in this case you will be able to access the function from Bundle::BundleItemsAssociationExtension.instance_methods
. Therefore, the delay method won't be able to access foo method even if you pass an association object. I would suggest moving the method to the Items class and then calling delayed job on it.
延迟Job的delay函数检查对象是否响应队列中的方法,并返回NoMethodError,因为这个函数没有为Items类定义。扩展的工作方式是向proxy_association的所有者类添加一个模块,在这种情况下,您将能够从Bundle: BundleItemsAssociationExtension.instance_methods访问函数。因此,即使您传递了一个关联对象,延迟方法也不能访问foo方法。我建议将方法移动到Items类,然后调用它上的deferred job。
#2
1
Here in collection_association.rb
在collection_association.rb
#has_many calls this eventually
def build
wrap_block_extension
...
end
And then
然后
#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
I didn't go through the source code, but I think we can get an educated guess from here: b.items
returns an object(I forgot the class name, some proxy or something, sorry), which when method missing happens, it'll look into somewhere else, including options[:extend]
, so in this case it gives us what we want by b.items.foo
.
我没有仔细阅读源代码,但是我认为我们可以从这里得到一个有根据的猜测:b。项返回一个对象(我忘记了类名、某个代理或其他东西,不好意思),当出现方法丢失时,它将查找其他地方,包括选项[:extend],因此在本例中,它给出了我们所需的b.items.foo。foo。
But when you call b.items.bar
, it won't find anything relative, so it'll first check if the 'return value' of b.items
, which is an Array, responds to method #bar
, and if still not, it calls Item#bar
for a final try.
但当你打电话给b项时。bar,它不会找到任何相关的东西,所以它会首先检查b的“返回值”。项目,它是一个数组,响应方法#bar,如果仍然没有,它调用项目#bar进行最后的尝试。
In your case, #delay
is a method recognized by Array
, so it's really like tmp1 = b.items.all; tmp2 = tmp1.delay; tmp2.foo
, so foo
is surely nowhere to be found.
在你的例子中,#delay是一个被数组识别的方法,所以它非常像tmp1 = b。tmp2 = tmp1.delay;tmp2。foo,所以foo肯定无处可寻。
#3
1
Thanks those who answered. They are not 100% correct but not by far. So I'll leave the bounty so it is shared by you guys.
谢谢那些回答。他们不是百分之百正确,但也不是绝对正确。我留下赏金,让你们分享。
The BundleItemsAssociationExtension
is mixed into the ActiveRecord::Relation object. So when I call:
BundleItemsAssociationExtension混合到ActiveRecord::关系对象中。所以当我叫:
bundle.items.scoped.methods # returns array containing my :foo
So the ActiveRecord::Relation object is the one containing my extension methods.
所以ActiveRecord::关系对象是包含我的扩展方法的对象。
As a side note: it turns out I can't use Delayed Job on Relation object, because it includes an anonymous module that can't be serialized. So in the end I have to use class method to do what I want to achieve.
附带说明:我不能在关系对象上使用延迟的作业,因为它包含一个不能序列化的匿名模块。所以最后我要用类方法来实现我想要的。