У меня есть метод, который довольно дорогой и только почти работает.
$shadow_stack = []
set_trace_func( lambda {
|event, file, line, id, binding, classname|
if event == "call"
$shadow_stack.push( eval("local_variables", binding) )
elsif event == "return"
$shadow_stack.pop
end
} )
def method1( tuti, fruity )
foo
end
def method2(bim, bam, boom)
foo
x = 10
y = 3
end
def foo
puts $shadow_stack[-2].join(", ")
end
method1(1,2)
method2(3,4,4)
Выходы
tuti, fruity
bim, bam, boom, x, y
Мне любопытно, почему вы хотите использовать такую функциональность таким обобщенным образом.
Мне любопытно, как вы думаете, эта функциональность позволит автоматическую отладку? Вам по-прежнему нужно вводить вызовы для вашей функции "foo". Фактически, что-то, основанное на set_trace_func
, в большей степени может быть автоматическим, так как вам не нужно трогать существующий код. Действительно, именно так и реализован debug.rb в терминах set_trace_func.
Решения вашего точного вопроса действительно в основном, как вы обрисовали. используйте caller + parsetree или откройте файл и получите данные таким образом. Я не знаю возможности отражения, которая позволила бы вам получить имена аргументов. Вы можете одобрить моё решение, взяв соответствующий объект метода и вызвав #arity
, чтобы затем определить, какие из local_variables
являются аргументами, но, хотя кажется, что результат этой функции упорядочен, я не уверен, что безопасно полагаться на что. Если вы не возражаете, я спрашиваю, когда у вас есть данные и интерфейс, который вы описываете, что вы собираетесь с ними делать? Автоматическая отладка не была тем, что изначально приходило на ум, когда я предполагал использовать эту функциональность, хотя, возможно, это не с моей стороны.
Aha!
Тогда я бы подошел к этому иначе. Уже есть несколько библиотек ruby для проектирования по контракту, включая ruby-contract, rdbc и т. Д.
Другой вариант - написать что-то вроде:
def positive
lambda { |x| x >= 0 }
end
def any
lambda { |x| true }
end
class Module
def define_checked_method(name, *checkers, &body)
define_method(name) do |*args|
unless checkers.zip(args).all? { |check, arg| check[arg] }
raise "bad argument"
end
body.call(*args)
end
end
end
class A
define_checked_method(:add, positive, any) do |x, y|
x + y
end
end
a = A.new
p a.add(3, 2)
p a.add(3, -1)
p a.add(-4, 2)
Выходы
5
2
checked_rb.rb:13:in `add': bad argument (RuntimeError)
from checked_rb.rb:29
Конечно, это может быть сделано намного более изощренно, и, действительно, это то, что предоставили библиотеки, о которых я упоминал, но, возможно, это способ привести вас туда, куда вы хотите идти, не обязательно выбирая путь, который вы запланировали использовать для получения есть