Why Does Ruby Use Yield

What does the yield keyword do in Ruby?

This is an example fleshing out your sample code:

class MyClass
attr_accessor :items

def initialize(ary=[])
@items = ary
end

def each
@items.each do |item|
yield item
end
end
end

my_class = MyClass.new(%w[a b c d])
my_class.each do |y|
puts y
end
# >> a
# >> b
# >> c
# >> d

each loops over a collection. In this case it's looping over each item in the @items array, initialized/created when I did the new(%w[a b c d]) statement.

yield item in the MyClass.each method passes item to the block attached to my_class.each. The item being yielded is assigned to the local y.

Does that help?

Now, here's a bit more about how each works. Using the same class definition, here's some code:

my_class = MyClass.new(%w[a b c d])

# This points to the `each` Enumerator/method of the @items array in your instance via
# the accessor you defined, not the method "each" you've defined.
my_class_iterator = my_class.items.each # => #<Enumerator: ["a", "b", "c", "d"]:each>

# get the next item on the array
my_class_iterator.next # => "a"

# get the next item on the array
my_class_iterator.next # => "b"

# get the next item on the array
my_class_iterator.next # => "c"

# get the next item on the array
my_class_iterator.next # => "d"

# get the next item on the array
my_class_iterator.next # =>
# ~> -:21:in `next': iteration reached an end (StopIteration)
# ~> from -:21:in `<main>'

Notice that on the last next the iterator fell off the end of the array. This is the potential pitfall for NOT using a block because if you don't know how many elements are in the array you can ask for too many items and get an exception.

Using each with a block will iterate over the @items receiver and stop when it reaches the last item, avoiding the error, and keeping things nice and clean.

Why does Ruby use yield?

Yield Passes Objects to a Method's Block

[Yield is] going to the origin of the call and grabbing a block immediately following the call.

Not really. yield passes an argument to a block; it doesn't "grab a block" or do anything with it. In other words, this:

def foo; yield self; end
foo { |x| x.inspect }
# => "main"

Here, yield isn't doing anything but passing an argument to the block that is passed into the foo method. Every Ruby method supports an optional block—except when a block is actually mandatory—so the only "magic" is that the language allows a block to be passed even when it isn't explicitly declared as part of the method signature.

Further Examples

To see this implicit signature in action, consider this:

def foo; puts block_given?; end
foo { |x| x.inspect }

which will print "true" and return nil, which is the expected return value from the puts method.

Of course, without yield the block doesn't do anything at all. For example:

def foo; end
foo { |x| x.inspect }
# => nil

Blocks and yields in Ruby

Yes, it is a bit puzzling at first.

In Ruby, methods can receive a code block in order to perform arbitrary segments of code.

When a method expects a block, you can invoke it by calling the yield function.

Example:

Take Person, a class with a name attribute and a do_with_name method. When the method is invoked it will pass the name attribute to the block.

class Person 
def initialize( name )
@name = name
end

def do_with_name # expects a block
yield( @name ) # invoke the block and pass the `@name` attribute
end
end

Now you can invoke this method and pass an arbitrary code block.

person = Person.new("Oscar")

# Invoking the method passing a block to print the value
person.do_with_name do |value|
puts "Got: #{value}"
end

Would print:

Got: Oscar

Notice the block receives as a parameter a variable called value. When the code invokes yield it passes as argument the value of @name.

yield( @name )

The same method can be invoked with a different block.

For instance to reverse the name:

reversed_name = ""

# Invoke the method passing a different block
person.do_with_name do |value|
reversed_name = value.reverse
end

puts reversed_name

=> "racsO"

Other more interesting real life examples:

Filter elements in an array:

 days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]  

# Select those which start with 'T'
days.select do | item |
item.match /^T/
end

=> ["Tuesday", "Thursday"]

Or sort by name length:

 days.sort do |x,y|
x.size <=> y.size
end

=> ["Monday", "Friday", "Tuesday", "Thursday", "Wednesday"]

If the block is optional you can use:

yield(value) if block_given?

If is not optional, just invoke it.

You can try these examples on your computer with irb (Interactive Ruby Shell)

Here are all the examples in a copy/paste ready form:

class Person 
def initialize( name )
@name = name
end

def do_with_name # expects a block
yield( @name ) # invoke the block and pass the `@name` attribute
end
end

person = Person.new("Oscar")

# Invoking the method passing a block to print the value
person.do_with_name do |value|
puts "Got: #{value}"
end

reversed_name = ""

# Invoke the method passing a different block
person.do_with_name do |value|
reversed_name = value.reverse
end

puts reversed_name

# Filter elements in an array:
days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]

# Select those which start with 'T'
days.select do | item |
item.match /^T/
end

# Sort by name length:
days.sort do |x,y|
x.size <=> y.size
end

What does “yield” do?

It tells Rails to put your view content to this block (which is called by yield) at that place in the layout file.

Checkout the rails guide to learn more about the ActionView.

https://guides.rubyonrails.org/action_view_overview.html

As pointed out by @Aleksei Matiushkin, yield is pure ruby, so you should also learn more about that in your own time.

Here's a (my) visual presentation to explain what happened on that line:

view.html.erb:

<p>Hello there!</p>
<p>I'm a content from view</p>

layout.html.erb:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
<%= yield %>
</body>
</html>

Now the results will be like this:

<!DOCTYPE html>
<html>
<head>
</head>

<body>
<p>Hello there!</p>
<p>I'm a content from view</p>
</body>
</html>

Python yield vs Ruby yield

In ruby, yield is a shortcut that is used to call an anonymous function. Ruby has a special syntax for passing an anonymous function to a method; the syntax is known as a block. Because the function has no name, you use the name yield to call the function:

def do_stuff(val)
puts "Started executing do_stuff"
yield(val+3)
yield(val+4)
puts "Finshed executing do_stuff"
end

do_stuff(10) {|x| puts x+3} #<= This is a block, which is an anonymous function
#that is passed as an additional argument to the
#method do_stuff

--output:--
Started executing do_stuff
16
17
Finshed executing do_stuff

In python, when you see yield inside a function definition, that means that the function is a generator. A generator is a special type of function that can be stopped mid execution and restarted. Here's an example:

def do_stuff(val):
print("Started execution of do_stuff()")

yield val + 3
print("Line after 'yield val + 3'")
yield val + 4
print("Line after 'yield val + 4'")

print("Finished executing do_stuff()")

my_gen = do_stuff(10)

val = next(my_gen)
print("--received {} from generator".format(val))

output:

Started execution of do_stuff()
--received 13 from generator

More code:

val = next(my_gen)    
print("--received {} from generator".format(val))

output:

Line after 'yield val + 3'
--received 14 from generator

From the output, you can see that yield causes a result to be returned; then execution is immediately halted. When you call next() again on the generator, execution continues until the next yield statement is encountered, which returns a value, then execution halts again.

Why is `yield self` helpful?

The second example works because of the specific circumstances where you have a local variable referring to the the Dog. As an alternative consider what happens if using an anonymous instance:

Dog.new.bark do
puts "Bark, bark, bark!"
# what goes here?? puts ????.breed
end

or maybe you want to declare your block somewhere else and pass it in e.g.

loud = lambda { |dog| puts "A #{dog.breed} doing some LOUD YAPPING" }

puts d.bark(&loud)

so essentially setting up with yield self gives flexibility in how your code can be used.

How does Ruby on Rails use yield for layouts?

Technically, yield is calling a block in this context as well. However, the block is the view your controller action was told to render.

For example, let's say you have a StaticContentController that has an index action on it that represented your home page. With routes configured correctly, you visit your home page. Rails will load the layout file in views/layouts that is appropriate for that controller (application.html.haml, unless you overrode this with a layout for your controller). When it reaches the yield command, it inserts the view at views/static_content/index.html.haml at the location where yield is inside your layout. Then, it loads the rest of your layout file.

Yield variable scope with Ruby

As I understand the code block, it reads, "for each argument, put to screen "My name is #{that argument}"

Nope. Literally, it reads "pass argument 'Eric' to method yield_name and also pass this block". The mere fact that you passed a block to method means nothing. The method can simply just not call it (also known as "yielding to block"). So you can't make any assumptions about number of block invocations or values of supplied parameters without knowing implementation of the method.

In this case, we do know the implementation

def yield_name(name)
puts "In the method! Let's yield..."
yield("Kim")
puts "... now in between the yields!"
yield(name)
puts "Block complete! Back in the method."
end

Here we see that per each call, yield_name calls its block two times. First, with a static value ("Kim"). Second time, it simply relays its parameter name. If there were no yield statements here, the block you supplied would be completely ignored (and, therefore, nothing would be printed). But we do trigger the block two times and see two lines in the output.



Related Topics



Leave a reply



Submit