ループ内の (生成された) ブロック内からループを抜け出す

概要

jobs.each do |job|
  msg job.name do
    break if stop_all_jobs?
    job.run!
  end
end   

def msg(msg, &block)
  puts 'START ' + msg
  yield
  puts 'END ' + msg
end

上記の例では、break は期待どおりにループから抜け出しません。メッセージコードブロックからのみ抜け出すことができます。

これは少し奇妙に思えますが、コンテキストに基づいていると思います。つまり、生成されたコード ブロック内のコードからループを抜け出すにはどうすればよいでしょうか?

解決策

1 つの方法は、スロー/キャッチを使用することです。いいえ、例外ではありません。Ruby には、すべてのオーバーヘッドなしで、例外と同じように機能する別のフロー制御機能があります (ただし、それを使用する際にオーバーヘッドがないことは認められませんが)。

catch :stop_all_jobs do
  msg job.name do
    throw :stop_all_jobs if stop_all_jobs?
    job.run!
  end
end

catch ブロックの結果となる値を 2 番目の引数として渡してスローすることもできます。

もちろん、より読みやすい解決策となる可能性があるのは、コードをメソッドにまとめて、break の代わりに return を使用することです。しかし、それはそれほど面白くありません。