Есть ли способ доступа к аргументам метода в Ruby?
Новый для Ruby и ROR и любящий его каждый день, так что вот мой вопрос, так как я не знаю, как это сделать (и я пробовал:))
у нас есть метод
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
Так что я ищу способ получить все аргументы, переданные методу, без перечисления каждого из них. Поскольку это Ruby, я предполагаю, что есть способ:), если бы это была java, я бы просто перечислил их:)
Вывод:
Method has failed, here are all method arguments {"Mario", "Super", 40, true, true}
Ответы
Ответ 1
В Ruby 1.9.2 и более поздних версиях вы можете использовать метод parameters
для метода, чтобы получить список параметров для этого метода. Это вернет список пар, указывающий имя параметра и требуемый ли он.
например.
Если вы делаете
def foo(x, y)
end
затем
method(:foo).parameters # => [[:req, :x], [:req, :y]]
Вы можете использовать специальную переменную __method__
для получения имени текущего метода. Таким образом, в рамках метода имена его параметров могут быть получены с помощью
args = method(__method__).parameters.map { |arg| arg[1].to_s }
Затем вы можете отобразить имя и значение каждого параметра с помощью
logger.error "Method failed with " + args.map { |arg| "#{arg} = #{eval arg}" }.join(', ')
Примечание:, поскольку этот ответ был первоначально написан, в текущих версиях Ruby eval
больше нельзя вызывать с символом. Чтобы решить эту проблему, при создании списка имен параметров добавлен явный to_s
, т.е. parameters.map { |arg| arg[1].to_s }
Ответ 2
С Ruby 2.1 вы можете использовать binding.local_variable_get для чтения значения любой локальной переменной, включая параметры метода (аргументы). Благодаря этому вы можете улучшить принятый ответ, чтобы избежать evil eval.
def foo(x, y)
method(__method__).parameters.map do |_, name|
binding.local_variable_get(name)
end
end
foo(1, 2) # => 1, 2
Ответ 3
Один из способов справиться с этим:
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
Ответ 4
Это интересный вопрос. Возможно, используя local_variables? Но должен быть способ, кроме использования eval. Я смотрю в ядре 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
Ответ 5
Это может быть полезно...
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
Ответ 6
Прежде чем идти дальше, вы передаете слишком много аргументов в foo. Похоже, что все эти аргументы являются атрибутами модели, правильно? Вы действительно должны проходить сам объект. Конец речи.
Вы можете использовать аргумент "splat". Это заставляет все в массив. Это будет выглядеть так:
def foo(*bar)
...
log.error "Error with arguments #{bar.joins(', ')}"
end
Ответ 7
Если вы измените подпись метода, вы можете сделать что-то вроде этого:
def foo(*args)
# some code
# error happens here
logger.error "Method has failed, here are all method arguments #{args}"
end
Или:
def foo(opts={})
# some code
# error happens here
logger.error "Method has failed, here are all method arguments #{opts.values}"
end
В этом случае интерполированный args
или opts.values
будет массивом, но вы можете join
, если в запятой. Приветствия
Ответ 8
Кажется, что этот вопрос пытается выполнить, можно сделать с помощью только что выпущенного драгоценного камня, https://github.com/ericbeland/exception_details. Он будет отображать локальные переменные и vlaues (и переменные экземпляра) из освобожденных исключений. Возможно стоит посмотреть...
Ответ 9
Вы можете определить константу, такую как:
ARGS_TO_HASH = "method(__method__).parameters.map { |arg| arg[1].to_s }.map { |arg| { arg.to_sym => eval(arg) } }.reduce Hash.new, :merge"
И используйте его в своем коде, например:
args = eval(ARGS_TO_HASH)
another_method_that_takes_the_same_arguments(**args)
Ответ 10
Если вам нужны аргументы в качестве хэша, и вы не хотите загрязнять тело метода хитрым извлечением параметров, используйте это:
def mymethod(firstarg, kw_arg1:, kw_arg2: :default)
args = MethodArguments.(binding) # All arguments are in 'args' hash now
...
end
Просто добавьте этот класс в ваш проект:
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
Ответ 11
Если функция находится внутри какого-то класса, вы можете сделать что-то вроде этого:
class Car
def drive(speed)
end
end
car = Car.new
method = car.method(:drive)
p method.parameters #=> [[:req, :speed]]