Say I have a User
object, which has an email
property, and I need the upper cased last letter of their email
:
u = User.find(1)
letter = u.email.upcase.last
If u
or email
is nil
in this chain, then I get a NoMethodError: undefined method 'blah' for nil:Nilclass
. I should be able to work around it in most cases, but sometimes, a nil
gets where it shouldn't or its hard to contain. One way would be verbose:
u = User.find(1)
letter = nil
if u && u.email
letter = u.email.upcase.last
end
But this gets annoying and hazardous in a view, or in a long chain of a.bunch.of.properties.down.a.hierarchy
. I read about try
method in Rails:
u = User.find(1)
letter = u.try(:email).try(:upcase).try(:last)
This is less verbose, but I feel icky writing all those tries. And once I put try
in the chain, I have to use them all the way down. Is there a better way?
However, in terms of how it's implemented, nil is fundamentally different than in other languages. In Ruby, nil is—you've guessed it—an object. It's the single instance of the NilClass class. Since nil in Ruby is just an object like virtually anything else, this means that handling it is not a special case.
Well, nil is a special Ruby object used to represent an “empty” or “default” value. It's also a “falsy” value, meaning that it behaves like false when used in a conditional statement. Now: There is ONLY one nil object, with an object_id of 4 (or 8 in 64-bit Ruby), this is part of why nil is special.
I like to use the Null Object Pattern. Avdi has a great post explaining this, but the basic idea is you have a little class that can stand in for an object and respond reasonably to the messages you might pass the original object. I've found these are useful not only for avoiding NoMethodError
s but also for setting default values/nice messages.
For instance, you could do:
class NilUser
def email
"(no email address)"
end
end
u = User.find(1) || NilUser.new
u.email.upcase.last # => No errors!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With