Ruby Blocks
Anonymous chunks of code passed to methods
🧱 What are Ruby Blocks?
Blocks are anonymous pieces of code enclosed in do...end or curly braces {}. They're passed to methods and executed within them, enabling powerful iteration and custom behavior patterns.
# Block with curly braces
[1, 2, 3].each { |num| puts num }
# Block with do...end
[1, 2, 3].each do |num|
puts num * 2
end
Output:
1
2
3
2
4
6
Key Block Concepts
Syntax
Use {} for one line, do...end for multiple
5.times { puts "Hi" }
5.times do
puts "Hello"
end
Parameters
Blocks can accept parameters
[1, 2].each do |n|
puts n
end
Iteration
Perfect for looping through data
arr.each { |item|
puts item
}
Yield
Methods call blocks with yield
def run
yield
end
🔹 Block Syntax
Ruby offers two ways to write blocks: curly braces for single-line blocks and do...end for multi-line blocks. Both work identically, but convention suggests using braces for short, simple operations.
# Curly braces - single line
3.times { puts "Hello" }
# do...end - multiple lines
3.times do
puts "Hello"
puts "World"
end
# With parameters
[1, 2, 3].each { |num| puts "Number: #{num}" }
# Multi-line with parameters
["apple", "banana"].each do |fruit|
puts "I like #{fruit}"
puts "It's delicious!"
end
Output:
Hello
Hello
Hello
Hello
World
Hello
World
Hello
World
Number: 1
Number: 2
Number: 3
I like apple
It's delicious!
I like banana
It's delicious!
🔹 Common Block Methods
Ruby's enumerable methods use blocks extensively for iteration and transformation. These built-in methods make working with collections intuitive and expressive, reducing the need for traditional loops in most cases.
# each - iterate through items
[1, 2, 3].each { |n| puts n * 2 }
# map - transform each item
doubled = [1, 2, 3].map { |n| n * 2 }
puts doubled.inspect
# select - filter items
evens = [1, 2, 3, 4, 5].select { |n| n.even? }
puts evens.inspect
# times - repeat n times
5.times { |i| puts "Iteration #{i}" }
# upto - count up
1.upto(3) { |n| puts "Count: #{n}" }
Output:
2
4
6
[2, 4, 6]
[2, 4]
Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Count: 1
Count: 2
Count: 3
🔹 Creating Methods with Blocks
You can create your own methods that accept blocks using the yield keyword. This allows you to build custom iteration patterns and reusable code structures that execute caller-provided logic at specific points.
# Simple yield
def greet
puts "Before block"
yield
puts "After block"
end
greet { puts "Inside block!" }
# Yield with parameters
def repeat(n)
n.times { |i| yield(i) }
end
repeat(3) { |num| puts "Number: #{num}" }
# Conditional yield
def maybe_run
yield if block_given?
puts "Method complete"
end
maybe_run { puts "Block executed" }
maybe_run # No block provided
Output:
Before block
Inside block!
After block
Number: 0
Number: 1
Number: 2
Block executed
Method complete
Method complete
🔹 Block Parameters
Blocks can receive multiple parameters from the calling method. These parameters are defined between pipe characters and allow blocks to access iteration values, indices, or any data the method wants to share.
# Single parameter
[10, 20, 30].each { |num| puts num }
# Multiple parameters
hash = { name: "Alice", age: 25 }
hash.each { |key, value| puts "#{key}: #{value}" }
# With index
["a", "b", "c"].each_with_index do |letter, index|
puts "#{index}: #{letter}"
end
# Custom method with multiple yields
def calculate(a, b)
yield(a, b, a + b)
end
calculate(5, 3) { |x, y, sum| puts "#{x} + #{y} = #{sum}" }
Output:
10
20
30
name: Alice
age: 25
0: a
1: b
2: c
5 + 3 = 8
🔹 Practical Block Examples
Blocks shine in real-world scenarios like data processing, file operations, and custom iterations. These examples demonstrate how blocks make Ruby code elegant, readable, and powerful for everyday programming tasks.
# Data transformation
prices = [10, 20, 30]
discounted = prices.map { |price| price * 0.9 }
puts "Discounted: #{discounted.inspect}"
# Filtering data
numbers = [1, 2, 3, 4, 5, 6]
evens = numbers.select { |n| n.even? }
odds = numbers.reject { |n| n.even? }
puts "Evens: #{evens.inspect}"
puts "Odds: #{odds.inspect}"
# Custom iterator
def countdown(from)
from.downto(1) { |n| yield(n) }
puts "Blast off!"
end
countdown(3) { |n| puts n }
# Accumulation
total = [1, 2, 3, 4].reduce(0) { |sum, n| sum + n }
puts "Total: #{total}"
Output:
Discounted: [9.0, 18.0, 27.0]
Evens: [2, 4, 6]
Odds: [1, 3, 5]
3
2
1
Blast off!
Total: 10