Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
62: Custom Sortable Classes
If you've ever tried to sort a list of custom objects using .sort, you probably ran into a ArgumentError (comparison of X with Y failed). That happens because Ruby doesn't know if your object is "bigger" or "smaller" than another one. It's not magic; we have to tell it exactly how to make that decision.
How do I actually tell Ruby how to compare two objects of my own class?
The secret is the "spaceship operator": <=>. I call it that because it looks like a UFO. This operator is the foundation of all sorting in Ruby. It expects one of three specific return values: -1 if the left side is smaller, 0 if they're equal, and 1 if the left side is larger.
Let's say we're building a system to manage software versions. If we just use strings, "1.10" would be sorted as smaller than "1.2" because strings are compared alphabetically. That's a disaster for versioning. We need a Version class that understands numbers.
class Version
attr_reader :major, :minor
def initialize(major, minor)
@major = major
@minor = minor
end
def <=>(other)
# First, compare the major versions
comparison = @major <=> other.major
# If major versions are different, we have our answer.
# If they are the same (0), we fall back to comparing the minor versions.
return comparison == 0 ? @minor <=> other.minor : comparison
end
end
Now, if you have two Version objects, you can call v1 <=> v2 and get a meaningful result. But you'll notice something annoying: you still can't use v1 < v2 or v1 >= v2. You'd have to write all those methods yourself... unless you use a shortcut.
Wait, do I really have to write < and > and == for every single class?
Nope. This is where the Comparable module comes in. It's one of my favorite parts of the Ruby standard library. When you include Comparable in your class and define the <=> method, Ruby automatically gives you <, >, <=, >=, and between? for free.
class Version
include Comparable # This is the magic line
attr_reader :major, :minor
def initialize(major, minor)
@major = major
@minor = minor
end
def <=>(other)
comparison = @major <=> other.major
comparison == 0 ? @minor <=> other.minor : comparison
end
end
v1 = Version.new(1, 2)
v2 = Version.new(1, 10)
puts v1 < v2 # true
puts v1 > v2 # false
By including Comparable, your class now "behaves" like a number or a string. Even better, you can now throw a bunch of these objects into an array and call .sort on that array, and it will just work.
What should I do if the other object isn't the same type?
In a real app, you might accidentally try to compare a Version object to a string or nil. If you try to call other.major on a string, your program will crash with a NoMethodError. I've seen this kill production apps more times than I'd like to admit.
The convention in Ruby is that if <=> cannot compare two objects, it should return nil. This tells the calling method (like .sort) that the objects are not comparable, which allows Ruby to throw a descriptive ArgumentError instead of a confusing NoMethodError.
def <=>(other)
return nil unless other.is_a?(Version)
comparison = @major <=> other.major
comparison == 0 ? @minor <=> other.minor : comparison
end
I always recommend adding this type check. It makes your code robust and prevents your app from blowing up just because some weird data crept into your collection.
📋 Practical Task
Build a Sortable Book Class
You are building a library catalog. Create a Book class that allows books to be sorted primarily by their publication_year (older books first) and secondarily by their title (alphabetically) if they were published in the same year.
- The class should include the
Comparablemodule. - Initialize the class with
titleandpublication_year. - Implement the
<=>operator to handle the two-step sorting logic. - Ensure that comparing a
Bookto a non-Bookobject returnsnil.
Test your implementation by creating an array of three books (at least two from the same year) and calling .sort on the array to verify the order.
There are no comments for now.