DEV Community

Alec Grey
Alec Grey

Posted on

Yield

Intro

Of the many things I take for granted as a beginner Rubyist, I think the concept I've taken for granted the most is code blocks, and the keyword yield. I assumed that do and end were just inherent parts of enumerables, conditional statements, and forms.

The truth is, we can take advantage of these code blocks in our own methods, and access them with the keyword yield.

What is yield?

yield is a built-in Ruby keyword. A list of all Ruby keywords can be found here.

When yield is interpreted, it begins the execution of the code-block that directly succeeds the method:

def execute_block
   yield
end

execute_block { puts "Hello" } 
"Hello"
#=> nil

execute_block do
   2 + 2
end 
#=> 4
Enter fullscreen mode Exit fullscreen mode

In the above example, we have a method that simply executes the code block it is given. When the yield keyword is hit, the method is paused, and the block is ran. The result of the block gets returned to the method, and execution can continue.

Now that we've seen the basic idea of blocks & yield, let's look at some cool implementation.

Performance testing

A common use for a custom yield method is when performance testing a method or block of code.

def speedtest
    t1 = Time.now
    result = yield
    t2 = Time.now
    puts t2 - t1
    result
end
Enter fullscreen mode Exit fullscreen mode

This method will take a time-stamp before and after the execution of a block. It will take the difference of those times to find the runtime, then return the result of the code-block.

speedtest { 1 + 1 }
1.0e-06
=> 2

speedtest { [1, 1].reduce(:+) }
3.0e-06
=> 2
Enter fullscreen mode Exit fullscreen mode

Customized Conditional-flow

Let's make our own conditional flow:

def allow_if_true(method, false_message)
    return false_message unless yield
    method
end
Enter fullscreen mode Exit fullscreen mode

With this method, we limit the execution of method, so that it only runs if the given block returns true.

allow_if_true(top_secret_internet_stuff, "Access Denied") do
   session[:username] == "Bill Gates"
end
Enter fullscreen mode Exit fullscreen mode

Now, we can successfully keep safe all of the internet's deep dark secrets. Take that hackers!

What about enumerating?

Fun fact: We can pass parameters to our yield! Let's take a look at how we can use that by making our own each-like enumerator:

def do_stuff_to(arr)
   return "not an array!" unless arr.class == Array
   count = 0
   while count < arr.length
      yield arr[count]
      count += 1
   end
   arr
end
Enter fullscreen mode Exit fullscreen mode

With this simple implementation, we

  1. First assure we have the right data-type with unless
  2. Iterate over the array with a while loop & count
  3. Display the original array at the end.
do_stuff_to([1, 2, 3]) do |i|
   puts i ** 2
end
1
4
9
#=> [1, 2, 3]
Enter fullscreen mode Exit fullscreen mode

Did you notice the |i|? When we pass a value to yield, we denote it's presence by declaring it in pipe-notation in the block. Just like our built-in enumerable methods, we have a parameter for each value passed to the yield.

Closing

To be completely honest, at this point I don't know how useful a custom yield method will be to your day-to-day development. But at the very least, I hope this helps de-mystify this concept to you, and that you'll be more comfortable knowing what goes on when you see these come up.

Cheers!

Top comments (0)