Ruby5 #1 – How does a Ruby block work?

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

Leave a Reply

You must be logged in to post a comment.