This tutorial will guide you through the fundamentals of Ruby blocks, including their syntax, use cases, and real-world applications. You’ll learn how to leverage blocks for code reusability, iteration, and functional programming concepts.

Beginner30 minutes

Step 1: Introduction to Blocks

Blocks are a fundamental feature in Ruby that allows you to pass a chunk of code to a method. They are essentially anonymous functions that can be treated as objects and passed around. Blocks are an essential part of Ruby's syntax and are used extensively in the Ruby ecosystem. Code Example: In this example, the block { |num| puts num } is passed to the each method, which iterates over each element in the array and executes the block for each element.
ruby
array = [1, 2, 3, 4, 5]
array.each { |num| puts num }

Practice Exercise

Write a method called `triple` that takes a block as an argument. The method should iterate over the elements of an array and execute the block with each element multiplied by 3.

Show Solution
def triple(arr)
  arr.map { |num| yield(num * 3) }
end

result = triple([1, 2, 3]) { |num| num }
puts result.inspect # Output: [3, 6, 9]

# Explanations:
# 1. The `triple` method takes an array `arr` as an argument.
# 2. Inside the method, we use the `map` method to iterate over the array.
# 3. For each element `num`, we call the `yield` keyword with `num * 3`.
# 4. The block `{ |num| num }` is passed to the `triple` method when it's called.
# 5. The block simply returns the value passed to it (num * 3).
# 6. The `map` method returns a new array with the transformed values.

Step 2: Block Syntax

Blocks in Ruby can be defined using either the `do...end` syntax or the curly braces `{ ... }` syntax. The choice between the two is primarily stylistic, but there are some guidelines: - Use curly braces { ... } for single-line blocks. - Use do...end for multi-line blocks.
# Single-line block
[1, 2, 3].each { |num| puts num }

# Multi-line block
numbers = [1, 2, 3, 4, 5]
numbers.each do |num|
  square = num ** 2
  puts "The square of #{num} is #{square}"
end

Practice Exercise

Write a method called `greet` that takes a block. The method should call the block and pass it the string `"Hello"`. Inside the block, concatenate the string with a name (e.g., `"Hello, Alice"`), and print the result.

Show Solution
def greet(&block)
  name = "Alice"
  block.call("Hello, #{name}")
end

greet { |greeting| puts greeting }
# Output: Hello, Alice

# Explanations:
# 1. The `greet` method takes a block using the `&block` parameter.
# 2. Inside the method, we define a `name` variable.
# 3. We call the block using `block.call` and pass it a string with the greeting and name.
# 4. Inside the block `{ |greeting| puts greeting }`, we print the greeting string.
# 5. The `call` method is used to execute the block and pass it the argument.

Step 3: Blocks and Methods

Blocks are often used in conjunction with methods in Ruby. Methods can take blocks as arguments, and the blocks can be executed within the method using the `yield` keyword. Code Example: In this example, the `greet` method doesn't take any arguments, but it expects a block to be passed to it. Inside the method, the `yield` keyword is used to execute the block and pass it a string argument.
def greet
  yield "Hello"
  yield "Goodbye"
end

greet { |greeting| puts greeting }
# Output:
# Hello
# Goodbye

Practice Exercise

Write a method called apply_operation that takes a block and two numbers as arguments. The method should execute the block with the two numbers and return the result.

Show Solution
def apply_operation(num1, num2, &block)
  block.call(num1, num2)
end

result = apply_operation(3, 4) { |a, b| a + b }
puts result # Output: 7

result = apply_operation(5, 2) { |a, b| a * b }
puts result # Output: 10

# Explanations:
# 1. The `apply_operation` method takes two arguments, `num1` and `num2`, and a block using `&block`.
# 2. Inside the method, we call the block using `block.call` and pass it `num1` and `num2` as arguments.
# 3. When calling the method, we pass a block that performs an operation (e.g., addition or multiplication).
# 4. The result of the block execution is returned by the method and can be stored in a variable.

Step 4: Blocks and Iteration

Blocks are commonly used in Ruby for iterating over collections like arrays and hashes. Ruby provides several built-in methods like `each`, `map`, `select`, and `reduce` that accept blocks for iterating and transforming data. Code Example:
array = [1, 2, 3, 4, 5]

# Iterate over the array
array.each { |num| puts num }

# Transform the array
doubled = array.map { |num| num * 2 }
puts doubled.inspect # Output: [2, 4, 6, 8, 10]

# Filter the array
even_numbers = array.select { |num| num.even? }
puts even_numbers.inspect # Output: [2, 4]

Practice Exercise

Given an array of strings, write a method that takes a block and returns a new array containing only the strings that satisfy the condition defined in the block.

Show Solution
def filter_strings(strings, &block)
  strings.select(&block)
end

words = ["apple", "banana", "cherry", "date", "elderberry"]
result = filter_strings(words) { |word| word.length > 5 }
puts result.inspect # Output: ["banana", "cherry", "elderberry"]

# Explanations:
# 1. The `filter_strings` method takes an array of strings `strings` and a block `&block`.
# 2. Inside the method, we use the `select` method and pass it the block using `&block`.
# 3. The `select` method returns a new array containing only the elements that satisfy the condition in the block.
# 4. When calling `filter_strings`, we pass a block that checks if the string length is greater than 5.
# 5. The method returns a new array with the strings that satisfy the condition in the block.

Step 5: Blocks and Functional Programming

Blocks in Ruby are an essential part of functional programming. They allow you to treat code as data and pass it around as arguments to methods. This enables you to write more modular and reusable code. Code Example: In this example, we define two lambda functions (`addition` and `subtraction`) and pass them as arguments to the `calculate` method. The `calculate` method executes the lambda function with the provided arguments.
def calculate(num1, num2, operation)
  operation.call(num1, num2)
end

addition = lambda { |a, b| a + b }
subtraction = lambda { |a, b| a - b }

result = calculate(5, 3, addition)
puts result # Output: 8

result = calculate(5, 3, subtraction)
puts result # Output: 2

Practice Exercise

Write a method called `apply_operations` that takes an array of numbers and two lambda functions as arguments. The first lambda function should perform an operation on each number in the array, and the second lambda function should perform another operation on the results of the first lambda function. The method should return the final result.

Show Solution
def apply_operations(numbers, operation1, operation2)
  result = numbers.map(&operation1)
  result.map(&operation2)
end

double = lambda { |num| num * 2 }
square = lambda { |num| num ** 2 }

numbers = [1, 2, 3, 4, 5]
result = apply_operations(numbers, double, square)
puts result.inspect # Output: [4, 16, 36, 64, 100]

# Explanations:
# 1. The `apply_operations` method takes an array of numbers `numbers`, and two lambda functions `operation1` and `operation2`.
# 2. Inside the method, we use `map` to apply `operation1` to each number in the array, creating a new array `result`.
# 3. We then use `map` again on the `result` array, applying `operation2` to each element.
# 4. The final result is an array with the numbers transformed by both operations.
# 5. In the example, we define `double` and `square` lambda functions and pass them to `apply_operations`.
# 6. The method first doubles each number, then squares the doubled numbers, returning the final result.

Sign in to take Cornell notes on this lesson — they save automatically and stay with your account.

Sign in

Click to access the login or register cheese