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.
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 .
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
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
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.