Sometimes we have classes that represent collections, for example a class Group
can represent a collection of members
class Group
def initialize(members)
@members = members.dup
end
end
When we want to iterate over the collection’s members we have to expose them through attr_reader
class Group
attr_reader :members
def initialize(members)
@members = members.dup
end
end
group1 = Group.new(["Nick", "Ana", "Petros"])
group1.members.each { |x| p |x| }
The problem with this approach is that the consumers of our collection have to know that we named our variable “members” and use it to iterate. If we ever rename this variable, the consumers of our collection have to rename their call as well. Moreover, we can’t control the order of the enumeration. What can we do to fix that?
We can make our collection an Enumerable, and define an “each” method
class Group
include Enumerable
def initialize(members)
@members = members.dup
end
def iterate_members_alphabeticaly
ite = @members.dup.sort
while k = ite.shift()
yield k
end
end
def iterate_members_by_name_length
ite = @members.dup.sort_by { |x| x.size }
while k = ite.shift()
yield k
end
end
def each(&block)
enum_for(:iterate_members_by_name_length).each(&block)
end
end
group1 = Group.new(["Nick", "Petros", "Ana"])
group1.each {|x| p x }
That way we don’t expose the name of the variable and we can control the order of the enumeration, e.g. in the previous example we can change the :iterate_members_by_name_length
with :iterate_members_alphabeticaly
def each(&block)
enum_for(:iterate_members_by_name_length).each(&block)
end