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.
Not quite what you're looking for? The <
symbol shows up in Heredoc syntax (<<
, <<-
, <<~
), for the Shovel operator (<<
), and in the Spaceship operator (<=>
).
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.