I am trying to understand blocks and yield
and how they work in Ruby.
How is yield
used? Many of the Rails applications I've looked at use yield
in a weird way.
Can someone explain to me or show me where to go to understand them?
I am trying to understand blocks and yield
and how they work in Ruby.
How is yield
used? Many of the Rails applications I've looked at use yield
in a weird way.
Can someone explain to me or show me where to go to understand them?
I sometimes use "yield" like this:
In Ruby, a block is basically a chunk of code that can be passed to and executed by any method. Blocks are always used with methods, which usually feed data to them (as arguments).
Blocks are widely used in Ruby gems (including Rails) and in well-written Ruby code. They are not objects, hence cannot be assigned to variables.
Basic Syntax
A block is a piece of code enclosed by { } or do..end. By convention, the curly brace syntax should be used for single-line blocks and the do..end syntax should be used for multi-line blocks.
Any method can receive a block as an implicit argument. A block is executed by the yield statement within a method. The basic syntax is:
When the yield statement is reached, the meditate method yields control to the block, the code within the block is executed and control is returned to the method, which resumes execution immediately following the yield statement.
When a method contains a yield statement, it is expecting to receive a block at calling time. If a block is not provided, an exception will be thrown once the yield statement is reached. We can make the block optional and avoid an exception from being raised:
It is not possible to pass multiple blocks to a method. Each method can receive only one block.
See more at: http://www.zenruby.info/2016/04/introduction-to-blocks-in-ruby.html
Yield can be used as nameless block to return a value in the method. Consider the following code:
You can create a method "Up" which is assigned one argument. You can now assign this argument to yield which will call and execute an associated block. You can assign the block after the parameter list.
When the Up method calls yield, with an argument, it is passed to the block variable to process the request.
There are two points I want to make about yield here. First, while a lot of answers here talk about different ways to pass a block to a method which uses yield, let's also talk about the control flow. This is especially relevant since you can yield MULTIPLE times to a block. Let's take a look at an example:
When the each method is invoked, it executes line by line. Now when we get to the 3.times block, this block will be invoked 3 times. Each time it invokes yield. That yield is linked to the block associated with the method that called the each method. It is important to notice that each time yield is invoked, it returns control back to the block of the each method in client code. Once the block is finished executing, it returns back to the 3.times block. And this happens 3 times. So that block in client code is invoked on 3 separate occasions since yield is explicitly called 3 separate times.
My second point is about enum_for and yield. enum_for instantiates the Enumerator class and this Enumerator object also responds to yield.
So notice every time we invoke kinds with the external iterator, it will invoke yield only once. The next time we call it, it will invoke the next yield and so on.
There's an interesting tidbit with regards to enum_for. The documentation online states the following:
If you do not specify a symbol as an argument to enum_for, ruby will hook the enumerator to the receiver's each method. Some classes do not have an each method, like the String class.
Thus, in the case of some objects invoked with enum_for, you must be explicit as to what your enumerating method will be.
I found this article to be very useful. In particular, the following example:
which should give the following output:
So essentially each time a call is made to
yield
ruby will run the code in thedo
block or inside{}
. If a parameter is provided toyield
then this will be provided as a parameter to thedo
block.For me, this was the first time that I understood really what the
do
blocks were doing. It is basically a way for the function to give access to internal data structures, be that for iteration or for configuration of the function.So when in rails you write the following:
This will run the
respond_to
function which yields thedo
block with the (internal)format
parameter. You then call the.html
function on this internal variable which in turn yields the code block to run therender
command. Note that.html
will only yield if it is the file format requested. (technicality: these functions actually useblock.call
notyield
as you can see from the source but the functionality is essentially the same, see this question for a discussion.) This provides a way for the function to perform some initialisation then take input from the calling code and then carry on processing if required.Or put another way, it's similar to a function taking an anonymous function as an argument and then calling it in javascript.
Yields, to put it simply, allow the method you create to take and call blocks. The yield keyword specifically is the spot where the 'stuff' in the block will be performed.