100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Ruby

Comparable and Enumerable Modules

See how Ruby's Comparable and Enumerable modules let a class gain dozens of methods for free by implementing just one core method each: `<=>` or `each`.

Advanced OOPIntermediate10 min readJul 9, 2026
Analogies

Comparable and Enumerable Modules

Comparable and Enumerable are two of Ruby's most powerful built-in mixins, and they embody a core Ruby design philosophy: implement one small, well-defined method, and inherit a rich, battle-tested API for free. Comparable grants ordering operators (<, >, <=, >=, ==, between?, clamp) to any class that defines <=>. Enumerable grants dozens of traversal, search, and transformation methods (map, select, reduce, sort, find, min, max, include?, and more) to any class that defines each. Both are includable modules — include Comparable or include Enumerable — not superclasses, so they can be mixed into any class hierarchy.

🏏

Cricket analogy: Just as a franchise only needs to register one player database schema to unlock ranking, filtering, and search across an entire league portal, a class only needs to define <=> or each to unlock Ruby's whole toolkit for free.

Comparable: order from a single method

To make a class sortable and comparable, define <=> (the spaceship operator), which must return -1, 0, or 1 depending on whether self is less than, equal to, or greater than the argument (or nil if the objects are not comparable). Once <=> exists and Comparable is included, the class automatically gains <, <=, ==, >=, >, between?, and clamp.

🏏

Cricket analogy: The spaceship operator works like an umpire's single lbw decision, out, not out, or referred, that then automatically determines the scoreboard, the review outcome, and the match result, one -1/0/1 call cascades into everything else.

ruby
class Temperature
  include Comparable
  attr_reader :degrees

  def initialize(degrees)
    @degrees = degrees
  end

  def <=>(other)
    degrees <=> other.degrees
  end
end

freezing = Temperature.new(32)
boiling  = Temperature.new(212)
freezing < boiling               #=> true
[boiling, freezing].sort.map(&:degrees) #=> [32, 212]
freezing.clamp(Temperature.new(50), boiling).degrees #=> 50

Enumerable: iteration from a single method

To make a custom collection class fully enumerable, define an each method that yields elements one at a time, then include Enumerable. This single method unlocks map, select, reject, reduce, sort_by, group_by, min_by, max_by, count, first, take, all?, any?, none?, and dozens more — all implemented in terms of each internally.

🏏

Cricket analogy: A scorer who commits to reading out deliveries one ball at a time (each) lets analysts derive strike rate (map), boundary count (select), and best batting spell (max_by) without the scorer doing that work themselves.

ruby
class Playlist
  include Enumerable

  def initialize(songs)
    @songs = songs
  end

  def each
    return enum_for(:each) unless block_given?
    @songs.each { |song| yield song }
  end
end

playlist = Playlist.new(['Song A', 'Song B', 'Song C'])
playlist.map(&:upcase)          #=> ['SONG A', 'SONG B', 'SONG C']
playlist.select { |s| s.include?('B') } #=> ['Song B']
playlist.count                  #=> 3

This 'one method unlocks many' pattern is a form of the Template Method design pattern: the module defines high-level algorithms (like sort, min_by) in terms of a primitive operation (each or <=>) that subclasses/includers supply. It's a hallmark of Ruby's mixin-driven design, contrasting with languages that require explicitly implementing every interface method (e.g. Java's Comparable<T> still only needs compareTo, but collection interfaces like Iterable require more boilerplate).

Forgetting return enum_for(:each) unless block_given? inside each means calling playlist.each without a block (common when Enumerable methods call each internally to build a lazy Enumerator) will raise an error or behave incorrectly instead of returning an Enumerator.

  • Comparable requires only <=> and grants <, >, <=, >=, ==, between?, and clamp.
  • Enumerable requires only each and grants map, select, reduce, sort_by, min_by, count, and dozens more.
  • Both are mixins included via include ModuleName, usable in any class.
  • <=> must return -1, 0, 1, or nil for incomparable objects.
  • Guard each with return enum_for(:each) unless block_given? to support Enumerator chaining.
  • This pattern reflects Ruby's philosophy of maximizing behavior from a minimal required interface.

Practice what you learned

Was this page helpful?

Topics covered

#Ruby#RubyStudyNotes#Programming#ComparableAndEnumerableModules#Comparable#Enumerable#Modules#Order#StudyNotes#SkillVeris