Reading Ruby blocks

· Small Sharp Tools


A block is not an object until you ask for it to be one. That single fact
explains most of the syntax.

Yield

def each_pair
  yield :name, "pulpmesh"
  yield :kind, "generator"
end

each_pair { |key, value| puts "#{key}=#{value}" }

No parameter in the signature, no allocation, and block_given? tells you
whether anyone passed one.

The captured block

def with_retries(attempts, &block)
  tries = 0
  begin
    tries += 1
    block.call(tries)
  rescue StandardError
    retry if tries < attempts
    raise
  end
end

The ampersand converts the block into a Proc so it can be stored, passed on,
or called later. That conversion is the cost.

Passing it through

def each_post(&block)
  posts.each(&block)
end

Capturing only to hand it straight on is the one case where a plain yield
inside posts.each { |post| yield post } is both faster and clearer.

Return, break, next

[1, 2, 3].each do |number|
  next if number.even?
  break number if number > 2
end

Arity is looser than you think

pair = [1, 2]
[pair].each { |a, b| puts "#{a} #{b}" }   # 1 2
[pair].each { |a| puts a.inspect }        # [1, 2]

Blocks destructure. Lambdas do not. If an arity mismatch should be an error,
you wanted a lambda.

ruby blocks
last updated: