Comment déterminer l'origine d'un LocalJumpError?
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
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
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.