ruby-on-rails - query - scope rails
¿Los rieles intentan el método de lanzar NoMethodError? (3)
¿Por qué se intenta lanzar un error? ¿Eso no derrota todo el propósito? Tal vez es sólo en la consola?
ruby-1.9.2-p180 :101 > User.first.try(:something)
NoMethodError: undefined method `something'' for #<User:0x000001046ad128>
from /Users/me/.rvm/gems/ruby-1.9.2-p180/gems/activemodel-3.0.10/lib/active_model/attribute_methods.rb:392:in `method_missing''
from /Users/me/.rvm/gems/ruby-1.9.2-p180/gems/activerecord-3.0.10/lib/active_record/attribute_methods.rb:46:in `method_missing''
from (irb):101
from /Users/me/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.10/lib/rails/commands/console.rb:44:in `start''
from /Users/me/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.10/lib/rails/commands/console.rb:8:in `start''
from /Users/me/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.10/lib/rails/commands.rb:23:in `<top (required)>''
from script/rails:6:in `require''
from script/rails:6:in `<main>''
EDITAR:
Gracias chicos, ahora lo entiendo.
¿Hay una manera de hacer lo que quería sin usar respond_to?
, ¿tal que User.try(:something)
devuelve nil
lugar de lanzar el error?
Rieles 3
No entiendes cómo funciona el try
, en el fino manual :
prueba (* a, & b)
Invoca el método identificado por el método de símbolo, pasándole cualquier argumento y / o el bloque especificado, tal como lo hace elObject#send
regular deObject#send
Ruby.Sin embargo, a diferencia de ese método, una excepción
NoMethodError
no se generará y en su lugar se devolveránil
, si el objeto receptor es un objetonil
oNilClass
.
Y la versión de try
que está parcheada en NilClass
:
prueba (* args)
Llamartry
ennil
siempre devuelvenil
.
Así que try
no ignora su intento de llamar a un método inexistente en un objeto, ignora su intento de llamar a un método en nil
y devuelve nil
lugar de generar una excepción. El método de try
es solo una forma fácil de evitar tener que comprobar si hay nil
en cada paso de una cadena de llamadas de método.
Rieles 4
El comportamiento de try
ha cambiado en Rails 4 por lo que ahora:
Invoca el método público cuyo nombre
public_send
como primer argumento, comopublic_send
hacepublic_send
, excepto que si el receptor no responde, la llamada devuelvenil
lugar de generar una excepción.
Así que ahora try
cuidar ambos controles a la vez. Si quieres el comportamiento de Rails 3, ¡hay que try!
:
Igual que
try
, pero generará una excepciónNoMethodError
si la recepción [sic] no esnil
y no implementó [sic] el método probado.
Esto es lo que hace el intento.
Invoca el método identificado por el método de símbolo, pasándole cualquier argumento y / o el bloque especificado, tal como lo hace el envío regular de objetos Ruby. Sin embargo, a diferencia de ese método, una excepción NoMethodError no se generará y en su lugar se devolverá nil, si el objeto receptor es un objeto nil o NilClass.
Entonces, digamos que configura @user
en su controlador pero no lo @user.try(:foo) => nil
entonces @user.try(:foo) => nil
lugar de
@user.foo
NoMethodError: undefined method `foo'' for nil:NilClass
El punto importante aquí es que try es un método de instancia. Tampoco devuelve cero si el objeto que prueba no es nulo .
Sé que esto es viejo, pero puede ayudar a alguien más, porque esto es lo primero que apareció cuando busqué en Google este problema. "Tomé prestado" el código para intentarlo e implementé mi propio método try_method que funciona igual que el intento , excepto que primero verifica si el método existe antes de llamar a enviar . Implementé esto en Object y lo puse en un inicializador, y ahora puedo llamarlo en cualquier objeto.
class Object
# Invokes the method identified by _method_, passing it any
# arguments specified, just like the regular Ruby <tt>Object#send</tt> does.
#
# *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
# if the method does not exist.
#
# This differs from the regular Ruby <tt>Object#try</tt> method which only
# suppresses the +NoMethodError+ exception if the object is Nil
#
# If try_method is called without a method to call, it will yield any given block with the object.
#
# Please also note that +try_method+ is defined on +Object+, therefore it won''t work with
# subclasses of +BasicObject+. For example, using try_method with +SimpleDelegator+ will
# delegate +try_method+ to target instead of calling it on delegator itself.
#
# ==== Examples
#
# Without +try_method+
# @person && @person.respond_to?(:name) && @person.name
# or
# (@person && @person.respond_to?(:name)) ? @person.name : nil
#
# With +try_method+
# @person.try_method(:name)
#
# +try_method+ also accepts arguments and/or a block, for the method it is trying
# Person.try_method(:find, 1)
# @people.try_method(:collect) {|p| p.name}
#
# Without a method argument try_method will yield to the block unless the receiver is nil.
# @person.try_method { |p| "#{p.first_name} #{p.last_name}" }
#--
# +try_method+ behaves like +Object#send+, unless called on +NilClass+ or a class that does not implement _method_.
def try_method(method=nil, *args, &block)
if method == nil && block_given?
yield self
elsif respond_to?(method)
__send__(method, *args, &block)
else
nil
end
end
end
class NilClass
# Calling +try_method+ on +nil+ always returns +nil+.
# It becomes specially helpful when navigating through associations that may return +nil+.
#
# === Examples
#
# nil.try_method(:name) # => nil
#
# Without +try_method+
# @person && @person.respond_to(:children) && [email protected]? && @person.children.respond_to(:first) && @person.children.first.respond_to(:name) && @person.children.first.name
#
# With +try_method+
# @person.try_method(:children).try_method(:first).try_method(:name)
def try_method(*args)
nil
end
end