Ruby Yield
Passing blocks to methods for flexible code
🎯 What is Yield?
Yield allows methods to accept and execute blocks of code. It makes your methods flexible by letting callers customize behavior without changing the method itself, promoting reusable and elegant code.
# Simple yield example
def greet
puts "Hello!"
yield
puts "Goodbye!"
end
greet { puts "Nice to meet you!" }
Output:
Hello!
Nice to meet you!
Goodbye!
Key Yield Concepts
Basic Yield
Execute a block inside a method
yield
Yield with Args
Pass values to the block
yield(value)
Block Given?
Check if block was provided
block_given?
Multiple Yields
Call block multiple times
3.times { yield }
🔹 Basic Yield Usage
The yield keyword transfers control to the block provided by the caller. When yield executes, Ruby runs the block's code, then returns control back to the method.
# Method with yield
def say_hello
puts "Start"
yield
puts "End"
end
say_hello { puts "Hello from block!" }
Output:
Start
Hello from block!
End
🔹 Yield with Parameters
Pass data from your method to the block using yield with arguments. The block receives these values as parameters, allowing it to work with method data dynamically.
# Yield with arguments
def calculate(a, b)
result = yield(a, b)
puts "Result: #{result}"
end
calculate(5, 3) { |x, y| x + y }
calculate(5, 3) { |x, y| x * y }
Output:
Result: 8
Result: 15
🔹 Checking for Blocks
Use block_given? to make blocks optional in your methods. This prevents errors when no block is provided and allows your method to handle both cases gracefully.
# Optional block
def process_data(data)
puts "Processing: #{data}"
if block_given?
yield(data)
else
puts "No custom processing"
end
end
process_data("Hello")
process_data("Hello") { |d| puts "Custom: #{d.upcase}" }
Output:
Processing: Hello
No custom processing
Processing: Hello
Custom: HELLO
🔹 Multiple Yields
Call yield multiple times within a method to execute the block repeatedly. This is useful for iteration, callbacks, or any scenario where you need to run the same code multiple times.
# Yield multiple times
def repeat(times)
times.times do |i|
yield(i + 1)
end
end
repeat(3) { |num| puts "Iteration #{num}" }
Output:
Iteration 1
Iteration 2
Iteration 3
🔹 Yield with Return Values
Blocks can return values back to the method through yield. The method can capture and use these return values to make decisions or perform calculations based on block results.
# Capture block return value
def transform(value)
result = yield(value)
puts "Original: #{value}, Transformed: #{result}"
end
transform(5) { |n| n * 2 }
transform("hello") { |s| s.upcase }
Output:
Original: 5, Transformed: 10
Original: hello, Transformed: HELLO
🔹 Practical Yield Example
Create a timing method that measures how long a block takes to execute. This demonstrates a real-world use case where yield enables flexible, reusable utility methods.
# Measure execution time
def measure_time
start_time = Time.now
yield
end_time = Time.now
puts "Execution time: #{(end_time - start_time).round(3)} seconds"
end
measure_time do
sum = 0
1000.times { |i| sum += i }
puts "Sum calculated: #{sum}"
end
Output:
Sum calculated: 499500
Execution time: 0.001 seconds
🔹 Array Iteration with Yield
Build custom iteration methods using yield to process array elements. This shows how Ruby's built-in methods like each are implemented and how you can create similar functionality.
# Custom each method
def my_each(array)
i = 0
while i < array.length
yield(array[i])
i += 1
end
end
my_each([1, 2, 3, 4]) { |num| puts "Number: #{num}" }
Output:
Number: 1
Number: 2
Number: 3
Number: 4