Я создаю гем Rails 3, который существенно модифицирует записи, возвращаемые из запроса ActiveRecord.Одна из вещей, которые я делаю, это переопределение методов method_missing
и respond_to?
, но, похоже, мое определение respond_to?
приводит к бесконечному циклу, который выдает ошибку «SystemStackError: слишком большой уровень стека».
Вот мои оригинальные определения этих методов:
def respond_to?(name, *args)
super(name, *args) || parent_association.respond_to?(name)
end
def method_missing(name, *args, &block)
if parent_association.respond_to?(name)
parent_association.send(name, *args, &block)
else
super(name, *args, &block)
end
end
def parent_association
send(parent_association_name) # Essentially yields another ActiveRecord
# instance (e.g.: instance of User), but
# never returns itself.
end
Пытаясь понять, почему происходил этот бесконечный цикл, я реструктурировал respond_to?
с некоторыми выводами «до» и «после» впосмотрите, где он застревает.
def respond_to?(name, *args)
return true if super(name, *args)
puts "before (#{name})"
result = parent_association.respond_to?(name)
puts "after"
result
end
При запуске кажется, что различные обратные вызовы и методы атрибутов работают, как и ожидалось, с одним вызовом до и после для каждого:
before (_run__374051839217347232__initialize__1707831318230746190__callbacks)
after
before (_run__374051839217347232__validation__1707831318230746190__callbacks)
after
before (_run__374051839217347232__validate__1707831318230746190__callbacks)
after
before (_run__374051839217347232__save__1707831318230746190__callbacks)
after
before (_run__374051839217347232__create__1707831318230746190__callbacks)
after
before (created_at)
after
before (created_on)
after
...
Однаковсякий раз, когда я вижу обратный вызов поиска, который, кажется, зацикливается на бесконечном цикле:
before (_run__374051839217347232__find__1707831318230746190__callbacks)
before (_run__374051839217347232__find__1707831318230746190__callbacks)
before (_run__374051839217347232__find__1707831318230746190__callbacks)
before (_run__374051839217347232__find__1707831318230746190__callbacks)
before (_run__374051839217347232__find__1707831318230746190__callbacks)
before (_run__374051839217347232__find__1707831318230746190__callbacks)
before (_run__374051839217347232__find__1707831318230746190__callbacks)
before (_run__374051839217347232__find__1707831318230746190__callbacks)
...
SystemStackError: stack level too deep
Если я взломаю свой respond_to?
, то все будет работать гладко:
def respond_to?(name, *args)
return true if super(name, *args)
return false if name =~ /^_run_.*_find_.*_callbacks$/
parent_association.respond_to?(name)
end
Что я делаю не так, что мне, кажется, нужен этот взлом?И как мне этого избежать?