As every Ruby programmer eventually discovers, calling blocks or procs that contain return
statements can be dangerous as this might exit your current context:
def some_method(&_block)
puts 1
yield
# The following line will never be executed in this example
# as the yield is actually a `yield-and-return`.
puts 3
end
def test
some_method do
puts 2
return
end
end
test
# This prints "1\n2\n" instead of "1\n2\n3\n"
In cases you want to be absolutely sure some of your code runs after you called a block or proc, you can use a begin ... ensure
construct. But since ensure
is also called if there is an exception during yield, it requires a little more work.
I've created a tiny module that deals with this problem in two different ways:
Using
safe_yield
, it is detected whether the yielded block or proc actually returns using thereturn
keyword. If so, it raises an exception.unknown_block = proc do return end ReturnSafeYield.safe_yield(unknown_block) # => Raises a UnexpectedReturnException exception
Using
call_then_yield
, you can call a block and then make sure that a second block is executed, even if the first block contains areturn
statement.unknown_block = proc do return end ReturnSafeYield.call_then_yield(unknown_block) do # => This line is called even though the above block contains a `return`. end
I'm considering to create a quick Gem out of this, or is there any built-in solution to prevent quick return from the nested block which I missed?