Any way to get the calling method's binding?

Discussion in 'Ruby' started by Gavin Sinclair, Jun 12, 2004.

  1. Something I've wanted to do on a few occasions recently is to evaluate
    an expression in the context of the calling method. A simple example
    is this:

    def trace(expr, _binding)
    LOG.debug "#{expr} = #{eval expr, _binding)}"
    end

    def something
    x = 5
    y = 7
    trace "x + y", binding
    end

    The effect of calling something() now is that the message "x + y = 12"
    gets logged.

    It Would Be Nice if I could do this instead:

    def trace(expr)
    _binding = somehow_get_calling_methods_binding()
    LOG.debug "#{expr} = #{eval expr, _binding)}"
    end

    def something
    x = 5
    y = 7
    trace "x + y"
    end

    Is there a substitute for "somehow_get_calling_methods_binding()" in
    the code above?

    Thanks,
    Gavin
    Gavin Sinclair, Jun 12, 2004
    #1
    1. Advertising

  2. 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. A simple example
    > is this:


    It's being discussed:

    http://rcrchive.net/rcr/RCR/RCR253
    Joel VanderWerf, Jun 12, 2004
    #2
    1. Advertising

  3. In the meantime, you could do this:

    def trace (expr)
    LOG.debug "#{expr} = #{yield expr}"
    end

    def something
    x=5
    x=7
    trace "x+y", {|n| eval n}
    end

    something => 12

    I'll admit that it'd be nice if the block passing could be automated;
    not sure how to do that off the top of my head. This reminds me of
    dynamic scoping; anybody got any clue about how to do away with the
    block? Maybe there's a way to do this with an extension?

    Austin


    Joel VanderWerf wrote:

    > 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. A simple example
    >> is this:

    >
    >
    > It's being discussed:
    >
    > http://rcrchive.net/rcr/RCR/RCR253
    >
    >
    Austin McDonald, Jun 12, 2004
    #3
  4. Austin McDonald wrote:
    > In the meantime, you could do this:
    >
    > def trace (expr)
    > LOG.debug "#{expr} = #{yield expr}"
    > end
    >
    > def something
    > x=5
    > x=7
    > trace "x+y", {|n| eval n}
    > end
    >
    > something => 12


    def trace(&block)
    expr = block.call
    puts "#{expr} = #{eval(expr, block)}"
    end

    def something
    x = 5
    y = 7
    trace {"x + y"}
    end

    something # ==> x + y = 12
    Joel VanderWerf, Jun 12, 2004
    #4
  5. Excellent; I had the right idea, but yours is much cleaner.

    For giggles, and maybe b/c it's a little more explicit, you could use
    Kernel#binding to make it obvious what's going on:

    def trace(expr, b)
    LOG.debug "#{expr} = #{eval(expr, b)}"
    end

    def something()
    x=5
    y=7
    trace "x+y", binding
    end

    something # ==> x+y = 12

    Although I like Joel's version better, I think this is a little clearer.

    Austin



    Joel VanderWerf wrote:

    > Austin McDonald wrote:
    >
    >> In the meantime, you could do this:
    >>
    >> def trace (expr)
    >> LOG.debug "#{expr} = #{yield expr}"
    >> end
    >>
    >> def something
    >> x=5
    >> x=7
    >> trace "x+y", {|n| eval n}
    >> end
    >>
    >> something => 12

    >
    >
    > def trace(&block)
    > expr = block.call
    > puts "#{expr} = #{eval(expr, block)}"
    > end
    >
    > def something
    > x = 5
    > y = 7
    > trace {"x + y"}
    > end
    >
    > something # ==> x + y = 12
    >
    >
    >
    Austin McDonald, Jun 12, 2004
    #5
  6. On Sat, 12 Jun 2004, Austin McDonald wrote:

    > Excellent; I had the right idea, but yours is much cleaner.
    >
    > For giggles, and maybe b/c it's a little more explicit, you could use
    > Kernel#binding to make it obvious what's going on:
    >
    > def trace(expr, b)
    > LOG.debug "#{expr} = #{eval(expr, b)}"
    > end
    >
    > def something()
    > x=5
    > y=7
    > trace "x+y", binding
    > end
    >
    > something # ==> x+y = 12
    >
    > Although I like Joel's version better, I think this is a little clearer.
    >
    > Austin
    >


    Umm that's exactly what gavin was trying to avoid, explicitely passing the
    binding...

    Charles Comstock
    Charles Comstock, Jun 12, 2004
    #6
  7. Right. I'm out of it at the moment; got wrapped up in following the
    idea trails and wound up where Gavin started... my apologies. I think
    I'll get some sleep.

    Austin

    Austin McDonald wrote:

    > Excellent; I had the right idea, but yours is much cleaner.
    >
    > For giggles, and maybe b/c it's a little more explicit, you could use
    > Kernel#binding to make it obvious what's going on:
    >
    > def trace(expr, b)
    > LOG.debug "#{expr} = #{eval(expr, b)}"
    > end
    >
    > def something()
    > x=5
    > y=7
    > trace "x+y", binding
    > end
    >
    > something # ==> x+y = 12
    >
    > Although I like Joel's version better, I think this is a little clearer.
    >
    > Austin
    >
    >
    >
    > Joel VanderWerf wrote:
    >
    >> Austin McDonald wrote:
    >>
    >>> In the meantime, you could do this:
    >>>
    >>> def trace (expr)
    >>> LOG.debug "#{expr} = #{yield expr}"
    >>> end
    >>>
    >>> def something
    >>> x=5
    >>> x=7
    >>> trace "x+y", {|n| eval n}
    >>> end
    >>>
    >>> something => 12

    >>
    >>
    >>
    >> def trace(&block)
    >> expr = block.call
    >> puts "#{expr} = #{eval(expr, block)}"
    >> end
    >>
    >> def something
    >> x = 5
    >> y = 7
    >> trace {"x + y"}
    >> end
    >>
    >> something # ==> x + y = 12
    >>
    >>
    >>

    >
    >
    Austin McDonald, Jun 12, 2004
    #7
  8. 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|
    > 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
    Florian Gross, Jun 12, 2004
    #8
  9. "Florian Gross" <> schrieb im Newsbeitrag
    news:...
    > 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
    Robert Klemme, Jun 12, 2004
    #9
  10. Robert Klemme wrote:

    > 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
    > }


    Global trace_funcs are a *huge* performance bottle neck. That's why I'm
    doing so much work to make sure that the trace_func will go away as soon
    as possible.

    Regards,
    Florian Gross
    Florian Gross, Jun 12, 2004
    #10
  11. "Florian Gross" <> schrieb im Newsbeitrag
    news:...
    > Robert Klemme wrote:
    >
    > > 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
    > > }

    >
    > Global trace_funcs are a *huge* performance bottle neck. That's why I'm
    > doing so much work to make sure that the trace_func will go away as soon
    > as possible.


    Ah, ok. I thought there should be some advantage...

    robert
    Robert Klemme, Jun 12, 2004
    #11
  12. At 22:23 12/06/2004 +0900, you wrote:
    >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|
    >> 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 is a very creative piece of work, congratulations !
    I am adding that to my rcr.rb with the proper credit.

    I guess that it is not extremely efficient but it will
    help me a lot in my exception logging scheme. So far I
    used to explicitly pass the binding to be able to dump
    the local variables when required. Thanks !

    For more speed demanding usage, there would be some need
    for native support I guess, but as of today your
    solution is probably very close to the most efficient
    solution.

    BTW: What is "simplecc.rb" ?

    Yours,

    JeanHuguesRobert

    -------------------------------------------------------------------------
    Web: http://hdl.handle.net/1030.37/1.1
    Phone: +33 (0) 4 92 27 74 17
    Jean-Hugues ROBERT, Jun 13, 2004
    #12
  13. Jean-Hugues ROBERT wrote:

    >> [Binding.of_caller]

    > This is a very creative piece of work, congratulations !
    > I am adding that to my rcr.rb with the proper credit.


    Thanks. :)

    > I guess that it is not extremely efficient but it will
    > help me a lot in my exception logging scheme. So far I
    > used to explicitly pass the binding to be able to dump
    > the local variables when required. Thanks !


    Nice to hear that it's useful. I actually need it for implementing a
    wrapper around eval(). (I need to give unique file descriptors to
    evaluated code in proc_source.rb so that I can later map them back to code.)

    > For more speed demanding usage, there would be some need
    > for native support I guess, but as of today your
    > solution is probably very close to the most efficient
    > solution.


    I dunno how fast it is -- we would need to do some benchmarking to find
    that out -- but at least it is only slow where it's being used. :)

    > BTW: What is "simplecc.rb" ?


    It's the same code that is included in the rescue part. It's the only
    way I can use Continuations without going insane.

    > Yours,
    > JeanHuguesRobert


    Regards,
    Florian Gross
    Florian Gross, Jun 13, 2004
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. boki
    Replies:
    2
    Views:
    680
  2. Eric Lilja
    Replies:
    6
    Views:
    321
    David Hilsee
    Mar 13, 2005
  3. Ben
    Replies:
    3
    Views:
    232
  4. Kenneth McDonald
    Replies:
    3
    Views:
    111
    Joel VanderWerf
    Aug 31, 2007
  5. Xeno Campanoli
    Replies:
    9
    Views:
    145
    Aaron Patterson
    Feb 14, 2010
Loading...

Share This Page