Reason #145 • May 25th, 2026

Using blocks for cleanup

This week we'll celebrate the block, one of Ruby's most powerful and beloved features!

Blocks are anonymous chunks of code that can be passed to methods and executed in a specific context. They are used for a wide variety of purposes, such as iteration, cleanup, callbacks and custom control flow.

Today we'll look at how blocks can be used for cleanup, i.e. to ensure that resources are properly released after use, even in the face of errors. This is a pattern commonly seen in Ruby's standard library:

Ruby
File.open("example.txt", "w") do |file|
  file.write("Hello, world!")

  # If an error occurs here, the file will still be closed properly
  raise "Something went wrong!"
end

require "net/http"
Net::HTTP.start("example.com") do |http|
  response = http.get("/")

  # If an error occurs here, the connection will still be closed properly
  raise "Something went wrong!"
end
    

To implement this pattern, the method that takes a block uses ensure to guarantee that the cleanup code runs at the end of block execution:

Ruby
def open_file(path, mode)
  file = File.open(path, mode)
  begin
    yield(file)
  ensure
    file.close
  end
end

# NOTE: Only works on hosts with lsof installed (e.g. Linux and macOS)
number_of_open_files_before_block = `lsof -p #{Process.pid} | wc -l`.strip.to_i
puts "Open files before: #{number_of_open_files_before_block}"

begin
  open_file("example.txt", "w") do |file|
    file.write("Hello, world!")

    number_of_open_files_during_block = `lsof -p #{Process.pid} | wc -l`.strip.to_i
    puts "Open files during block: #{number_of_open_files_during_block}"

    raise "Something went wrong!"
  end
rescue
end

number_of_open_files_after_block = `lsof -p #{Process.pid} | wc -l`.strip.to_i
puts "Open files after: #{number_of_open_files_after_block}"
    

History

Blocks have been a part of Ruby since its inception. They were inspired by blocks in Smalltalk and closures in Lisp and other functional languages.

The idea of using blocks together with ensure for cleanup has also been there from the very beginning, with e.g. IO.foreach using it all the way back in Ruby 1.0 in 1995.

The block form of File.open shipped in Ruby 1.2 in 1998.

Net::HTTP including the block form of Net::HTTP.start shipped in Ruby 1.4.4 in 2000.

Reason #146 ?