Comparable and Enumerable
Prerequisites: Modules and mixins
Two built-in modules show the real power of mixins: define one method, include the module, and get dozens of methods for free.
Comparable — define <=>, get comparisons
class Version
include Comparable
attr_reader :parts
def initialize(text)
@parts = text.split(".").map(&:to_i)
end
def <=>(other)
parts <=> other.parts
end
def to_s = parts.join(".")
end
a = Version.new("1.10.0")
b = Version.new("1.9.3")
p a > b
p a == Version.new("1.10.0")
puts [a, b, Version.new("2.0")].sort.join(", ")
puts [a, b].max
p b.between?(Version.new("1.0"), a)
true
true
1.9.3, 1.10.0, 2.0
1.10.0
true
Comparing the text "1.10.0" > "1.9.3" would give the wrong answer
(text compares character by character). Comparing arrays of integers
gives the right one — and Comparable turns that single <=> method
into >, <, ==, between?, clamp, and sorting.
Enumerable — define each, get map, select, and more
class Playlist
include Enumerable
def initialize(*songs)
@songs = songs
end
def each(&block)
@songs.each(&block)
self
end
end
list = Playlist.new("Intro", "Blue", "Anthem", "Outro")
p list.map(&:upcase)
p list.select { |s| s.length > 4 }
p list.sort
p list.include?("Blue")
p list.first(2)
p list.count
["INTRO", "BLUE", "ANTHEM", "OUTRO"]
["Intro", "Anthem", "Outro"]
["Anthem", "Blue", "Intro", "Outro"]
true
["Intro", "Blue"]
4
&block in the parameter list captures the block passed to each as
an object, so it can be handed on to the array's own each. The
Procs and Lambdas lesson explains this in detail.
Common mistakes
- Returning
nilfrom<=>for objects that should be comparable.nilmeans "can't compare", andsortraises an error. - Subclassing
Arrayto make a collection class. IncludingEnumerableand wrapping an array is simpler and safer.