Skip to content
AgileLogics
6 min read

Blocks

Prerequisites: Variable scope

A block is a chunk of code you pass to a method, which the method can run whenever it likes. You've already used them with each and times. They're the most distinctive feature of Ruby.

Real-world analogy: a block is an instruction slip you hand to someone along with a job — "for each parcel you deliver, text me the address". The courier (the method) decides when to follow your slip.

Syntax

method_name { |parameter| code }

method_name do |parameter|
  code
end

Writing a method that takes a block

Inside a method, yield runs the block that was passed to it:

def twice
  yield
  yield
end

twice { puts "Hi!" }
Hi!
Hi!

yield can pass values into the block, and receive the block's result back:

def with_logging(task)
  puts "Starting #{task}"
  result = yield(task)
  puts "Finished #{task}"
  result
end

value = with_logging("sum") { |name| [1, 2, 3].sum }
p value
Starting sum
Finished sum
6

This "do something before and after" pattern is how Ruby opens files, database transactions, and timers safely — you'll see it with File.open in Level 2.

block_given? checks whether a block was passed, so a method can make the block optional.

The &:method shortcut

When a block only calls one method on its argument, there's a shorthand:

words = ["ruby", "rails"]
p words.map { |w| w.upcase }
p words.map(&:upcase)
["RUBY", "RAILS"]
["RUBY", "RAILS"]

Ruby 3.4 also lets you refer to a one-argument block's parameter as it: words.map { it.upcase }.

Common mistakes

  • Calling yield when no block was passed: no block given (yield) (LocalJumpError). Guard it with block_given?.
  • Putting a { } block on a method call without parentheses around its arguments — puts [1, 2].map { |n| n * 2 } works, but mixing styles in complex calls can bind the block to the wrong method. Add parentheses when in doubt.

Practice

Write a method repeat(n) that runs its block n times, passing the current pass number starting at 1. Use it to print Pass 1 to Pass 3.

Show solution
def repeat(n)
  1.upto(n) { |i| yield i }
end

repeat(3) { |i| puts "Pass #{i}" }