Object.respond_to?stecke in einer Endlosschleife fest
-
13-12-2019 - |
Frage
Ich baue ein Rails 3-Gem, das die von einer ActiveRecord-Abfrage zurückgegebenen Datensätze im Wesentlichen ändert.Eines der Dinge, die ich mache, ist das Überschreiben method_missing
Und respond_to?
Methoden, aber es scheint, dass meine respond_to?
Definition führt zu einer Endlosschleife, die einen „SystemStackError:Fehler „Stapelebene zu tief“.
Hier sind meine ursprünglichen Definitionen dieser Methoden:
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
Als ich versuchte herauszufinden, warum es zu dieser Endlosschleife kam, habe ich eine Umstrukturierung vorgenommen respond_to?
mit einigen „Vorher“- und „Nachher“-Ausgaben, um zu sehen, wo es hängen bleibt.
def respond_to?(name, *args)
return true if super(name, *args)
puts "before (#{name})"
result = parent_association.respond_to?(name)
puts "after"
result
end
Bei der Ausführung scheint es, dass verschiedene Rückrufe und Attributmethoden wie erwartet ausgeführt werden, mit jeweils einem einzigen Vorher- und Nachher-Aufruf:
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
...
Jedes Mal, wenn ich jedoch einen Find-Rückruf sehe, scheint dieser in einer Endlosschleife gefangen zu sein:
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
Wenn ich meine hacke respond_to?
, dann scheint alles reibungslos zu laufen:
def respond_to?(name, *args)
return true if super(name, *args)
return false if name =~ /^_run_.*_find_.*_callbacks$/
parent_association.respond_to?(name)
end
Was mache ich falsch, dass ich diesen Hack anscheinend brauche?Und wie kann ich es vermeiden?
Lösung 2
Das Problem war letztendlich diese Funktion:
def parent_association
send(parent_association_name) # Essentially yields another ActiveRecord
# instance (e.g.: instance of User), but
# never returns itself.
end
Die Variable parent_association_name
ist so etwas wie employee
, was durch etwas wie Folgendes definiert wird:
belongs_to :employee
Weil employee
wird auf der Modellinstanz erst definiert, NACHDEM der Suchrückruf ausgeführt wurde und weil ich zum ersten Mal aufgerufen habe respond_to?
an einer Stelle, BEVOR der Find-Rückruf aufgerufen wird (in Code Nr. in meiner ursprünglichen Frage enthalten), der Aufruf von send(parent_assocation_name)
verursachte einen rekursiven Aufruf von respond_to?
.
Andere Tipps
Wenn parent_association
ein neues AR-Objekt zurückgibt, erbt es auch Ihren Hack, sodass der generationspflichtige respond_to?
aufrufen wird, wodurch Ihr generationstätiges Objektcode aufgerufen wird, der ein AR neues Objekt erstellt usw.