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.
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 onnil
. 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 onnil
. 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 onowner
, which isnil
. An alternative is to do:See also this post.
Adds an “_?” method that lets you say (following your example)
user.photo._?.url
.This is awesome. If I want to say
@person.alive?
in a view, I don’t want to check whether or not@person
is set. Well done.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.
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.Yeah, that’s the rule. But:
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?I think the real problem here is that “safe nils” means “nothing is safe.”
Dog help us all.
This seems a nice alternative to:
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. :)