#---Block---

In most languages, you can pass a function as argument to another function.
In Ruby, you can do that via a block which is just an anonymous function.

Explaining Ruby blocks via Javascript

Before seeing how to define a function that takes a block as an argument, and how to call such a block within the function, let’s do the same in Javascript first.
It will be easier to understand.

In Javascript, here’s how you define a function that takes another function as one of its arguments:
(You can run this by copy pasting the code into the javascript console of developer tools in your browser)

## <<Beginning of Javascript code>>
function GreetAll(names, fnGreeter) {
  return names.map(fnGreeter)
}

With a list of names like this:

let names = ["Brutha", "Vorbis", "Didactylos", "Nhumrod"]

Here’s how GreetAll will be called:

let result = GreetAll(names, function(name) {
  let hi = (name == "Vorbis") ? "HI" : "hi"
  return `${hi} ${name}`
})
console.log(result)
## ["hi Brutha","HI Vorbis","hi Didactylos","hi Nhumrod"]
## <<End of Javascript code>>

Here’s what GreetAll does:
The passed-in function fnGreeter is called with each name in names, and the results are all collected in a new array and returned.

Now let’s do it in Ruby.

A block passed to a function can be called within the function using the yield keyword.

def greet_all(names)
  names.map { |name| yield(name) }
end

And here’s how you’d call greet_all by passing in a block in addition to a list of names.

names = ["Brutha", "Vorbis", "Didactylos", "Nhumrod"]
result = greet_all(names) do |name|
  hi = (name == 'Vorbis') ? 'HI' : 'hi'
  "#{hi} #{name}"
end
p result
## ["hi Brutha","HI Vorbis","hi Didactylos","hi Nhumrod"]

As mentioned in the beginning, a block is just an anonymous function. All the general properties of a function applies to it. Such as:

  • The last line is the return value of the block
  • You can define it to take multipe arguments
  • If you want to pass a block that takes keyword arguments, optional arguments etc, you can do so.

Another block example

The format_user function returns a formatted string of a user.

def format_user(name, age)
  info = {name: name, age: age}
  if block_given?
    yield(info)
  else
    "User: #{name}, Age: #{age}"
  end
end

If you call it with just the user details, the function will use the default formatter:

name, age = 'Centurion', 100
p format_user(name, age)
## => "User: Centurion, Age: 100"

But if you call it with a block that defines a format, then it will use that formatter:

result = format_user(name, age) do |user|
  "#{user[:name]}'s age is #{user[:age]}"
end
p result
## => "Centurion's age is 100"

Two syntaxes to write blocks

  • The do end thing is used when you have multiple lines of code in the block.
  • The {...} thing is used for single line blocks.

Why to use ruby blocks?

The underlying question is: “Why to define a function that takes another function as an argument?”

As you can see in these examples, the purpose of passing a block (or more generally a function) is that the caller gets to define some aspect of the outcome.

More about blocks

  • If you define a function that uses yield, but call it without passing a block, Ruby won’t raise an error. But you can check if a block is passed with the block_given? function.
  • You can only pass at most one block to a function. If you want to pass in more functions as arguments, you must use lambdas or procs
  • It’s one of the few things in Ruby that’s not an object. You can’t call any method on it.