"Florian Gross" <flgr@ccan.de> schrieb im Newsbeitrag
news:2j0eafFrmjefU1@uni-berlin.de...
Gavin Sinclair wrote:
> Something I've wanted to do on a few occasions recently is to evaluate
> an expression in the context of the calling method.
Use this:
> begin
> require 'simplecc'
> rescue LoadError
> def Continuation.create(*args, &block)
> cc = nil; result = callcc {|c| cc = c; block.call(cc) if block and
args.empty?}
> result ||= args
> return *[cc, *result]
> end
> end
>
> # This method returns the binding of the method that called your
> # method. Don't use it when you're not inside a method.
> #
> # It's used like this:
> # def inc_counter
> # Binding.of_caller do |binding|
> # eval("counter += 1", binding)
> # end
> # end
> # counter = 0
> # 2.times { inc_counter }
> # counter # => 2
> #
> # You will have to put the whole rest of your method into the
> # block that you pass into this method. If you don't do this
> # an Exception will be raised. Because of the way that this is
> # implemented it has to be done this way.
> 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|
You're recreating the function on every invocation. Could be a performance
bottleneck.
> 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(ArgumentError, error_msg ) })
> end
> end
>
> unless result
> set_trace_func(tracer)
> return nil
> else
> Thread.critical = old_critical
> yield result
> end
> end
Regards,
Florian Gross
This seems much simpler to me...
set_trace_func lambda { |event, file, line, id, binding, classname|
case event
when "call"
( Thread.current[:bindings] ||= ).push binding
when "return"
Thread.current[:bindings].pop
end
}
module Kernel
private
def caller_binding
Thread.current[:bindings][-3]
end
end
# demo usage:
def test2
# set x for demonstration purposes
x = 99
p eval( "x", caller_binding )
end
def test
x = 10
test2
x = 20
test2
end
test
Regards
robert