Every Ruby developer is familiar with nil, and Ruby on
Rails comes with a full complement of tools to handle it:
nil?, present?, try and more.
However, it’s easy to let these tools hide duplication and leak
concerns. If you find yourself checking for nil all over
your codebase, try replacing some of the nil values with
The most_recent_answer_text method asks its
answers association for most_recent answer. It
only wants the text from that answer, but it must first
check to make sure that an answer actually exists to get
text from. It needs to perform this check because
most_recent might return nil:
This call clutters up the method, and returning nil is
contagious: Any method that calls most_recent must also
check for nil. The concept of a missing answer is likely to
come up more than once, as in this example:
Introducing a null object can remove duplication and clutter. But it
can also cause pain and confusion:
As a developer reading a method like
Question#most_recent_answer_text, you may be confused to
find that most_recent_answer returned an instance of
NullAnswer and not Answer.
It’s possible some methods will need to distinguish between
NullAnswers and real Answers. This is common
in views, when special markup is required to denote missing values. In
this case, you’ll need to add explicit present? checks and
define present? to return false on your null
NullAnswer may eventually need to reimplement large
part of the Answer API, leading to potential duplicated code and shotgun surgery, which
is largely what we hoped to solve in the first place.
Don’t introduce a null object until you find yourself swatting enough
nil values to grow annoyed. And make sure the removal of
the nil-handling logic outweighs the drawbacks above.
All checks for nil are a condition, but Ruby provides
many ways to check for nil without using an explicit
if. Watch out for nil conditional checks
disguised behind other syntax. The following are all roughly
# Explicit if with nil?if user.nil?nilelse user.nameend# Implicit nil check through truthy conditionalif user user.nameend# Relies on nil being falseyuser && user.name# Call to tryuser.try(:name)
The canonical reference for writing fantastic Rails applications from authors who have created hundreds.