В чем разница между `try` и` &.` (безопасным навигационным оператором) в Ruby

Вот мой код:

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>

Я думал, что &. является ярлыком для .try, но я думаю, что я ошибся. Может кто-нибудь указать мне в правильном направлении относительно того, чего я не хватает?

Мне кажется, что это не рубин. Виноград может быть? Хотя я не понимаю, как это может быть.

Ответ 1

&. работает как #try! , а не #try.

А вот и описание #try! (из документации):

То же, что и #try, но вызовет исключение NoMethodError, если получатель не равен nil и не реализует проверенный метод.

Таким образом, в основном это избавляет вас от вызова метода для nil, но если объект представлен, он попытается вызвать его метод как обычно.

Цитата #try из документации Rails, поэтому важно подчеркнуть, что Ruby не предоставляет #try; это обеспечивается Rails, или, точнее, ActiveSupport. Оператор безопасной навигации (&.), Однако, является языковой функцией, представленной в Ruby 2.3.0.

Ответ 2

Метод try игнорирует много вещей, он просто дает ему шанс и называет его днем, если что-то не получается.

Параметр условной навигации & будет блокировать вызовы только для объектов nil. Все остальное считается действительным и будет действовать с полными последствиями, включая исключения.

Ответ 3

согласен с @Redithion в комментарии

Оператор безопасной навигации (&.) В Ruby

сценарий

Представьте, что у вас есть account которой есть owner и вы хотите получить address владельца. Если вы хотите быть в безопасности и не рисковать ошибкой Nil, напишите что-то вроде следующего.

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

Это действительно многословно и раздражает печатать. ActiveSupport включает в себя метод try который имеет похожее поведение (но с несколькими ключевыми отличиями, которые будут обсуждаться позже):

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

Он выполняет то же самое - возвращает либо адрес, либо nil если какое-то значение в цепочке равно nil. В первом примере также может возвращаться значение false, если, например, для owner установлено значение false.

С помощью &.

Мы можем переписать предыдущий пример, используя оператор безопасной навигации:

account&.owner&.address

Больше примеров

Давайте сравним все три подхода более подробно.

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

Пока никаких сюрпризов. Что если owner false (вряд ли, но не невозможный в захватывающем мире дерьмового кода)?

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'

Здесь приходит первый сюрприз - &. синтаксис пропускает только nil но распознает ложь! Это не совсем эквивалентно синтаксису s1 && s1.s2 && s1.s2.s3.

Что если владелец присутствует, но не отвечает на 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>'

пример ниже сбивает с толку и nil&.nil? должен вернуть true.

Будьте осторожны при использовании &. оператор и проверка на nil значения. Рассмотрим следующий пример:

nil.nil?
# => true

nil?.nil?
# => false

nil&.nil?
# => nil

Ответ 4

Я приезжаю на вечеринку немного позже, другие ответы касаются того, как это работает, но я хотел добавить что-то, что другие ответы не были затронуты.

Ваш вопрос спрашивает, в чем разница между try и &. в Ruby. Здесь ключевое слово Ruby.

Самое большое отличие состоит в том, что try не существует в Ruby, это метод, предоставляемый Rails. вы можете увидеть это или себя, если вы сделаете что-то подобное в консоли rails:

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

Однако, если вы сделаете то же самое в консоли irb, вы получите:

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

&. является частью стандартной библиотеки Ruby и поэтому доступен в любом проекте Ruby, а не только в Rails.

Ответ 5

согласен с @Redithion в разделе комментариев

Оператор безопасной навигации (&.) В Ruby

Оператор безопасной навигации возвращает nil, если метод вызывается для объекта nil. Перед этим, если метод вызывается для объекта nil, он выдает ошибку, как указано ниже.

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

Зачем нам нужна безопасная навигация?

Много раз мы не получаем объект из-за неправильных входных значений. В этом случае, если мы продолжим вызывать методы, ожидающие, поскольку у нас есть объект, код может прерваться, если объект окажется нулевым объектом.

Чтобы избежать такого случая, введена безопасная навигация. Это гарантирует, что наш код не сломается, даже если объект, для которого вызывается метод, является нулевым объектом. Это следует использовать, когда мы хорошо получаем объект nil, когда вызов метода завершается неудачно.

Примеры

Представьте, что у вас есть учетная запись, у которой есть владелец, и вы хотите получить адрес владельца. Если вы хотите быть в безопасности и не рисковать ошибкой Nil, напишите что-то вроде следующего.

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

Это действительно многословно и раздражает печатать. ActiveSupport включает в себя метод try, который имеет похожее поведение (но с несколькими ключевыми отличиями, которые будут обсуждаться позже):

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

Он выполняет то же самое - возвращает либо адрес, либо ноль, если какое-либо значение в цепочке равно нулю. В первом примере также может возвращаться значение false, если, например, для владельца установлено значение false.

С помощью &.

Мы можем переписать предыдущий пример, используя оператор безопасной навигации:

account&.owner&.address