ruby on rails - que - ¿Hay alguna manera de acceder a los argumentos de método en Ruby?
ruby paso de parametros (10)
Nuevo en Ruby y ROR y me encanta cada día, así que aquí está mi pregunta ya que no tengo idea de cómo googlearlo (y lo he intentado :))
tenemos método
def foo(first_name, last_name, age, sex, is_plumber)
# some code
# error happens here
logger.error "Method has failed, here are all method arguments #{SOMETHING}"
end
Entonces, estoy buscando la forma de que todos los argumentos pasen al método, sin enumerar cada uno. Como esto es Ruby, supongo que hay una manera :) si fuera Java, simplemente los enumeraría :)
La salida sería:
Method has failed, here are all method arguments {"Mario", "Super", 40, true, true}
Esto puede ser útil ...
def foo(x, y)
args(binding)
end
def args(callers_binding)
callers_name = caller[0][/`.*''/][1..-2]
parameters = method(callers_name).parameters
parameters.map { |_, arg_name|
callers_binding.local_variable_get(arg_name)
}
end
Antes de ir más lejos, estás pasando demasiados argumentos a foo. Parece que todos esos argumentos son atributos en un modelo, ¿correcto? Realmente debería estar pasando el objeto en sí. Fin del discurso.
Podría usar un argumento "splat". Pone todo en una matriz. Se vería así:
def foo(*bar)
...
log.error "Error with arguments #{bar.joins('', '')}"
end
Desde Ruby 2.1 puede usar binding.local_variable_get para leer el valor de cualquier variable local, incluidos los parámetros del método (argumentos). Gracias a eso puedes mejorar la respuesta aceptada para evitar mal eval.
def foo(x, y)
method(__method__).parameters.map do |_, name|
binding.local_variable_get(name)
end
end
foo(1, 2) # => 1, 2
En Ruby 1.9.2 y posterior puede usar el método de parameters
en un método para obtener la lista de parámetros para ese método. Esto devolverá una lista de pares que indica el nombre del parámetro y si es necesario.
p.ej
Si lo haces
def foo(x, y)
end
entonces
method(:foo).parameters # => [[:req, :x], [:req, :y]]
Puede usar la variable especial __method__
para obtener el nombre del método actual. Entonces, dentro de un método, los nombres de sus parámetros se pueden obtener a través de
args = method(__method__).parameters.map { |arg| arg[1].to_s }
Luego, podría mostrar el nombre y el valor de cada parámetro con
logger.error "Method failed with " + args.map { |arg| "#{arg} = #{eval arg}" }.join('', '')
Nota: dado que esta respuesta se escribió originalmente, en las versiones actuales de Ruby eval
ya no se puede llamar con un símbolo. Para solucionar esto, se ha agregado un to_s
explícito al to_s
la lista de nombres de parámetros, es decir, parameters.map { |arg| arg[1].to_s }
parameters.map { |arg| arg[1].to_s }
Esta es una pregunta interesante. Tal vez usando local_variables ? Pero debe haber una forma diferente a usar eval. Estoy buscando en Kernel doc
class Test
def method(first, last)
local_variables.each do |var|
puts eval var.to_s
end
end
end
Test.new().method("aaa", 1) # outputs "aaa", 1
Parece que lo que esta pregunta está tratando de lograr podría hacerse con una gema que acabo de publicar, https://github.com/ericbeland/exception_details . Enumerará las variables locales y los vlaues (y las variables de instancia) de las excepciones rescatadas. Puede valer la pena mirar ...
Puede definir una constante como:
ARGS_TO_HASH = "method(__method__).parameters.map { |arg| arg[1].to_s }.map { |arg| { arg.to_sym => eval(arg) } }.reduce Hash.new, :merge"
Y úsalo en tu código como:
args = eval(ARGS_TO_HASH)
another_method_that_takes_the_same_arguments(**args)
Si cambia la firma del método, puede hacer algo como esto:
def foo(*args)
# some code
# error happens here
logger.error "Method has failed, here are all method arguments #{args}"
end
O:
def foo(opts={})
# some code
# error happens here
logger.error "Method has failed, here are all method arguments #{opts.values}"
end
En este caso, args
interpolados u opts.values
será una matriz, pero puede join
si está en coma. Aclamaciones
Si necesita argumentos como Hash, y no quiere contaminar el cuerpo del método con la extracción complicada de parámetros, use esto:
def mymethod(firstarg, kw_arg1:, kw_arg2: :default)
args = MethodArguments.(binding) # All arguments are in `args` hash now
...
end
Solo agrega esta clase a tu proyecto:
class MethodArguments
def self.call(ext_binding)
raise ArgumentError, "Binding expected, #{ext_binding.class.name} given" unless ext_binding.is_a?(Binding)
method_name = ext_binding.eval("__method__")
ext_binding.receiver.method(method_name).parameters.map do |_, name|
[name, ext_binding.local_variable_get(name)]
end.to_h
end
end
Una forma de manejar esto es:
def foo(*args)
first_name, last_name, age, sex, is_plumber = *args
# some code
# error happens here
logger.error "Method has failed, here are all method arguments #{args.inspect}"
end