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.
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.