Introduction
A code block (or simply "block") is an object in the Ruby programming language that holds some Ruby code along with the execution context. The most visually distinctive feature of Ruby is its use of code blocks, which is also one of the features that confuse newcomers from other languages the most. A Ruby code block is essentially an unnamed method.
Most other programming languages have constructs akin to a Ruby code block, such as function pointers in C, function objects in C++, lambdas and list comprehensions in Python, anonymous functions in Perl, and anonymous inner classes in Java. These features are hidden away in those languages' obscure corners, avoided by inexperienced programmers. Without code blocks, Ruby cannot be written. Only Lisp is more block-oriented than the other major languages.
Binding a block argument to a variable in ruby
Problem
You've created a method that accepts a code block, but calling the block with yield is insufficient. To directly manipulate the code block, you must somehow link the code block to a variable. The likelihood is that you must pass it as a code block to another method.
Solution
Place the block variable's name at the end of the list of arguments for your method. Add an ampersand before it, so Ruby will recognize it as a block argument and not a definitive statement.
The block variable will be bound to a Proc object that has been created from an incoming code block. It can be passed to other methods, called directly with the call, or yielded to as if it had never been bound to a variable. The same thing is accomplished by all three of the following techniques:
def repeat(n)
n.times { yield } if block_given?
end
repeat(2) { puts "Hello." }
# Hello.
# Hello.
def repeat(n, &block)
n.times { block.call } if block
end
repeat(2) { puts "Hello." }
# Hello.
# Hello.
def repeat(n, &block)
n.times { yield } if block
end
repeat(2) { puts "Hello." }
# Hello.
# Hello.
Discussion
A method will accept an optional block called foo if its final argument is the string &foo. A block will be made available as a Proc object bound to the variable foo if the caller decides to pass one in. As an optional argument, if no block is passed in, foo will be nil. By doing this, you can check whether you received a block without calling Kernel#block given?
Adding the ampersand at the beginning of the appropriate variable name, you can pass any Proc object as the code block when calling a method. You can perform this action on a Proc object initially supplied to your method as a code block.
Code blocks are accepted by many collection methods, including each, select, and detect. When your methods can bind a block to a variable, it is simple to wrap such methods. In this case, the most effective way locates the most prominent element in a collection that produces an accurate result for the supplied block:
def biggest(collection, &block)
block ? collection.select(&block).max : collection.max
end
array = [1, 2, 3, 4, 5]
biggest(array) { |i| i < 3} # => 2
biggest(array) { |i| i != 5 } # => 4
biggest(array) # => 5
It is also very useful if you have to create a frontend for a method that accepts a block. An incoming code block can be bound to a variable in your wrapper method, which can be passed to the other method as a code block.
This code repeatedly calls a code block, passing in a random number between min and max each time:
def pick_random_numbers(min, max, limit)
limit.times { yield min+rand(max+1) }
end
Pick_random_numbers has a wrapper method in this code. It makes six calls to a code block, each time using a random number between 1 and 49:
def lottery_style_numbers(&block)
pick_random_numbers(1, 49, 6, &block)
end
lottery_style_numbers { |n| puts "Lucky number: #{n}" }
# Lucky number: 20
# Lucky number: 39
# Lucky number: 41
# Lucky number: 10
# Lucky number: 41
# Lucky number: 32
The last argument specified for a method must always be the code block argument. This means that the code block argument comes after the container for the variable arguments if your method accepts a variable number of arguments:
def invoke_on_each(*args, &block)
args.each { |arg| yield arg }
end
invoke_on_each(1, 2, 3, 4) { |x| puts x ** 2 }
# 1
# 4
# 9
# 14