Где находится расширение ассоциации?
У меня есть метод расширения ассоциации, как показано ниже:
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, а затем вызвать для него отложенное задание.