Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best practice for handling nil objects and properties?

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?

like image 407
Dave Sanders Avatar asked Oct 19 '13 18:10

Dave Sanders


People also ask

How do you handle nil in Ruby?

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.

What is a nil object?

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.


1 Answers

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 NoMethodErrors 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!
like image 126
carols10cents Avatar answered Sep 21 '22 12:09

carols10cents