What is the best explanation for Ruby blocks that you can share?
Both usage and writing code that can take a block?
I offer up my own explanation from this answer, slightly modified:
"Blocks" in Ruby are not the same as the general programming terms "code block" or "block of code".
Pretend for a moment that the following (invalid) Ruby code actually worked:
def add10( n )
puts "#{n} + 10 = #{n+10}"
end
def do_something_with_digits( method )
1.upto(9) do |i|
method(i)
end
end
do_something_with_digits( add10 )
#=> "1 + 10 = 11"
#=> "2 + 10 = 12"
...
#=> "9 + 10 = 19"
While that code is invalid, its intent—passing some code to a method and having that method run the code—is possible in Ruby in a variety of ways. One of those ways is "Blocks".
A Block in Ruby is very, very much like a method: it can take some arguments and run code for those. Whenever you see foo{ |x,y,z| ... }
or foo do |x,y,z| ... end
, those are blocks that take three parameters and run the ...
on them. (You might even see that the upto
method above is being passed a block.)
Because Blocks are a special part of the Ruby syntax, every method is allowed to be passed a block. Whether or not the method uses the block is up to the method. For example:
def say_hi( name )
puts "Hi, #{name}!"
end
say_hi("Mom") do
puts "YOU SUCK!"
end
#=> Hi, Mom!
The method above is passed a block that is ready to issue an insult, but since the method never calls the block, only the nice message is printed. Here's how we call the block from a method:
def say_hi( name )
puts "Hi, #{name}!"
if block_given?
yield( name )
end
end
say_hi("Mridang") do |str|
puts "Your name has #{str.length} letters."
end
#=> Hi, Mridang!
#=> Your name has 7 letters.
We use block_given?
to see whether or not a block was passed along or not. In this case we passed an argument back to the block; it's up to your method to decide what to pass to the block. For example:
def say_hi( name )
puts "Hi, #{name}!"
yield( name, name.reverse ) if block_given?
end
say_hi("Mridang"){ |str1, str2| puts "Is your name #{str1} or #{str2}?" }
#=> Hi, Mridang!
#=> Is your name Mridang or gnadirM?
It's just a convention (and a good one, and one you want to support) for some classes to pass the instance just created to the block.
This is not an exhaustive answer, as it does not cover capturing blocks as arguments, how they handle arity, un-splatting in block parameters, etc. but intends to serve as a Blocks-Are-Lambdas intro.