Objet.respond_to?coincé dans une boucle infinie
-
13-12-2019 - |
Question
Je suis en train de construire un Rails 3 gemme qui modifie essentiellement les enregistrements renvoyés à partir d'un ActiveRecord requête.L'une des choses que je fais est la substitution de la method_missing
et respond_to?
méthodes, mais il semble que mon respond_to?
définition entraîne dans une boucle infinie qui est en train de lancer un "SystemStackError:niveau de la pile trop profonde erreur".
Voici ma définition de départ de ces méthodes:
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
En essayant de savoir pourquoi cette boucle infinie qui se passait, j'ai restructuré respond_to?
avec un "avant" et "après" la sortie pour voir où il est coincé.
def respond_to?(name, *args)
return true if super(name, *args)
puts "before (#{name})"
result = parent_association.respond_to?(name)
puts "after"
result
end
Lors de l'exécution, il semble que divers rappels et attribut des méthodes de fonctionner comme prévu, avec un seul avant et après l'appel de chacun:
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
...
Cependant, toutes les fois que je vois un trouver de rappel, qui semble être pris dans une boucle infinie:
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
Si je bidouille mon respond_to?
, puis tout semble fonctionner correctement:
def respond_to?(name, *args)
return true if super(name, *args)
return false if name =~ /^_run_.*_find_.*_callbacks$/
parent_association.respond_to?(name)
end
Ce que je fais mal que j'ai l'impression d'avoir besoin de ce hack?Et comment puis-je l'éviter?
La solution 2
La question a fini par être cette fonction:
def parent_association
send(parent_association_name) # Essentially yields another ActiveRecord
# instance (e.g.: instance of User), but
# never returns itself.
end
La variable parent_association_name
c'est quelque chose comme employee
, qui est défini par quelque chose comme:
belongs_to :employee
Parce que employee
n'est pas défini sur le modèle de l'instance jusqu'à ce que APRÈS la découverte de rappel s'exécute, et parce que j'ai été le premier appel respond_to?
dans un endroit AVANT de le trouver callback est appelé (dans le code pas inclus dans ma question initiale), l'appel à send(parent_assocation_name)
était à l'origine un appel récursif de respond_to?
.
Autres conseils
Si parent_association
renvoie un nouvel AR objet, il hérite également votre hack, afin de l'appelant respond_to?
sur ce qui fera appel à votre respond_to?
, créer un AR nouvel objet, etc...