Using singleton objects as default arguments in Ruby

Using singleton objects as default arguments in Ruby

Sometimes you would like to define a method which takes an optional argument, but the programmer might pass nil. And your code needs to distinguish between the value not being provided (default value) and nil. How can it be achieved?

The usual solution for default value is to define them as nil or other empty/zero values which makes sense such as 0 or a string, an empty array, etc.

class Foo
  def bar(one, two: nil)
    # ...

But what if you need to distinguish between nil and no value being provided? What if you want to distinguish between:, two: nil)


Here is the solution. Define a single, unique object and use it as a default. And instead of checking if the passed argument is nil check if that’s the singleton object or not.

class Foo

  def bar(one, two: NOT_PROVIDED)
    puts one.inspect
    if two == NOT_PROVIDED
      puts "not provided"
      puts two.inspect

  private_constant :NOT_PROVIDED

using private_constant is not necessary but I like to remind Ruby devs that we can use it for ages and that we can have private classes that way as well.
not provided, two: 2)

You could use a symbol (:not_provided) or number or anything else that’s unique in ruby, but in general methods (such as assert_changes described below) they could be valid objects to be provided as an argument. So the best way to solve it, is to use a unique object that nobody can pass as an argument.

Here is how Rails is using it to implement assert_changes:

assert_changes :@object, from: nil, to: :foo do
  @object = :foo

assert_changes -> { object.counter }, from: 0, to: 1 do
def assert_changes(expression, message = nil, from: UNTRACKED, to: UNTRACKED, &block)
  exp = if expression.respond_to?(:call)
   -> { eval(expression.to_s, block.binding) }

  before =
  retval = yield

  unless from == UNTRACKED
    error = "#{expression.inspect} isn't #{from.inspect}"
    error = "#{message}.\n#{error}" if message
    assert from === before, error

  after =

  if to == UNTRACKED
    error = "#{expression.inspect} didn't changed"
    error = "#{message}.\n#{error}" if message
    assert_not_equal before, after, error
    error = "#{expression.inspect} didn't change to #{to}"
    error = "#{message}.\n#{error}" if message
    assert to === after, error


I guess I prefer the rspec approach

expect do
  object.increment change{ object.counter }.from(0).to(1)

but I admire the assert_changes implementation which uses UNTRACKED object.

Although, it’s kind of similar to using boolean arguments, which often is an indicator that 2 separate methods should be defined. So instead of foo(1, true) and foo(1, false), it is often argued it’s better to just have foo(1) and bar(1) and I usually agree with this guideline. However, in case of assert_changes the usage of named arguments and singleton object seems OK to me.

Would you like to continue learning more?

If you enjoyed that story, subscribe to our newsletter. We share our every day struggles and solutions for building maintainable Rails apps which don’t surprise you.

You might enjoy reading:

Want to learn more about building frontend friendly Rails apps?

Imagine you can have your Rails app, even in the legacy state, but have techniques and opinionated ways to improve both your developer’s experience and speed of delivering features? To take your API to a higher level in terms of maintenance and provide user experience improvements? It is possible. You don’t need to spend months to research the topic. We already did it for you :)

Click here to read more!

Frontend-friendly Rails

There is more... check out other books published by us

You might also like