Yesterday, I introduced a bug into our codebase by calling #titleize
on a string that could possibly come in as nil
:
def route
medication.route_name.titleize
end
This threw an error on production and a coworker fixed this by adding try
:
def route
medication.route_name.try(:titleize)
end
I'm not totally happy with this solution, after being really influenced by Avdi's article on #try
, and I've been trying to come up with something I like better. Here are two possibilities.
Firstly, in Avdi's talk "Confident Code", he talks about asking for the data type you need: if you need a string, ask for a string. So I thought about this:
def route
medication.route_name.to_s.titleize
end
This way, nil
will be converted to empty string, and #titleize
will be called, which will not error. The obvious issue with this is that you're calling #to_s
on something which is only ever an instance of String
or NilClass
.
The only other approach I could think of was this:
def route
medication.route_name.present? medication.route_name.titleize : ''
end
The issue here is the if/else
parading around as prettier code through the use of a ternary operator, and the repetition of medication.route_name
. This could be minified by delegating route_name
to medication
, or some other work around. But there's still the if/else
conditional. And that's more code.
The bigger question here is how to handle nil
values. This method is a pretty typical example of how this comes up. How would you handle it?