Skip to content

@eregon /binding_of_caller.rb
Created

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
# 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
Something went wrong with that request. Please try again.