Class | Binding |
In: |
lib/binding_of_caller.rb
|
Parent: | Object |
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(amount = 1) Binding.of_caller do |binding| # Create a lambda that will increase the variable 'counter' # in the caller of this method when called. inc = eval("lambda { |arg| counter += arg }", binding) # We can refer to amount from inside this block safely. inc.call(amount) end # No other statements can go here. Put them inside the block. end counter = 0 2.times { inc_counter } counter # => 2
Binding.of_caller must be the last statement in the method. This means that you will have to put everything you want to do after the call to Binding.of_caller into the block of it. This should be no problem however, because Ruby has closures. If you don’t do this an Exception will be raised. Because of the way that Binding.of_caller is implemented it has to be done this way.
Please note that currently bindings returned by Binding.of_caller() will have a wrong self context which means you can not call methods, access instance variables and so on on the calling object. You can work around this by defining the method which uses the binding on all objects and telling your users to use them without a receiver. This is how ruby-breakpoint works around the problem.
This is believed to be a bug in Ruby and has been reported to ruby-core. See www.ruby-forum.com/topic/67255
# File lib/binding_of_caller.rb, line 46 46: def Binding.of_caller(&block) 47: old_critical = Thread.critical 48: Thread.critical = true 49: count = 0 50: cc, result, error, extra_data = Continuation.create(nil, nil) 51: if error then 52: Thread.critical = old_critical 53: error.call 54: end 55: 56: tracer = lambda do |*args| 57: type, context, extra_data = args[0], args[4], args 58: if type == "return" 59: count += 1 60: # First this method and then calling one will return -- 61: # the trace event of the second event gets the context 62: # of the method which called the method that called this 63: # method. 64: if count == 2 65: # It would be nice if we could restore the trace_func 66: # that was set before we swapped in our own one, but 67: # this is impossible without overloading set_trace_func 68: # in current Ruby. 69: set_trace_func(nil) 70: cc.call(context, nil, extra_data) 71: end 72: elsif type == "line" then 73: nil 74: elsif type == "c-return" and extra_data[3] == :set_trace_func then 75: nil 76: else 77: set_trace_func(nil) 78: error_msg = "Binding.of_caller used in non-method context or " + 79: "trailing statements of method using it aren't in the block." 80: cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil) 81: end 82: end 83: 84: unless result 85: set_trace_func(tracer) 86: return nil 87: else 88: Thread.critical = old_critical 89: case block.arity 90: when 1 then yield(result) 91: else yield(result, extra_data) 92: end 93: end 94: end