🤯 ARKADEMY.DEV for $45/mo 🤯

The === (case equality) operator in Ruby

The === (case equality) operator in Ruby

Recently I’ve been working on adding more exercises to DevMemo.io about Ruby’s Enumerable module. And I try to balance learning most popular APIs (which you might already know) with some less popular but very useful.

And my attention was captured by Enumerable#grep.

# grep(pattern) → array

# Returns an array of every element in enum
# for which Pattern === element.

#=> [38, 39, 40, 41, 42, 43, 44]

names = %w(
# => %w(William Adam James)

As you can see it works with any class which implements === operator. So I was curious as to which classes implement it and in what way. Let’s see.

Class / Module

mod === obj #→ true or false

=== returns true if obj is an instance of mod or one of mod’s descendants. Of limited use for modules, but can be used to classify objects by class.

Basically implemented as



# => [String, Comparable, Object, Kernel, BasicObject]

String === "text"
# => true

Object === "text"
# => true

Comparable === "text"
# => true

Numeric === "text"
# => false


rxp === str #→ true or false

Basically implemented as:

rxp =~ str >= 0


/^[a-z]*$/ === "HELLO"
#=> false

/^[A-Z]*$/ === "HELLO"
#=> true


rng === obj #→ true or false

Returns true if obj is an element of the range, false otherwise.


(Date.new(2017, 8, 21)..Date.new(2017, 8, 27)) === Date.new(2017, 8, 27)
# => true

(Date.new(2017, 8, 21)..Date.new(2017, 8, 27)) === Date.new(2017, 8, 29)
# => false

("a".."z") === "a"
# => true

("a".."z") === "abc"
# => false


proc === obj # → result_of_proc

Invokes the block with obj as the proc‘s parameter just like #call.


is_today = -> (date) { Date.current === date }

is_today === Date.current
# => true

is_today === Date.tomorrow
# => false

is_today === Date.yesterday
# => false


For most of other objects the behavior of === is the same as ==.

Your class

You can define your own class and it’s own === which might be as complex (or as simple) as you want. And you can use instances of such class as matchers in case..when statements or as arguments to Array#grep.

class State
  def initialize(expected_state)
    @expected_state = expected_state

  def ===(obj)
    obj.state.to_s == @expected_state.to_s

class Order < Struct.new(:id, :state, :customer_name)

p = Order.new(1, "placed",   "Robert")
v = Order.new(2, "verified", "Anne")
s = Order.new(3, "shipped",  "Kate")
orders = [p,v,s]

verified = State.new(:verified)
placed   = State.new(:placed)

verified === p
# => false

# => [#<struct Order id=2, state="verified", customer_name="Anne">]

message = case v
when verified
  "Your order has been verified and is awaiting shippment"
when placed
  "Please wait for verification"
# => "Your order has been verified and is awaiting shippment"

Of course this is only for the cases when you don’t feel that checking those conditions is a responsibility of the tested object and you don’t want to implement it as a method in its class.

Your object

As Ruby allows you to define singleton method which affect only a single object’s behavior, you don’t even need a class.

VERIFIED = Object.new
def VERIFIED.===(obj)
  obj.state.to_s == "verified"

class Order < Struct.new(:id, :state, :customer_name)

VERIFIED === Order.new(1, "placed", "Robert")
# => false

VERIFIED === Order.new(2, "verified", "Rita")
# => true

But frankly, I would rather go with Proc in such case.

VERIFIED = -> (obj){ obj.state.to_s == "verified" }


If you don’t want to forget about Enumerable#grep try DevMemo.io. We’ve been recently working on a Beta version which includes scheduling flashcards repetitions and reminders.

Also, subscribe to our newsletter to receive weekly free Ruby and Rails lessons.

Now, a plug 🔌. Join ARKADEMY.DEV for $45 monthly and get access to our best courses: Rails Architect Masterclass ($799 regular price), Anti-IF course, Blogging for busy programmers, Async Remote, TDD video class, Domain-Driven Rails video course!

You might also like