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.
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 ...
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.
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.
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With