Где находится расширение ассоциации?

У меня есть метод расширения ассоциации, как показано ниже:

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() метод сидеть в?

3 ответа

Решение

Спасибо тем, кто ответил. Они не на 100% правильные, но не совсем. Так что я оставлю награду, так что вы, ребята, поделились ею.

BundleItemsAssociationExtension смешивается с объектом ActiveRecord:: Relation. Поэтому, когда я звоню:

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

Таким образом, ActiveRecord:: Relation является объектом, содержащим мои методы расширения.


В качестве примечания: оказывается, что я не могу использовать отложенное задание для объекта Relation, потому что он содержит анонимный модуль, который не может быть сериализован. В конце концов, я должен использовать метод класса, чтобы делать то, что я хочу достичь.

Здесь в 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, который является массивом, отвечает на метод #barи, если все еще нет, он вызывает Item#bar для последней попытки.

В твоем случае, #delay это метод, признанный Arrayтак что это действительно как tmp1 = b.items.all; tmp2 = tmp1.delay; tmp2.foo, так foo наверняка нигде не найти.

Функция задержки отложенного задания проверяет, отвечает ли объект методу, находящемуся в очереди, и возвращает ли NoMethodError потому что функция не определена для класса Items. Способы работы расширений заключаются в добавлении модуля в класс владельца proxy_association, в этом случае вы сможете получить доступ к функции из Bundle::BundleItemsAssociationExtension.instance_methods, Следовательно, метод delay не сможет получить доступ к методу foo, даже если вы передадите объект ассоциации. Я бы предложил переместить метод в класс Items, а затем вызвать для него отложенное задание.

Другие вопросы по тегам