What is the difference between `try` and` & .` (a safe navigation operator) in Ruby

Here is my code:

class Order < Grape::Entity expose :id { |order, options| order.id.obfuscate } expose :time_left_to_review do |order, options| byebug order&.time_left_to_review # ERROR end expose :created_at { |order, options| order.last_transition.created_at } end # NoMethodError Exception: undefined method `time_left_to_review' for #<Order:0x007f83b9efc970> 

I thought &. is a shortcut for .try , but I think I was wrong. Can someone point me in the right direction regarding what I am missing?

It seems to me that this is not a ruby. Grapes maybe? Although I do not understand how this can be.

+14
source share
5 answers

&. works like #try! , not #try .

And here is the description of #try! (from the documentation ):

Same as #try, but will throw a NoMethodError exception if the receiver is not nil and does not implement the tested method.

Thus, this basically saves you from calling the method for nil , but if an object is presented, it will try to call its method as usual.

The quote is #try from the Rails documentation, so it’s important to emphasize that Ruby does not provide #try ; this is provided by Rails, or more precisely, ActiveSupport. The safe navigation operator ( &. ), However, is a language feature introduced in Ruby 2.3.0.

+29
source

The try method ignores a lot of things, it just gives it a chance and calls it day if something fails.

The conditional navigation parameter & will block calls only for nil objects. Everything else is considered valid and will operate with full consequences, including exceptions.

+10
source

agree with @Redithion in the comment

Safe Navigation Operator (&.) In Ruby

scenario

Imagine that you have an account which has an owner and you want to get the address owner. If you want to be safe and not risk the Nil error, write something like the following.

 if account && account.owner && account.owner.address ... end 

It is really verbose and annoying to type. ActiveSupport includes a try method that has similar behavior (but with a few key differences that will be discussed later):

 if account.try(:owner).try(:address) ... end 

It does the same - returns either the address or nil if some value in the chain is nil . The first example can also return false if, for example, owner set to false.

Via &.

We can rewrite the previous example using the safe navigation operator :

 account&.owner&.address 

More examples

Let's compare all three approaches in more detail.

 account = Account.new(owner: nil) # account without an owner account.owner.address # => NoMethodError: undefined method 'address' for nil:NilClass account && account.owner && account.owner.address # => nil account.try(:owner).try(:address) # => nil account&.owner&.address # => nil 

So far no surprises. What if owner false (unlikely, but not impossible in the exciting world of shitty code)?

 account = Account.new(owner: false) account.owner.address # => NoMethodError: undefined method 'address' for false:FalseClass ' account && account.owner && account.owner.address # => false account.try(:owner).try(:address) # => nil account&.owner&.address # => undefined method 'address' for false:FalseClass' 

Here comes the first surprise - &. the syntax skips only nil but recognizes false! This is not exactly equivalent to the syntax s1 && s1.s2 && s1.s2.s3 .

What if the owner is present but not responding to address ?

 account = Account.new(owner: Object.new) account.owner.address # => NoMethodError: undefined method 'address' for #<Object:0x00559996b5bde8> account && account.owner && account.owner.address # => NoMethodError: undefined method 'address' for #<Object:0x00559996b5bde8>' account.try(:owner).try(:address) # => nil account&.owner&.address # => NoMethodError: undefined method 'address' for #<Object:0x00559996b5bde8>' 

the example below is confusing and nil&.nil? should return true .

Be careful when using &. operator and checking for nil values. Consider the following example:

 nil.nil? # => true nil?.nil? # => false nil&.nil? # => nil 
+5
source

I arrive at the party a little later, other answers relate to how this works, but I wanted to add something that the other answers were not affected.

Your question asks what is the difference between try and &. in Ruby . Here is the Ruby keyword.

The biggest difference is that try does not exist in Ruby; it is a method provided by Rails. you can see this or yourself if you do something like this in the rails console:

 [1, 2, 3].try(:join, '-') #=> "1-2-3" 

However, if you do the same in the irb console, you will get:

 [1, 2, 3].try(:join, '-') NoMethodError: undefined method `try' for [1, 2, 3]:Array 

&. is part of the Ruby standard library and is therefore available in any Ruby project, and not just in Rails.

+3
source

I agree with @Redithion in the comments section

Safe Navigation Operator (&.) In Ruby

The safe navigation operator returns nil if the method is called for the nil object. Before that, if the method is called for the nil object, it throws an error, as follows.

 $ nil.some_method => NoMethodError: undefined method 'some_method' for nil:NilClass 

Why do we need safe navigation?

Many times we do not receive an object due to incorrect input values. In this case, if we continue to call methods that wait because we have an object, the code may break if the object turns out to be a null object.

To avoid this, safe navigation has been introduced. This ensures that our code does not break even if the object for which the method is called is a null object. This should be used when we get the nil object well, when the method call fails.

Examples

Imagine that you have an account that has an owner and you want to get the address of the owner. If you want to be safe and not risk the Nil error, write something like the following.

 if account && account.owner && account.owner.address ... end 

It is really verbose and annoying to type. ActiveSupport includes a try method that has similar behavior (but with a few key differences that will be discussed later):

 if account.try(:owner).try(:address) ... end 

It does the same thing - it returns either the address or zero if any value in the chain is zero. The first example can also return false if, for example, the owner is set to false.

Via &.

We can rewrite the previous example using the safe navigation operator:

 account&.owner&.address 
0
source

Source: https://habr.com/ru/post/1271122/


All Articles