Search code examples
rubyirblocal-variables

listing the local variables in irb


I am attempting to list all local variables and their values. This works in IRB:

local_variables.map {|var| "#{var} = " + local_variable_get(var).inspect

I would like to wrap this in a method, but when this code is placed inside a method, local_variables no longer points to the correct list of variables. Therefore, I tried some solutions along the lines of:

class Object
  def list_vars
    self.__send__(:binding).local_variables.map {|var| "#{var} = " + self.__send__(:binding).local_variable_get(var).inspect}
  end
end

However, it seems that the binding of the current object is different from main, which is used when calling local_variables directly.

Does self.send(:binding) change depending on what method self is in?


Solution

  • Your code is really complicated and obfuscated. First, let's clean up your code a bit so that we can see more clearly what's going on. You don't need self, since it is the default receiver anyway. And you don't need send, because you already know the name of the method you want to call. Also, Kernel#local_variables uses the current Binding anyway. Also, typically, such methods which are supposed to by called without an explicit receiver and don't actually make use of self (e.g. like puts or require) are put into Kernel. So, your code is equivalent to the following:

    module Kernel
      def list_vars
        local_variables.map {|var| "#{var} = " + binding.local_variable_get(var).inspect}
      end
    end
    

    Now we can immediately see the problem: you are reflecting on the local variables and the Binding of list_vars and not the place where it was called from! You need to pass the Binding into the method and use that instead:

    module Kernel
      def list_vars(binding)
        binding.local_variables.map {|var| "#{var} = " + binding.local_variable_get(var).inspect}
      end
    end
    
    list_vars(binding)
    

    Or, you could make it an instance method of Binding:

    class Binding
      def list_vars
        local_variables.map {|var| "#{var} = " + local_variable_get(var).inspect}
      end
    end
    
    binding.list_vars