¿Cuál es el equivalente pre-Ruby2.3 al operador de navegación segura (`& .`)?

Las respuestas a cada pregunta que puedo encontrar ( Q1 , Q2 ) con respecto al nuevo operador de navegación segura de Ruby ( &. ) Declaran erróneamente que obj&.foo es equivalente a obj && obj.foo .

Es fácil demostrar que esta equivalencia es incorrecta:

 obj = false obj && obj.foo # => false obj&.foo # => NoMethodError: undefined method `foo' for false:FalseClass 

Además, está el problema de la evaluación múltiple. Reemplazar obj con una expresión que tenga efectos secundarios muestra que los efectos secundarios se duplican solo en la expresión && :

 def inc() @x += 1 end @x = 0 inc && inc.itself # => 2 @x = 0 inc&.itself # => 1 

¿Cuál es el equivalente pre-2.3 más conciso a obj&.foo que evita estos problemas?

¡El operador de navegación segura en Ruby 2.3 funciona casi exactamente igual que el try! Método agregado por ActiveSupport, menos su manejo de bloques.

Una versión simplificada de eso podría verse así:

 class Object def try(method, *args, &block) return nil if self.nil? public_send(method, *args, &block) end end 

Puedes usar esto como

 obj.try(:foo).try(:each){|i| puts i} 

Este método de try implementa varios detalles del operador de navegación segura, incluyendo:

  • Siempre devuelve nil si el receptor es nil , independientemente de si nil realmente implementa el método consultado o no.
  • NoMethodError un NoMethodError si el receptor no nil no admite el método.
  • No traga ninguna excepción en las llamadas de método.

Debido a las diferencias en la semántica del idioma, no puede (completamente) implementar otras características del operador de navegación seguro real, incluyendo:

  • Nuestro método de try siempre evalúa argumentos adicionales, en contraste con el operador de navegación segura. Considera este ejemplo

     nil&.foo(bar()) 

    Aquí, la bar() no se evalúa. Al utilizar nuestro método de try como

     nil.try(:foo, bar()) 

    Siempre llamamos al método de bar primero, independientemente de si más tarde llamamos a foo con él o no.

  • obj&.attr += 1 es una syntax válida en Ruby 2.3.0 que no se puede emular con una sola llamada de método en versiones de idiomas anteriores.

Tenga en cuenta que al implementar realmente este código en producción, debería echar un vistazo a los Refinamientos en lugar de aplicar parches a las clases principales.

Creo que el método más similar que emulan los operadores de recorrido seguro es el método de try Rails. Sin embargo, no exactamente, necesitamos manejar el caso cuando el objeto no es nil pero tampoco responde al método.

Lo que devolverá cero si el método no puede evaluar el método dado.

Podemos reescribir try simplemente por:

 class Object def try(method) if !self.respond_to?(method) && !self.nil? raise NoMethodError, "undefined method #{ method } for #{ self.class }" else begin self.public_send(method) rescue NoMethodError nil end end end end 

Entonces se puede utilizar de la misma manera:

Ruby 2.2 y más bajo:

 a = nil a.try(:foo).try(:bar).try(:baz) # => nil a = false a.try(:foo) # => NoMethodError: undefined method :foo for FalseClass 

Equivalente en Ruby 2.3

 a = nil a&.foo&.bar&.baz # => nil a = false a&.foo # => NoMethodError