从块返回 - 从块内调用的方法?

时间:2010-11-17 14:45:05

标签: ruby

我正在尝试定义一个DSL,其中规则(为了这个例子,规则定义某些东西是“好”还是“坏”)在Ruby中的块中指定。以下是我想要做的(简化)版本:

def test_block
  # Lots of other code
  is_good = yield   # ... should give me true or false
  # Lots of other code
end

test_block do
  good if some_condition
  good if some_other_condition
  bad
end

有没有什么方法可以定义阻止中断的方法goodbad?在上面的例子中,我想:

  • 检查 some_condition 是否为true,如果是,则跳出块并让它返回true
  • 检查 some_other_condition 是否为true,如果是,则跳出块并让它返回true
  • 如果我们还在其中,则无条件地从块中返回false

即。我想让上面的代码表现得好像我已经编写了这样的块:

result = test_block do
  break true if some_condition
  break true if some_other_condition
  break false
end

break置于好/坏方法的定义中显然不起作用。是否有其他方法可以达到我想要的结果,还是我应该考虑一些完全不同的方法来解决这个问题?

1 个答案:

答案 0 :(得分:3)

您可以在块中引发异常并捕获该异常。

module Tester
  class Breaker < Exception; end
  class GoodBreak < Breaker; end
  class BaadBreak < Breaker; end
end

def test_block(name)
  begin
    yield
  rescue Tester::Breaker=>e
    case e
      when Tester::GoodBreak then puts "All is well with #{name}"
      when Tester::BaadBreak then puts "BAD STUFF WITH #{name}"
      else raise
    end
  end
end

def good; raise Tester::GoodBreak; end
def bad;  raise Tester::BaadBreak; end

test_block('early out') do
  good if true
  good if puts("NEVER SEE THIS") || true
  bad
end

test_block('simple pass') do
  good if false
  good if puts("SEE THIS FROM PASS TEST") || true
  bad
end

test_block('final fail') do
  good if false
  good if puts("SEE THIS BUT PUTS IS NIL")
  bad
end

#=> All is well with early out
#=> SEE THIS FROM PASS TEST
#=> All is well with simple pass
#=> SEE THIS BUT PUTS IS NIL
#=> BAD STUFF WITH final fail

这是使用throw/catch(感谢@jleedev!)而不是raise/rescue的另一个示例(更新以传递返回值):

def test_block(name)
  result = catch(:good){ catch(:bad){ yield } }
  puts "Testing #{name} yielded '#{result}'", ""
end

def good; throw :good, :good; end
def bad;  throw :bad,  :bad;  end

test_block('early out') do
  good if true
  good if puts("NEVER SEE THIS") || true
  bad
end

test_block('simple pass') do
  good if false
  good if puts("SEE THIS FROM PASS TEST") || true
  bad
end

test_block('final fail') do
  good if false
  good if puts("SEE THIS BUT PUTS IS NIL")
  bad
end

#=> Testing early out yielded 'good'
#=> 
#=> SEE THIS FROM PASS TEST
#=> Testing simple pass yielded 'good'
#=> 
#=> SEE THIS BUT PUTS IS NIL
#=> Testing final fail yielded 'bad'
相关问题