Why it's worth to know the NullObject pattern

2017, Mar 31    

From time to time most of the Ruby/Rails developers are facing a situation when an app is trying to call a method on a certain object, which out of nowhere appears in our system as nil, instead of an instance of the desired class. The worst scenario in such situations, assuming that you want to solve this problem somehow, is to take advantage of Ruby’s open classes and achieve a quick win by monkey-patching the NilClass to avoid raising the famous NoMethodError. Even if you’re new in Ruby world, you should know that it’s a really bad practice and do not do that. You can, of course, achieve the goal in a few different ways, and here we’ll be describing one of them. Have you ever heard about the NullObject pattern? Here’s a quick introduction on how to use it.

The problem

I think the most popular version of this problem is when you have a regular User class with some roles and session handling. In some situations, you have to perform a double checking when you want to check if your user is logged in as admin. Given that your app is operating on logged in users a helper method like current_user, you can’t just check if e.g. current_user.is_admin? is true. Why? Because if there is no current_user you’ll get an error

NoMethodError: undefined method `is_admin?' for nil:NilClass

That’s why you have to check two things: current_user && current_user.is_admin?. I bet you can imagine much more examples when you’re somehow checking if an object is nil. Very often the best solution to implement is the NullObject pattern.

The solution

Following the example above, let’s create a GuestUser class*, which implements our is_admin? method, that returns false, of course.

class GuestUser
  def is_admin?
    false
  end
end

Now we have to find the place where our current_user is defined and make a simple condition to return the real user or the GuestUser when needed. For example:

def current_user
  if session[:user_id]
    @current_user ||= User.find(session[:user_id])
  else
    @current_user ||= GuestUser.new
  end
end

And that’s it! You can call current_user.is_admin? without double checking if the user is currently logged in. So clean!

Sum up

Every attempt to directly monkey-patch any core Ruby class (including NilClass), instead of providing an explicit solution of the problem, may lead to more unexpected side effects than benefits. That’s why it’s worth to know the patterns. Patterns help you to keep your code clean and maintainable. Of course - there’s no silver bullet for any code and you should use the patterns you know wisely. And in proper places.

* - We should call it NullUser, but how ridiculously does it sound?!