From: Robert Klemme Date: 2004-06-12T23:28:41+09:00 Subject: Re: Any way to get the calling method's binding? "Florian Gross" 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