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?

Était-ce utile?

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...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top