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
Do You Check in Your Rvmrc File
Appending to Rake Db:Seed in Rails and Running It Without Duplicating Data
Removing Child Root Nodes in Rabl
Detect Browser Language in Rails
How to Convert a Ruby String Range to a Range Object
How to Check for Stdin Input in a Ruby Script
Turn Off Verbose SQL/Activerecord for Rails 3.1.1
Nokogiri To_Xml Without Carriage Returns
Failing to Install Nokogiri Gem
Ruby Array of Hash. Group_By and Modify in One Line
Most of My Assets Suddenly Return 404 After a Push to Heroku
Should I Learn MACruby or Rubycocoa
Bundler Puts My Gems in My Project Directory