public
Created

  • Download Gist
binding_of_caller.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
# 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

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.