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
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
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
Customized Conditional-flow
Let's make our own conditional flow:
def allow_if_true(method, false_message)
return false_message unless yield
method
end
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
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
With this simple implementation, we
- First assure we have the right data-type with
unless
- Iterate over the array with a
while
loop &count
- 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]
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)