Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does the "yield" keyword do in Ruby?

I encountered the following Ruby code:

class MyClass     attr_accessor :items     ...     def each         @items.each{|item| yield item}     end     ... end 

What does the each method do? In particular, I don't understand what yield does.

like image 205
Misha Moroshko Avatar asked Dec 01 '10 06:12

Misha Moroshko


People also ask

How does yield work in Ruby on Rails?

yield is a keyword in Ruby which allow the developer to pass some argument to block from the yield, the number of the argument passed to the block has no limitations, the main advantage of using yield in Ruby, if we face any situation we wanted to our method perform different functions according to calling block, which ...

What is the use of yield in rails?

We can also pass multiple arguments in a block. If we want to call a block multiple times, we can pass it to our method by sending a proc or lamda. But yield is more convenient and quicker in Rails 6 for the same task. Note: yield allows us to inject blocks into a function at any point.

What is do keyword in Ruby?

The do keyword comes into play when defining an argument for a multi-line Ruby block. Ruby blocks are anonymous functions that are enclosed in a do-end statement or curly braces {} . Usually, they are enclosed in a do-end statement if the block spans through multiple lines and {} if it's a single line block.

What is &Block in Ruby?

The &block is a way of sending a piece of Ruby code in to a method and then evaluating that code in the scope of that method. In your example code above it means a partial named cart will be rendered in a div.


2 Answers

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.

like image 53
the Tin Man Avatar answered Oct 12 '22 20:10

the Tin Man


When you write a method that takes a block, you can use the yield keyword to execute the block.

As an example, each could have been implemented in the Array class like this:

class Array   def each     i = 0     while i < self.size       yield( self[i] )       i = i + 1     end   end end 

MyClass#each takes a block. It executes that block once for each item in the instance's items array, passing the current item as an argument.

It might be used like this:

instance = MyClass.new instance.items = [1, 2, 3, 4, 5] instance.each do |item|   puts item end 
like image 23
cpm Avatar answered Oct 12 '22 21:10

cpm