Understanding Blocks (and Yield) in Ruby
One of the unique features of the Ruby programming languages is blocks. I’ve never heard of blocks before I started learning Ruby and it took me a little while to understand their use and purpose. They’re a powerful feature that’s used widely.
Here’s the simplest example of using a block in Ruby. The code below iterates through each item in an array of book titles and prints it.
books = ['To Kill a Mockingbird', '1984', 'The Catcher in the Rye']books.each do |book|
puts book
end# Outputs:
# To Kill a Mockingbird
# 1984
# The Catcher in the Rye
Essentially a block passes a parameter (or parameters) and lets you do something with it.
Blocks
- Start with do
- Accept one or more parameters
- Do something with the parameters
- End with end
Parameters
The parameters are just arguments that you accept. You can name them whatever you like.
In the example above we named our parameter book
but we could have easily named it anything.
books = ['To Kill a Mockingbird', '1984', 'The Catcher in the Rye']books.each do |book_title|
puts book_title
end# Outputs:
# To Kill a Mockingbird
# 1984
# The Catcher in the Rye
Whatever name you accept for you parameter is the variable name you use within your block.
Shorthand Syntax
Ruby also provides a shorthand syntax for blocks which allows you to write simple one-liners.
The same block from above can be written in the following way.
books = ['To Kill a Mockingbird', '1984', 'The Catcher in the Rye']books.each { |book| puts book }# Outputs:
# To Kill a Mockingbird
# 1984
# The Catcher in the Rye
Writing Your Own Blocks
You’re not limited to using the blocks that already exist. You can write your own.
Say you have an initializer method for an ApiClient
class. It’ll set the conventional configuration by default but you want to provide a way to customize the configuration if needed. For example, someone might want to add an additional header that should be present when the ApiClient
makes requests.
If you wanted to use the default you would do the following without passing a block.
client = ApiClient.new
However, what if you wanted to start with the defaults but then additionally customize the configuration? Maybe something like:
api_key = 'neXYWWxojNJxckDJxSLKjW2EurdI84R8ZZucMW89wHXmFOF13udOz'client = ApiClient.new do |config|
config.add_header('X-API-Key', api_key)
end
What would our initialize
method look like?
class ApiClient
# ... def initialize
@config = ApiConfig.new
yield @config if block_given?
self
end #...
end
Notice the yield
part. This is what lets you attach a block to your method. You may pass any number of arguments to the yield
method. These arguments will be the parameters given to the block.
Use the block_given?
to check if a block was given if you want it to be optional.