Aliou Diallo

Changing perception of objects by overriding the inspect method

While writing my previous post, I realized that while using the attributes API allows us to avoid making mistakes when instantiating our value objects, it might not entirely convey that there’s a limited number of objects that can be instantiated 1.

To this end, I thought about a way to prevent this: let’s pretend that our value objects are constants.

Generating constants

First, let’s take our Ship::Category from the previous post:

class Ship::Category
    shuttle supply_carrier troop_carrier war_ship

  def initialize(category)
    raise "invalid category: #{category.inspect}" unless

    @raw_category = category

  def to_s


  attr_reader :raw_category

Let’s start by defining constants for each of our values. Under the initialize method 2, let’s create the constants using const_set and a bit of metaprogramming:

class Shift::Category
  # ...

  VALID_CATEGORIES.each do |raw_category|
    const_set(raw_category.upcase, new(raw_category))

Calling Ship::Category.constants show us that our constants have correctly been created:

pry(main)> Ship::Category.constants

However, inspecting the constant reveals our trickery:

pry(main)> Ship::Category::SHUTTLE
# => #<Ship::Category:0x00007fbddc853350 @raw_category="shuttle">

So, how do we really pretend that our Ship::Category object is truly a constant ? We can do this by overriding the inspect method:

Overriding inspect

As we can see above, by default, inspect returns the class name, a representation of the memory address of the object and a list of instance variables of the object.

In our case, we want inspect to instead display how the object should be accessed. This means making it look like the constants we’ve created above:

class Shift::Category
  # ...

  def inspect

With this, the value object is now displayed as a constant when inspecting the object or in logs:

# Before we had:
pry(main)> Ship::Category::SHUTTLE
# => #<Ship::Category:0x00007fbddc853350 @raw_category="shuttle">

# Now we have
pry(main)> Ship::Category::SHUTTLE
# => Ship::Category::SHUTTLE

Besides indulging in my whims, there are other interesting reasons to override inspect:

The code examples in this post are also available on GitHub. Thanks to Bachir Çaoui and Stéphanie Chhim for reviewing a draft version of this post.

  1. Of course, we could look at the file defining the constants, but where would be the fun in that? 

  2. Because our constants are set directly in the class, the new method needs to be already defined, hence defining them under the initialize method.