Skip to Content
Course content

61: The Comparable Module and <=>

Click on the "Edit" button in the top corner of the screen to edit your slide content.

You've probably run into a situation where you have a custom object—say, a Version number for a piece of software—and you need to know if one version is newer than another. When you start building this, your first instinct might be to just override the operators you actually intend to use in your code.

Writing five methods for one job

Imagine we're building a system to track software releases. We have a Version class that holds a major and minor version number. To make them comparable, you might start by defining the "greater than" and "less than" operators manually. It looks something like this:

class Version
  attr_reader :major, :minor

  def initialize(major, minor)
    @major = major
    @minor = minor
  end

  def >(other)
    return true if @major > other.major
    return true if @major == other.major && @minor > other.minor
    false
  end

  def <(other)
    return true if @major < other.major
    return true if @major == other.major && @minor < other.minor
    false
  end
end

This works for a bit, but it's a maintenance nightmare. What happens when you realize you also need >=, <=, and ==? You'll find yourself copy-pasting nearly identical logic five times over. If you change how a version is calculated—say, you add a 'patch' number—you now have five different methods to update. It's tedious, and it's incredibly easy to make a typo in one of those methods that leads to a bug that only appears when checking a specific edge case.

Letting the spaceship do the heavy lifting

Ruby has a much more elegant way to handle this using the <=> operator, which we call the "spaceship operator." I call it that because it looks like a little UFO. Instead of returning a boolean (true or false), the spaceship operator returns an integer: -1 if the left side is smaller, 0 if they're equal, and 1 if the left side is larger.

The real magic happens when you pair this operator with the Comparable module. When you include Comparable in your class, Ruby looks at your <=> method and automatically generates all the other comparison operators for you. You write one method, and you get five or six for free.

class Version
  include Comparable
  attr_reader :major, :minor

  def initialize(major, minor)
    @major = major
    @minor = minor
  end

  def <=>(other)
    # Compare major versions first
    comparison = @major <=> other.major
    
    # If major versions are the same, use the minor version as the tie-breaker
    return comparison == 0 ? @minor <=> other.minor : comparison
  end
end

Notice how much cleaner that is. I'm using the spaceship operator inside the method to compare the integers themselves, because integers already know how to use <=>. I'm essentially delegating the hard work to Ruby's built-in types.

Why this makes sorting just work

Beyond just making < and > work, there's a huge architectural win here: compatibility with the rest of the language. Many of Ruby's core methods, like Array#sort or Enumerable#minmax, rely specifically on the <=> operator to function.

If you use the naive approach of only defining > and <, calling versions.sort will throw a NoMethodError because sort doesn't care about those methods—it only cares about the spaceship. By implementing Comparable, your custom objects now behave exactly like native Ruby numbers or strings. You can throw them into a list, call sort, and it just works without you having to pass a block to explain how to compare them.




📋 Practical Task

Implementing a Product Ranking System

You are building a catalog for an e-commerce site. You need to create a Product class that can be compared based on its "Value Score." The Value Score is calculated by dividing the rating (1-5) by the price. A product is considered "greater" (better value) if it has a higher Value Score.

Your task:

  • Create a Product class with name, price, and rating attributes.
  • Include the Comparable module.
  • Implement the <=> operator to compare products based on their calculated value score (rating / price.to_f).
  • Verify your implementation by creating an array of three different products and using the .sort method to order them from worst value to best value.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.