如何确定LocalJumpError的来源?

时间:2009-09-18 04:41:50

标签: ruby proxy decorator

我如何能够轻松地以编程方式确定LocalJumpError是否因呼叫者立即无法向方法提供所需的块,或者从该方法的更深层次以及它调用的其他方式而产生?

通过“轻松”,我的意思是我想避免$!.backtrace上的字符串检查/ regexen。适用于1.8和1.9的解决方案也是优选的。

动机:当我在ruby中进行方法调用时,通常是因为我输错了方法(NoMethodError),得到的参数数量错误(ArgumentError)或者忽略了传递必要的块(LocalJumpError)。

对于ruby中的代理或装饰包装器对象,我想区分这些调用者或API错误实现者或环境错误可以引发相同的类错误。例如:

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

2 个答案:

答案 0 :(得分:1)

要确定LocalJumpError是否是由忘记传递块的用户引起的,您需要知道两件事:用户是否提供了块以及该方法是否需要块。第一个很简单:只需检查blk是否为零。然而第二个是不可能的(至少在红宝石中)。

所以我认为解析堆栈跟踪是最好的选择。

答案 1 :(得分:0)

可以检查backtrace相对深度,以尽量将调用者错误与调用堆栈中更深层次的后续错误区分开来:

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

有趣的是,这个相对深度计算从MRI 1.8变为MRI 1.9 - 前者跟踪发送,后者似乎默默地省略它(ala perl的goto &sub,也许?),例如。 (在1.9下,LJE回溯而不是caller(0)堆栈,因为1.9显式地将rescue块计为离散堆栈帧。

现在,这可能不适用于非MRI,但我怀疑解析调用堆栈是否可以从一个实现移植到另一个实现。