From: "Mauricio Fernández" Date: 2004-09-28T22:52:59+09:00 Subject: Re: Binding of the Caller (redux) On Tue, Sep 28, 2004 at 09:14:02PM +0900, Gavin Kistner wrote: > On Sep 28, 2004, at 2:16 AM, Mauricio Fern�ndez wrote: > > >On Tue, Sep 28, 2004 at 12:56:25PM +0900, Gavin Kistner wrote: > >>./libs/binding_of_caller.rb:61:in `of_caller': Binding.of_caller used > >>in non-method context or trailing statements of method using it aren't > >>in the block. (ArgumentError) > > > >The message is quite explicit *g* > > Perhaps, but confusing to me :) > (The phrase "Either your zyloxens aren't grimbled or you tweedled > outside of a borklub" is also explicit, but that don't mean I have a > clue how to solve the problem ;) > > >Binding.of_caller do |b| > > evt.source = eval("self", b) > > callbacks.each{|m| m.call evt } > > end > >end > > That worked, thanks. > > So does "... trailing statements of method using it aren't in the > block" mean "you can't place any statements after the Binding.of_caller > invocation"? Exactly. >(Like it irrevocably changes the context and can't get you > back to where you were or something?) That's because Florian's Binding.of_caller works as follows: * a continuation for the block is created * of_caller returns; *the enclosing method returns* * the next event notified to the proc_func is the 'return'; at that point the binding of the caller is captured * the continuation is called, and the block is given the Binding that was obtained previously * when the block has been executed, the method returns *for the second time*, with the value returned by the block It is possible to implement Binding.of_caller without callcc, which makes it much faster, but that comes at a cost: * ugly interface * the value returned is a proxy for the real value (can be fixed with evil.rb of course for many kinds of objects) batsman@tux-chan:/tmp$ cat binding_of_caller.rb # UGLY 'binding of caller' implementation, which doesn't use callcc. # Based on Florian Gro�' original. # This method allows you to grab 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(self, r = lambda do # binding = eval("binding_of_caller", r) # 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(oldself, block) old_critical = Thread.critical Thread.critical = true count = 0 result = nil retvalue = Object.new #FIXME: which methods do we want #class << retvalue; self end.send(:define_method, :to_s){ result } class << retvalue; self end.send(:define_method, :value){ result } tracer = lambda do |*args| #p 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) eval("binding_of_caller = nil; lambda{|binding_of_caller|}", block).call(context) result = oldself.instance_eval(&block) Thread.critical = old_critical 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." raise(ArgumentError, error_msg) end end set_trace_func(tracer) retvalue end def foo a = "some string" bar end def bar Binding.of_caller(self, r = lambda do binding_of_caller = eval("binding_of_caller", r) eval("a", binding_of_caller) end) end # we can get rid of the proxy obj with Object#become or whatever p foo.value batsman@tux-chan:/tmp$ ruby -v binding_of_caller.rb ruby 1.8.2 (2004-09-22) [i686-linux] "some string" Both implementations have the same problem: you lose the previous trace_func. It could be restored transparently if Ruby exposed it. -- Running Debian GNU/Linux Sid (unstable) batsman dot geo at yahoo dot com