Ruby implements Blocks, Procs and lambdas which are referred to as closures in the computer science community.
If you beginning to learn Ruby you will quickly run into code that looks like this.
a = ["dog", "cat", "bird"]
a.alter_each! do |n, i|
"#{i}_#{n}"
end
So whats going on here?
We start off with an array of animal names and call the alter_each! method passing a block. In this block of code we can specify how we want to alter each item. Our example will prefix each animal name with it's position in the array. As the alter_each! method iterates through each item it will execute our block passing the value and index. Our block captures these params, prefixes the index to the name and returns the result.
Now lets look at the alter_each! method.
Notice the method doesn't specify any params, that's because a block is automatically assigned to yield keyword. yield is called like a function passing in the value and index of each item in the array and overriding the original value.
class Array
def alter_each!
self.each_with_index do |n, i|
self[i] = yield(n,i)
end
end
end
What if you need to pass a param to this method?
You can modify the method signature to accept params and finally catch the block with a param starting with an ampersand. In the example below our block will be captured with the &block param which we will invoke the call method. This is in place of using yield
class Array
def modify_each!(add_one = true, &block)
self.each_with_index do |n, i|
j = (add_one) ? (i + 1) : i
self[i] = block.call(n,j)
end
end
end
Here is a full example that will prefix the index with the yield method and append the index with the call method.
class Array
def alter_each!
self.each_with_index do |n, i|
self[i] = yield(n,i)
end
end
def modify_each!(add_one = true, &block)
self.each_with_index do |n, i|
j = (add_one) ? (i + 1) : i
self[i] = block.call(n,j)
end
end
end
a = ["dog", "cat", "cow"]
a.alter_each! do |n, i|
"#{i}_#{n}"
end
a.modify_each! false do |n,i|
"#{n}_#{i}"
end
puts a
For blocks in the wild check out the ActiveRecord save method.
def save(perform_validation = true, &block)
return false if perform_validation && block_given? && authenticate_with_sorenson? && !authenticate_with_sorenson
result = super
yield(result) if block_given?
result
end
References:
Understanding Ruby Blocks, Procs and lambdas