Daniel said:
Is there a way to get variables to return their symbol so you can access
what they will be referencing in the future ?
For ordinary variables you can use the code that I attached.
Regards,
Florian Gross
require 'binding_of_caller'
class Variable
def self.new(name, context = nil)
return super(name, context) unless context.nil?
Binding.of_caller do |context|
super(name, context)
end
end
class << self; alias :[] :new; end
attr_reader :context, :name
def initialize(name, context)
unless /^[a-z_][\w\d_]*$/.match(name.to_s)
raise(NameError, "Illegal variable name: #{name.inspect}")
end
@name, @context = name, context
@setter = lambda do |value|
eval "#{@name} = ObjectSpace._id2ref(#{value.id})", context
end
exists = lambda do
eval "local_variables.include?(#{@name.to_s.inspect})", context
end
@getter = lambda do
eval("#{@name}", context) if exists.call
end
end
def []=(value); @setter.call(value); end
def []; @getter.call; end
alias :value= :[]=
alias :value :[]
def inspect
"Variable[#{@name.inspect}, #{@context.inspect}]"
end
end
begin
require 'simplecc'
rescue LoadError
def Continuation.create(*args, &block)
cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and args.empty?}
result ||= args
return *[cc, *result]
end
end
# This method returns the binding of the method that called your
# method. It will raise an Exception when you're not inside a method.
#
# It's used like this:
# def inc_counter
# Binding.of_caller do |binding|
# eval("counter += 1", binding)
# end
# end
# counter = 0
# 2.times { inc_counter }
# counter # => 2
#
# You will have to put the whole rest of your method into the
# block that you pass into this method. If you don't do this
# an Exception will be raised. Because of the way that this is
# implemented it has to be done this way. If you don't do it
# like this it will raise an Exception.
def Binding.of_caller(&block)
old_critical = Thread.critical
Thread.critical = true
count = 0
cc, result, error = Continuation.create(nil, nil)
error.call if error
tracer = lambda do |*args|
type, context = args[0], args[4]
if type == "return"
count += 1
# First this method and then calling one will return --
# the trace event of the second event gets the context
# of the method which called the method that called this
# method.
if count == 2
# It would be nice if we could restore the trace_func
# that was set before we swapped in our own one, but
# this is impossible without overloading set_trace_func
# in current Ruby.
set_trace_func(nil)
cc.call(eval("binding", context), nil)
end
elsif type != "line"
set_trace_func(nil)
error_msg = "Binding.of_caller used in non-method context or " +
"trailing statements of method using it aren't in the block."
cc.call(nil, lambda { raise(ArgumentError, error_msg ) })
end
end
unless result
set_trace_func(tracer)
return nil
else
Thread.critical = old_critical
yield result
end
end