Ruby Nil Value Objects
Tags: ruby, oop, value, nil
Published: 2019-01-22 15:04:35

---
title: Ruby Nil Value Objects
published: true
description: Implementing an efficient Nil Object pattern with Ruby value objects
tags: ruby, oop, value, nil
series: Getting the Most Out of Ruby Value Objects
---

# TL;DR

A value object can be much more concisely defined if you dedicate a different class to handling null values. The null value class can be a Ruby singleton, so only one instance is ever instantiated, saving memory.

# Problem

So value objects in Ruby are great.

But not everything has a value all of the time. Nils are everywhere, and how do you efficiently handle them?

If you get it wrong then you're going to be staring at this sort of thing:

```ruby
class ISBN
  def initialize(input)
    @isbn_string = input
  end

  def gs1_prefix
    if isbn_string.nil?
      nil
    else
      isbn_string[0..2]
    end
  end

  def checksum_number
    if isbn_string.nil?
      nil
    else
      isbn_string[-1]
    end
  end

  def valid?
    if isbn_string.nil?
      false
    else
      etc
    end
  end

  delegate :nil?, to: :isbn_string
end
```
... or maybe you rely on empty strings giving you the right behaviour throughout ...

```ruby
class ISBN
  def initialize(input)
    @isbn_string = input.to_s
  end

  def gs1_prefix
    isbn_string[0..2]
  end

  def checksum_number
    isbn_string[-1]
  end

  def valid?
    etc
  end

  def nil?
    isbn_string.empty?
  end
end
```

An empty string is not a nil though, so this is a convenience that is also a little bit wrong.

# A Solution

One solution to consider is an explicit “nil object” approach, which provides a response to everything that an instance of ISBN responds to, with the appropriate value for an ISBN that has no specified value or is missing.

As a bonus you only ever need one instance of this object, so you can use a Ruby singleton to represent it.

```ruby
class ISBN
  class NilObject
    include Singleton
    
    def gs1_prefix
      nil
    end

    def checksum
      nil
    end

    def valid?
      false
    end

    def nil?
      true
    end
  end
end
```
... and your ISBN object's instance methods can be simplified to:

```ruby
class ISBN
  def initialize(input)
    @isbn_string = input
  end

  def gs1_prefix
    isbn_string[0..2]
  end

  def checksum_number
    isbn_string[-1]
  end

  def valid?
    etc
  end

  def nil?
    false
  end
end
```

How to invoke this object? This works nicely ...

```ruby
class ISBN
  def self.new(input)
    if input.nil?
      NilObject.instance 
    else
      super(input)
    end
  end

  def initialize(input)
    @isbn_string = input
  end

  etc
end
```

Yeah that's right, we overrode the `new` class method to return a different class. That's problematic if you enjoy type checking, but I don't think you should – you should perhaps care more that whatever is returned responds correctly.

# Further thoughts

If that is not your style, see the initialisation methods considered [here](https://dev.to/databasesponge/flexible-ruby-value-object-inits-3i3j) and consider:

```ruby
module ISBNInitializerExtensions
  refine String do
    def to_isbn
      ISBN.new(gsub(/[^[:digit:]]/,""))
    end
  end

  refine NilClass do
    def to_isbn
      ISBN::NilObject.instance
    end
  end
end
```

This lets `nil.to_isbn` return a concisely defined singleton that has just the appropriate behaviour.

And why the `#nil?` method? You're going to need that for validations ... another post later.

Make sure you have code coverage of the nil object class, and that you have tested that it responds to everything it needs to respond to.

And lastly, other specialised object types can also be defined, so you might have `ISBN`, `ISBN::NilObject`, and `ISBN::Invalid` for complex cases where the behaviour of an object initialised with an invalid value differs from that of a valid value.