Module: ReturnSafeYield
- Defined in:
- lib/return_safe_yield.rb,
lib/return_safe_yield/version.rb
Defined Under Namespace
Classes: UnexpectedReturnException
Constant Summary collapse
- VERSION =
"0.2.1"
Class Method Summary collapse
-
.call_then_yield(first, *args, &_second) ⇒ Object
Calls the two given blocks (
first, then ‘&_second`), even if the first block contains a return. -
.safe_yield(block, *args, &cb) ⇒ Object
Yields the given block and raises a
UnexpectedReturnExceptionexception if the block contained areturnstatement.
Class Method Details
.call_then_yield(first, *args, &_second) ⇒ Object
Calls the two given blocks (first, then ‘&_second`), even if the first block contains a return. The second block receives the return value of the first block as arguments.
The second block is not called if the first one raises an exception.
Example:
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
# => This line here might not be called however as the `return` statement
# exits the current method context.
You can also pass arguments to the first block:
unknown_block = proc do |arg1, arg2|
end
ReturnSafeYield.call_then_yield(unknown_block, 'arg1 value', 'arg2 value') do
end
The second block receives the first block’s return value as arguments (this does not apply if return is used explicitely):
unknown_block = proc
'return value'
end
ReturnSafeYield.call_then_yield(unknown_block) do |arg1|
arg1 == 'return value' # => true
end
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/return_safe_yield.rb', line 42 def self.call_then_yield(first, *args, &_second) exception = false first_block_result = nil returned = true begin first_block_result = first.call(*args) returned = false return first_block_result rescue Exception exception = true fail ensure unless exception second_block_result = yield(first_block_result) # In this very particular case, using `return` inside of `ensure` # is fine as we're checking if there is an exception. There is no other # way of returning the second block's result otherwise. return second_block_result unless returned end end end |
.safe_yield(block, *args, &cb) ⇒ Object
Yields the given block and raises a UnexpectedReturnException exception if the block contained a return statement. Thus it is safe to assume that yielding a block in this way never jumps out of your surrounding routine.
Note that you cannot pass a block using ‘safe_yield do`, as it does not make sense to check for return statements in code controlled by the caller itself.
Example:
unknown_block = proc do |some_argument|
return
end
ReturnSafeYield.safe_yield(unknown_block, some_argument)
# => Raises a UnexpectedReturnException exception
81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/return_safe_yield.rb', line 81 def self.safe_yield(block, *args, &cb) state = :returned result = block.call(*args, &cb) state = :regular return result rescue Exception state = :exception fail ensure if state == :returned fail UnexpectedReturnException, "Block #{block.inspect} contains a `return` which it is not supposed to." end end |