如何动态建立拦截救援链?

时间:2013-06-03 13:59:34

标签: ruby metaprogramming

是否可以构建一个block / rescue链,就像这个动态一样,例如来自符号数组[:f1, :f2, :f3, f4]。下面的代码应该模仿我写的静态

  begin
    applyF1()
  rescue
    applyF2()
    begin
      applyF3()
    rescue
      applyF4()
    end
  end

我已经知道如何从符号(object.send(message))发送内置消息,因此applyFx不是问题。

2 个答案:

答案 0 :(得分:1)

不确定你真正期望的行为,因为@sawa,我不认为你同样对待applyF{1..4}

此代码段显示了如何依次评估一些lambda,直到其中一个lambdas返回非假值而不提高任何值。

如果您要成对评估applyF{1..?}F1失败时,请按F2进行恢复,然后尝试使用F3并在失败时按F4进行恢复,等等),你可以让applyF{2,4,..}总是返回nil。

#!/usr/bin/env ruby

blocks = {
  a: ->(){ raise "raise" },
  b: ->(){ raise "also raise" },
  c: ->(){ :finished },
  d: ->(){ "won't be executed" },
}

ret = blocks.reduce(nil) do |has_succeeded, (key, lam)|
  has_succeeded ||=
  begin
    puts "trying #{key.inspect}"
    lam.call # when succeed, the lambda should return a non-false value
  rescue => e
    puts "failed with message=<#{e.message}>. trying next"
    nil
    # no else clause here, so
    # if lam.call raises nothing, memo keep its return value, and prevents calling following {lam}s.
    # if something is raised in lam.call, this block evalutes to nil
  end
end

# will print:
# trying :a
# failed with message=<raise>. trying next
# trying :b
# failed with message=<also raise>. trying next
# trying :c

p ret
# => :finished

参考:How is the value of a begin block determined?

答案 1 :(得分:0)

这是我想到的第一个:

def rescueChain(*args)
  args.each do |f|
    begin
      <... apply f here ...>
      return  # Exits from the entire function.
    rescue
    end
  end
end

我还没有测试过。