Question

Comment puis-je déterminer facilement et par programme si un LocalJumpError résulte de l'incapacité immédiate de l'appelant à fournir un bloc nécessaire à une méthode, ou de manière plus approfondie au sein de cette méthode et d'autres qu'il appelle?

Par " facilement, " Je veux dire que je voudrais éviter l'inspection de chaîne / l'expression régulière sur $ !. backtrace . Une solution applicable à 1.8 et 1.9 est également préférable.

Motivation: lorsque je botse un appel de méthode en ruby, c'est généralement parce que la méthode que j'ai saisie est incorrecte ( NoMethodError ), le nombre d'arguments est incorrect ( ArgumentError ) ou négligé de passer un bloc nécessaire ( LocalJumpError ).

Pour un objet wrapper de décoration ou de proxy dans ruby, j'aimerais distinguer ces erreurs d'appelant ou d'API de l'implémenteur ou des erreurs d'environnement pouvant soulever les mêmes classes. d'erreur. Par exemple:

...
def method_missing(sym, *args, &block)
  @wrapped.__send__(sym, *args, &block)
rescue NoMethodError
  raise MyApp::BadInvocation, "duh - no such method" unless @wrapped.respond_to?(sym)
  raise
rescue ArgumentError
  raise MyApp::BadInvocation, "duh - wrong arg count" \
    unless _args_fit_arity?(@wrapped.method(sym), args)
  raise
rescue LocalJumpError
  # XXX - what is the test?
  raise
end
Était-ce utile?

La solution

Pour savoir si LocalJumpError a été causé par l’oubli d’un bloc, vous devez savoir deux choses: si l’utilisateur a fourni un bloc et si la méthode a besoin d’un bloc. La première est simple: vérifiez si blk est nul. La seconde est toutefois impossible (en rubis ordinaire au moins).

Je suppose donc que l'analyse de la trace de pile est votre meilleur choix.

Autres conseils

On peut examiner la profondeur relative de la trace de retour afin de distinguer au mieux l'effort de l'appelant d'une erreur ultérieure plus profonde dans la pile d'appels:

def lje_depth_from_send
  Class.new { def lje; yield end }.new.__send__ :lje
rescue LocalJumpError
  return $!.backtrace.size - caller(0).size
end

def method_missing(sym, *args, &block)
  ...
rescue LocalJumpError
  if !block_given? and ($!.backtrace.size - caller(0).size) == lje_depth_from_send
    raise MyApp::BadInvocation, "duh - you forgot to supply a block"
  end
  raise
end

Fait intéressant, ce calcul de profondeur relative passe de MRI 1.8 à MRI 1.9 - les premières pistes envoyer , les dernières semblent l’omettre en silence (le goto & amp de pera perl) >, peut-être?), par exemple. (Sous 1.9, la trace LJE est moins profonde que la pile caller (0) , car 1.9 compte explicitement le bloc rescue dans un cadre de pile discret ).

Maintenant, cela pourrait ne pas fonctionner avec une technologie autre que l'IRM, mais je doute que l'analyse de la pile d'appels soit portable d'une implémentation à une autre.

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