Search code examples
ruby

Break out a loop from within a (yielded) block inside the loop


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

In the above example break does not break out of the loop as expected. It only breaks out of the msg code block.

This seems a little odd, but I guess it is based on context, that said, how do I break out of the loop from code which is within a yielded code block?


Solution

  • One way is to use throw/catch. No, not exceptions, Ruby has a separate control-of-flow feature that works a bit like exceptions, without all the overhead (although I must admit I'm not sure that there isn't any overhead in using it):

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

    You can even pass a value as the second argument to throw which will be the result of the catch block.

    A potentially more readable solution would, of course, be to pack the code up in a method and use return in place of break. But that wouldn't be as fun.