← Back to Ruby Operators

Left Angle Bracket

The main two places we see the left angle bracket symbol show up (by itself) in Ruby are for a less than comparison of two objects and for defining an inheritance relationship.

Less Than Comparison

We can compare two objects with < (or any of the other comparison operators <=, >, !=, etc.).

> 33 < 66
=> true
> 66 < 33
=> false
> 'A' < 'a'
=> true
> 'z' < 'a'
=> false

It seems obvious that we can compare two integers to one another. Perhaps it is less clear how exactly we go about comparing two strings. The way this works is that the String class (and any other class that wants its objects to be orderable) includes the Comparable mixin and defines the Spaceship operator method. The spaceship operator, given two instances of a class, has to respond with -1, 0, or 1 to indicate how they are ordered relative to one another. In the case of <, it is true when the <=> operator returns a -1.

We can compare all sorts of objects in useful ways as long as they do that small bit of setup.

> require 'date'
=> true
> one = DateTime.now
=> #<DateTime: 2024-12-11T10:29:59-06:00 ((2460656j,59399s,890014000n),-21600s,2299161j)>
> # wait a moment
=> nil
> two = DateTime.now
=> #<DateTime: 2024-12-11T10:30:16-06:00 ((2460656j,59416s,585114000n),-21600s,2299161j)>
> one < two
=> true

Here is an example where the default string comparison does not always give us the result we want. Comparing two version strings can give the correct result in a lot of cases, but because we are dealing with base-10 numbers in string format, it can quickly betray us. A custom class comparison, like the one provided by Gem::Version, is a great way to deal with this.

> "0.2.3" < "1.2.3"
=> true # ✓
> "10.0.1" < "2.3.4"
=> true # ✗
> Gem::Version.new("10.0.1") < Gem::Version.new("2.3.4")
=> false # ✓

Inheritance

Ruby is an object-oriented programming language. That means that inheritance is often an essential part of how we use it build software. When we define superclass-subclass (or parent-child) relationships, we are able to share behavior, reduce boilerplate, enforce requirements, and model real-world relationships.

We declare that a class (child) inherits from another class (parent) with the < symbol like so:

class Parent
  FORMAT = "%B %d, %Y"

  def print_now(format = nil)
    puts now(format)
  end

  def now(format = nil)
    Time.now.strftime(format || FORMAT)
  end
end

class Child < Parent
  def now(format = nil)
    "~> #{super(format || FORMAT)} <~"
  end
end

Parent.new.print_now
#=> December 11, 2024

Child.new.print_now
#=> ~> December 11, 2024 <~

This contrived example demonstrates a couple different things we get from an inheritance relationship. The subclass can reference constants defuned up the inheritance chain. A child can override a method defined by a parent and the child can call super to invoke the parent version of that method. The child inherits the parent's methods which is why we can call #print_now on an instance of the child class.

We see inheritance in real-world Ruby and Rails code all the time.

class ApplicationRecord < ActiveRecord::Base
  primary_abstract_class
end

class Book < ApplicationRecord
  # ...
end

Note: This is not intended to be a comprehensive introduction to inheritance in Ruby.

References