Nil is Unfriendly

In this episode, Ben and Joe discuss why abusing nil is unfriendly to your fellow developers, and dive into some of the other ways of representing nothingness. While watching, you'll learn how nil can be contagious, confuse a domain, and violate ...
This is a companion discussion topic for the original entry at https://thoughtbot.com/upcase/videos/nil-is-unfriendly-with-joe-ferris

Guys, Here is a option.

In My Decorator I have this code.

 def president_name
  self.object.leader.try(:username)
 end

In My Model .

 def leader
  groups.order(level: :desc).limit(1).first.try(:leader)
 end

So , In the scenario as above how would I limit the contagiousness of nil and How would I handle the nil. I do not want to check for nil every step of the way or have to use try as I am doing in above example. Please suggest the best solution to improve the code above.

@PeytonF18 usually when I get into scenarios like this where I might encounter nil on an association, I handle it with a null object.

    class MyDecorator
      def president_name
        leader.username        
      end
    end

    class MyModel < ActiveRecord::Base
      def leader
        first_group.leader_or_no_leader
      end

      def first_group
         groups.order(level: :desc).first_or_no_group
      end  
    end

    class NoLeaderExists
      def username
        "" # could also say "Has No Leader", etc.
      end
   end

   class Group
     def self.first_or_no_group
       first || NoGroup.new
     end

     def leader_or_no_leader
       leader || NoLeaderExists.new
     end
  end

  class NoGroup
    def leader_or_no_leader
      NoLeaderExists.new
    end
  end

``

Despite the new syntax supporting a try-like behavior, using try to rescue from nil is a serious code smell. If you’re using try, it’s time to investigate other approaches for the problem.

This example is a little convoluted because you have two possible places where there could be a nil something (the check for a leader, the check for a group). It might be worth restructuring your templates so that you’re digging less deeply in an individual template (these examples are violating Law of Demeter).

Thanks for the insight @geoffharcourt. Really appreciated. I understand where I am wrong now.
The method leader in my model knows too much .

Hence,

def leader 
  top_group.leader
end 

def top_group
   # here top would be a scope that returns a specific group that is needed 
    self.groups.top
end

And instead of using the null object pattern . Perhaps I can handle it in the decorator. Like following.

 def president_name
   leader.nil? ?  "No leader assigned yet" : leader.username
 end

Although this solves the problem at hand. I would like to discuss more about the advantage of using the null object pattern might offer in my design.

I think the approach with the ternary operator is the same problem as using try, it’s just phrased in a different way. This solution would still check for nil, but it would be a little easier to read:

def president_name
  if leader
     leader.username
  else
    "No leader assigned yet"
  end
end

If your leader method above could return a real leader or a null object leader whose #username method responded with "No leader assigned yet", you could avoid checking on leader’s presence and just write:

def leader
    top_group.leader || NoLeaderAssignedYet.new
end

def president_name
  leader.username
end

Once you start using null objects, you start to see all kinds of opportunities to use them in your code. The hardest transition for me was not calling them NullLeader, but rather naming them after the circumstances they describe. A null object for no user logged in could be Guest.

1 Like

Thanks! that pretty much sums it up for me. Starting using null object pattern from today onwards.

Glad it helped!

There will be times when a single check for nil isn’t worth the cost of making a null object, but as soon as you end up with two occasions in which something being nil bites you, that’s when I turn to a null object.

1 Like