| 11 | | # This method returns the binding of the method that called your |
|---|
| 12 | | # method. It will raise an Exception when you're not inside a method. |
|---|
| 13 | | # |
|---|
| 14 | | # It's used like this: |
|---|
| 15 | | # def inc_counter(amount = 1) |
|---|
| 16 | | # Binding.of_caller do |binding| |
|---|
| 17 | | # # Create a lambda that will increase the variable 'counter' |
|---|
| 18 | | # # in the caller of this method when called. |
|---|
| 19 | | # inc = eval("lambda { |arg| counter += arg }", binding) |
|---|
| 20 | | # # We can refer to amount from inside this block safely. |
|---|
| 21 | | # inc.call(amount) |
|---|
| 22 | | # end |
|---|
| 23 | | # # No other statements can go here. Put them inside the block. |
|---|
| 24 | | # end |
|---|
| 25 | | # counter = 0 |
|---|
| 26 | | # 2.times { inc_counter } |
|---|
| 27 | | # counter # => 2 |
|---|
| 28 | | # |
|---|
| 29 | | # Binding.of_caller must be the last statement in the method. |
|---|
| 30 | | # This means that you will have to put everything you want to |
|---|
| 31 | | # do after the call to Binding.of_caller into the block of it. |
|---|
| 32 | | # This should be no problem however, because Ruby has closures. |
|---|
| 33 | | # If you don't do this an Exception will be raised. Because of |
|---|
| 34 | | # the way that Binding.of_caller is implemented it has to be |
|---|
| 35 | | # done this way. |
|---|
| 36 | | def Binding.of_caller(&block) #:nodoc: |
|---|
| 37 | | old_critical = Thread.critical |
|---|
| 38 | | Thread.critical = true |
|---|
| 39 | | count = 0 |
|---|
| 40 | | cc, result, error, extra_data = Continuation.create(nil, nil) |
|---|
| 41 | | error.call if error |
|---|
| | 13 | class Binding #:nodoc: |
|---|
| | 14 | # This method returns the binding of the method that called your |
|---|
| | 15 | # method. It will raise an Exception when you're not inside a method. |
|---|
| | 16 | # |
|---|
| | 17 | # It's used like this: |
|---|
| | 18 | # def inc_counter(amount = 1) |
|---|
| | 19 | # Binding.of_caller do |binding| |
|---|
| | 20 | # # Create a lambda that will increase the variable 'counter' |
|---|
| | 21 | # # in the caller of this method when called. |
|---|
| | 22 | # inc = eval("lambda { |arg| counter += arg }", binding) |
|---|
| | 23 | # # We can refer to amount from inside this block safely. |
|---|
| | 24 | # inc.call(amount) |
|---|
| | 25 | # end |
|---|
| | 26 | # # No other statements can go here. Put them inside the block. |
|---|
| | 27 | # end |
|---|
| | 28 | # counter = 0 |
|---|
| | 29 | # 2.times { inc_counter } |
|---|
| | 30 | # counter # => 2 |
|---|
| | 31 | # |
|---|
| | 32 | # Binding.of_caller must be the last statement in the method. |
|---|
| | 33 | # This means that you will have to put everything you want to |
|---|
| | 34 | # do after the call to Binding.of_caller into the block of it. |
|---|
| | 35 | # This should be no problem however, because Ruby has closures. |
|---|
| | 36 | # If you don't do this an Exception will be raised. Because of |
|---|
| | 37 | # the way that Binding.of_caller is implemented it has to be |
|---|
| | 38 | # done this way. |
|---|
| | 39 | def of_caller(&block) |
|---|
| | 40 | old_critical = Thread.critical |
|---|
| | 41 | Thread.critical = true |
|---|
| | 42 | count = 0 |
|---|
| | 43 | cc, result, error, extra_data = Continuation.create(nil, nil) |
|---|
| | 44 | error.call if error |
|---|
| 43 | | tracer = lambda do |*args| |
|---|
| 44 | | type, context, extra_data = args[0], args[4], args |
|---|
| 45 | | if type == "return" |
|---|
| 46 | | count += 1 |
|---|
| 47 | | # First this method and then calling one will return -- |
|---|
| 48 | | # the trace event of the second event gets the context |
|---|
| 49 | | # of the method which called the method that called this |
|---|
| 50 | | # method. |
|---|
| 51 | | if count == 2 |
|---|
| 52 | | # It would be nice if we could restore the trace_func |
|---|
| 53 | | # that was set before we swapped in our own one, but |
|---|
| 54 | | # this is impossible without overloading set_trace_func |
|---|
| 55 | | # in current Ruby. |
|---|
| | 46 | tracer = lambda do |*args| |
|---|
| | 47 | type, context, extra_data = args[0], args[4], args |
|---|
| | 48 | if type == "return" |
|---|
| | 49 | count += 1 |
|---|
| | 50 | # First this method and then calling one will return -- |
|---|
| | 51 | # the trace event of the second event gets the context |
|---|
| | 52 | # of the method which called the method that called this |
|---|
| | 53 | # method. |
|---|
| | 54 | if count == 2 |
|---|
| | 55 | # It would be nice if we could restore the trace_func |
|---|
| | 56 | # that was set before we swapped in our own one, but |
|---|
| | 57 | # this is impossible without overloading set_trace_func |
|---|
| | 58 | # in current Ruby. |
|---|
| | 59 | set_trace_func(nil) |
|---|
| | 60 | cc.call(eval("binding", context), nil, extra_data) |
|---|
| | 61 | end |
|---|
| | 62 | elsif type == "line" then |
|---|
| | 63 | nil |
|---|
| | 64 | elsif type == "c-return" and extra_data[3] == :set_trace_func then |
|---|
| | 65 | nil |
|---|
| | 66 | else |
|---|
| 64 | | set_trace_func(nil) |
|---|
| 65 | | error_msg = "Binding.of_caller used in non-method context or " + |
|---|
| 66 | | "trailing statements of method using it aren't in the block." |
|---|
| 67 | | cc.call(nil, lambda { raise(ArgumentError, error_msg) }, nil) |
|---|
| 68 | | end |
|---|
| 69 | | end |
|---|
| 70 | | |
|---|
| 71 | | unless result |
|---|
| 72 | | set_trace_func(tracer) |
|---|
| 73 | | return nil |
|---|
| 74 | | else |
|---|
| 75 | | Thread.critical = old_critical |
|---|
| 76 | | case block.arity |
|---|
| 77 | | when 1 then yield(result) |
|---|
| 78 | | else yield(result, extra_data) |
|---|
| | 78 | Thread.critical = old_critical |
|---|
| | 79 | case block.arity |
|---|
| | 80 | when 1 then yield(result) |
|---|
| | 81 | else yield(result, extra_data) |
|---|
| | 82 | end |
|---|