Skip to content

Instantly share code, notes, and snippets.

@eregon
Created August 19, 2010 21:14
Show Gist options
  • Save eregon/538937 to your computer and use it in GitHub Desktop.
Save eregon/538937 to your computer and use it in GitHub Desktop.
# Binding.of_caller
# Revisited for 1.9
# eregon
=begin
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(Exception, error_msg ) })
end
end
unless result
set_trace_func(tracer)
return nil
else
Thread.critical = old_critical
yield result
end
end
=end
=begin
def Binding.of_caller
result = nil
count = 0
tracer = lambda do |event, file, line, id, _binding, classname|
p [event, file, line, id, binding, classname]
case event
when "c-return", "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)
result = eval("binding", _binding)
end
when "line"
p :line
else
p [event, file, line, id, _binding, classname]
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."
raise Exception, error_msg
end
end
unless result
set_trace_func(tracer)
return nil
else
yield result
end
end
=end
$stack = []
set_trace_func lambda { |event, file, line, id, _binding, classname|
# puts "%8s %-3d %25s %11s %s" % [event, line, id, classname, _binding.inspect]
$stack << [_binding, id]
}
class Binding
def [] var
self.eval(var.to_s)
end
def []= var, val
self.eval("lambda { |v| #{var} = v }").call(val)
end
def vars
self.eval("local_variables").each_with_object({}) { |var,h| h[var] = self.eval(var.to_s) }
end
def inspect
"#<Binding:#{vars}:#{object_id}>"
end
end
def Binding.of_caller
#b = $stack.reverse.find { |_,id| id == :method_added }.first
#p caller(2)
#p prec = caller[-3].match(/`(.+)'/)[1].to_sym
#p caller
prec = caller(2)[0].match(/`(.+)'/)[1].to_sym
prec = nil if prec =~ /^<.+>$/ # prec == :"<main>" or prec == :"<module:Test>"
b = $stack.reverse.find { |_,id| id == prec }.first
yield b
end
x = :secret
def m
Binding.of_caller do |b|
p :hello
p b.eval('x')
end
end
def m2
x = :secret2
m
end
m
m2
def m3
m2
end
m3
module Test
module NestedTest
x = :secret4
m
end
x = :secret3
m
end
class Base
def initialize caller
@caller = caller
end
def secret
x = :secret_final
@caller.context
end
end
class Caller
attr_reader :base
def initialize
@base = Base.new self
end
def context
Binding.of_caller do |b|
p :hello
p b[:x]
end
end
end
Caller.new.base.secret
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment