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