Safe navigation operator '&.' vs '.try' in Rails
We are back to our
Blog app, and this time we want to get the name of the post's author. The task is really straightforward, and we end up with the following piece of code:
We will get our desired result, only if
post.author.name are defined - otherwise, we will end up with
undefined method error.
If we want to avoid those errors, we can make use of some ifs:
if post if post.author if post.author.name post.author.name end end end
This solution gets the job done, but looks extremely bad.
Fortunately, Ruby provides a safe navigation operator
&. and Rails (or ActiveSupport to be more specific) provides the
try method, that can help avoid
undefined method exceptions without extensive use of if statements.
In this post, we will compare both of those methods, and look for the differences between them.
First, let's see how they behave when everything is defined.
author = Author.new(name: 'Joe') post = Post.new(author: author) post.author.name > "Joe" post&.author&.name > "Joe" post.try(:author).try(:name) > "Joe"
There are no big surprises. When there is no
nil along the chain both
try return the author's name.
Post without an author
post = Post.new post.author.name > NoMethodError (undefined method 'name' for nil:NilClass) post&.author&.name > nil post.try(:author).try(:name) > nil
As expected, both methods return
nil instead of calling the
name method on an empty author.
Let's see how both methods behave when they are used to call chained undefined methods for
nil.author.name > NoMethodError (undefined method 'author' for nil:NilClass) nil&.author&.name > nil nil.try(:author).try(:name) > nil
Just like in the previous example both methods returned
nil instead of calling the
Calling post's undefined method
Now let's get back to post, and see what happens when we try to call an undefined method
post = Post.new post.undefined_method > NoMethodError (undefined method 'undefined_method' for <Post:0x00007fe4ee8f5e50>) post&.undefined_method > NoMethodError (undefined method 'undefined_method' for <Post:0x00007fe4ee8f5e50>) post.try(:undefined_method) > nil
Here is the first difference between the discussed functions.
Safe navigation operator returns
nil only if the receiver is
nil as well - it does not do any validation of called methods.
On the other hand,
try is returning
nil whenever the receiver does not respond to the call.
Calling nil's defined method
Let's go back to the example with nil as the receiver of the first call, but this time we will extend
NilClass, so the called method is actually defined.
class NilClass def name 'called nil name' end end nil.name > "called nil name"
nil respond to the
name method. Let’s see how both methods are handling that:
nil&.name > nil nil.try(:name) > nil
The safe navigation operator behaves as expected - it does not care about the method implementation - it checks only if the receiver is
try is behaving in the same matter - if the receiver is
nil it is not checking if it responds to called method, instead, it returns
nil as well.
Safe navigation operator
&. is provided by Ruby. It returns
nil only if the receiver of the method is
nil as well. It does not check if called method is defined.
Rails provides us with the
try method, which checks if the receiver (which is not
nil) responds to the called method, and if not it returns
nil instead of throwing an exception.