联想延伸在哪里

时间:2022-06-01 20:44:01

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.

附带说明:我不能在关系对象上使用延迟的作业,因为它包含一个不能序列化的匿名模块。所以最后我要用类方法来实现我想要的。