Created
August 19, 2010 21:14
-
-
Save eregon/538937 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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