Keep consistent return types with your interfaces

This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the object-oriented-programming category.

Last Updated: 2024-04-18

If you have an interface in one class like this

# The BundleProduct class
def items
  products # This is an ActiveRecord relation
end

and another interface in a related class like this:

# IndividualProduct
def items
  [] # Regular array
end

Then callers which deal with both these product types will have problems, e.g.

# `items` could be a mixture of BundleProduct and IndividualProduct instances
items.includes(:other_thing) # kaboom!!, due to array not having the `includes` # method, it only being on ActiveRecord::Base

The fix is to use a special ActiveRecord representation for an empty record set.

def items
  self.class.none
end