safe nils in ruby

Groovy has a feature called safe navigation. It lets you send messages to nil and have them return nil if you’re in a special context. This is neat because it means in Grails views, where you might expect to do:

  ${ if (user.photo != NULL) {user.photo.url} }

You can instead do:

  ${ user.photo?.url }

The special context is explicitly signalled by the ? on the accessor.

ok switching to grails

Nah. What if we could add it to Ruby? Be aware that empty arrays behave this way already, because empty array comprehensions return [], which is safe to keep calling. So we just need:

class NilClass
  def method_missing(*args)
    super unless caller.first =~ /`_run_erb/
  end
end

Now, as long as we’re in a special context (ERb), we can chain our nils.

where should we keep it?

For now environment.rb makes a fine home. If it makes people feel better, I could put it in a plugin, or maybe a Rails patch. I am not using this in production (yet), but I want to hear thoughts.

8 responses

  1. This technique helps get around issues with not following the Law of Demeter. When you reach too far across associations in Rails, or even access an attribute on a belongs_to, you run the risk of exceptions due to calling it on nil. This seems to be even worse in views because of our need to display data across associations.

    Seems like a neat fix, but I’d expect some objections to it, stating that it’s better for you to know when it’s calling it on nil and to add a condition there. I’d say it would be better to just let it pass through without error but to have it log when it calls something on nil. Then at least both parties are somewhat satisfied in the matter.

    Say you have a Car, and Car belongs_to owner, unless it’s still on the lot. Now when displaying the owner’s name you’d be tempted to go @car.owner.full_name but if it’s still on the lot you’d get an exception when calling full name on owner, which is nil. An alternative is to do:

    class Car
    
    That way it still looks the same at a glance:  @car.owner.full_name vs. @car.owner_full_name.   The issue with this is that given a sufficiently large application, you  end up with a lot of this going on and it's more code to maintain.
    
    You can read up on the Law of Demeter for more pros/cons.
  2. I guess in Chris’s example, to avoid the Demeter violation, you would have to have a person_alive? method in the controller, and that seems dumb.

    The interfaces I work with don’t change quickly enough to make avoiding shallow Demeter violations worthwhile. If I was frequently traversing 4 or 5 levels, then maybe it could be an issue.

  3. Well, @person.alive? doesn’t violate the Law of Demeter, anyways. Rule of thumb for Demeter in Ruby would be that you only have at most one period.

    Doesn’t violate: @person.alive?

    Violates: @person.car.running?

    I’d agree that making a person_alive? method would be pretty silly, though. You should really know in a view if @person is set.

  4. Yeah, that’s the rule. But:

    @car = @person.car
    @car.running?
    

    To me, the Law of Demeter is about keeping your stable interfaces separated from your unstable interfaces, which seems orthogonal to the implementation. If I have a contract that car.running? will always work, why not use it?

  5. This seems a nice alternative to:

     "@person.car.running if @person.car" 

    which is the sort of thing I find myself writing in views a lot of the time. Both ways avoid the fact that my controllers/models are probably a bit broken if I have to worry about this happening, though. :)