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中工作

下一篇: 从方法外部获取在方法内定义的局部变量名称