#---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.
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 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"
do end
thing is used when you have multiple
lines of code in the block.{...}
thing is used for single line 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.
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.