Getting local variable names defined inside a method from outside the method
Is it possible in Ruby to get all the local variables names defined inside a method from outside the method using metaprogramming?
def foo
var = 100
arr = [1,2]
end
Something like foo.local_variables.
I only need to retrieve the variable names, not their values.
Why I'm trying to find those variable names : I have a class where I want to dynamically generate setters for all instance variables initialized in "#initialize".
In order to do that, inside "initialize", I am using something like instance_variables.each do |inst_var_name| define_singleton_method(...) do ...
instance_variables.each do |inst_var_name| define_singleton_method(...) do ...
This works: each instanced object gets its setters as singleton methods. However, I was looking for a solution where I could read the instance variables names from outside the "initialize" method, in order to be able to use "#define_method" for them, and create regular methods and not singleton methods.
You can (re)-parse the method and inspect the S-EXPR tree. See below for a proof of concept. You can get hold of the file where the method is defined using Method#source_location
and then read that file. There is surely room for improvement to my code but should get you started. It is a fully functional piece of code and only requires the ruby parser gem (https://github.com/whitequark/parser).
require 'parser/current'
node = Parser::CurrentRuby.parse(DATA.read) # DATA is everything that comes after __END__
def find_definition(node, name)
return node if definition_node?(node, name)
if node.respond_to?(:children)
node.children.find do |child|
find_definition(child, name)
end
end
end
def definition_node?(node, name)
return false if !node.respond_to?(:type)
node.type == :def && node.children.first == name
end
def collect_lvasgn(node)
result = []
return result if !node.respond_to?(:children)
node.children.each do |child|
if child.respond_to?(:type) && child.type == :lvasgn
result << child.children.first
else
result += collect_lvasgn(child)
end
end
result
end
definition = find_definition(node, :foo)
puts collect_lvasgn(definition)
__END__
def foo
var = 100
arr = [1,2]
if something
this = 3 * var
end
end
def bar
var = 200
arr = [3, 4]
end
Do you mind telling us WHY you want to find the variables?
These variables only exist briefly as Jordan points out, they exist only as long as the method is actively executing and are discarded immediately after.
If you want to instrument these you need to do it inside the method. The method itself does not allow this sort of reflection.
链接地址: http://www.djcxy.com/p/25770.html上一篇: 访问者在Ruby on Rails中工作
下一篇: 从方法外部获取在方法内定义的局部变量名称