24

Essentially I'm wondering if the following can be done in Ruby.

So for example:

def bar(symbol) 
  # magic code goes here, it outputs "a = 100" 
end

def foo
  a = 100 
  bar(:a) 
end
Sam Saffron
  • 128,308
  • 78
  • 326
  • 506

5 Answers5

18

You have to pass foo's context to bar:

def foo
  a = 100
  bar(:a, binding)
end
def bar(sym, b)
  puts "#{sym} is #{eval(sym.to_s, b)}"
end
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
18

There is no built-in way to get a callers binding in Ruby in 1.8.X or 1.9.X.

You can use https://github.com/banister/binding_of_caller to work around.

In MRI 2.0 you can use RubyVM::DebugInspector, see: https://github.com/banister/binding_of_caller/blob/master/lib/binding_of_caller/mri2.rb

Working sample in MRI 2.0:

require 'debug_inspector'

def bar(symbol)
  RubyVM::DebugInspector.open do |inspector|
    val = eval(symbol.to_s, inspector.frame_binding(2))
    puts "#{symbol}: #{val}"
  end
end

def foo
  a = 100
  bar(:a)
end

foo
# a: 100
Sam Saffron
  • 128,308
  • 78
  • 326
  • 506
  • here's an example of the fragile way to use set_trace_func to do so : http://stackoverflow.com/questions/1314592/how-can-i-get-the-binding-from-methodmissing/1315612#1315612 – rampion Sep 11 '09 at 17:12
  • 2
    Should include disclaimer: Please don't do this unless you are *actually* using it for debugging of some sort. – tmandry Jan 26 '14 at 04:35
  • Why @tmandry? This is a metaprogramming technique - it is undesirable in the way that all metaprogramming is undesirable (difficult to maintain and debug) but it isn't dangerous is a new or special way. – Brad Aug 24 '18 at 21:51
9

Here's a easier syntax hack, using a passed in block binding:

  def loginfo &block
    what = yield.to_s
    evaled = eval(what, block.binding)
    Rails.logger.info "#{what} = #{evaled.inspect}"
  end

called like this:

  x = 1
  loginfo{ :x }

will log out:

  x = 1
Jaime Cham
  • 1,494
  • 1
  • 15
  • 16
6

Just FYI, here's a "hacky way". This is my (re-)implementation of well-known ppp.rb:

#!/usr/bin/ruby
#
# better ppp.rb
#

require 'continuation' if RUBY_VERSION >= '1.9.0'

def ppp(*sym)
  cc = nil
  ok = false

  set_trace_func lambda {|event, file, lineno, id, binding, klass|
    if ok
      set_trace_func nil
      cc.call(binding)
    else
      ok = event == "return"
    end
  }
  return unless bb = callcc{|c| cc = c; nil }

  sym.map{|s| v = eval(s.to_s, bb); puts "#{s.inspect} = #{v}"; v }
end

a = 1
s = "hello"
ppp :a, :s

exit 0

This currently fails with 1.9.[012] due to a bug in ruby's set_trace_func.

Taisuke Yamada
  • 397
  • 2
  • 7
  • Very interesting! I've adapted it to build [my own version of caller_binding](http://rubychallenger.blogspot.com/2011/07/caller-binding.html). But [what is ppp?](http://stackoverflow.com/questions/6884122/ruby-what-is-ppp-rb) – Sony Santos Jul 30 '11 at 15:36
3

Check article out Variable Bindings in Ruby

class Reference
  def initialize(var_name, vars)
    @getter = eval "lambda { #{var_name} }", vars
    @setter = eval "lambda { |v| #{var_name} = v }", vars
  end
  def value
    @getter.call
  end
  def value=(new_value)
    @setter.call(new_value)
  end
end

def ref(&block)
  Reference.new(block.call, block.binding)
end

def bar(ref)
  # magic code goes here, it outputs "a = 100" 
  p ref.value
end

def foo
  a = 100 
  bar(ref{:a}) 
end

foo
neoneye
  • 50,398
  • 25
  • 166
  • 151
  • +1 looks very interesting, I wonder if this can be done without the block though ... – Sam Saffron Aug 31 '09 at 13:15
  • I don't think it can be done without the block in ruby 1.8.x. I'm not familiar with ruby 1.9.x, so things may be different there. – neoneye Aug 31 '09 at 16:20
  • In Ruby 2.1 you still need the block, but implementation of `Reference` can be redone using calls to [`Binding#local_variable_get`](http://www.ruby-doc.org/core-2.1.0/Binding.html#method-i-local_variable_get) and [`Binding#vars.local_variable_set`](http://www.ruby-doc.org/core-2.1.0/Binding.html#method-i-local_variable_set). Although, the current implementation probably allows for calls to reader and writer methods in addition to local variables, whereas the new implementation wouldn't... – Ajedi32 Jan 13 '15 at 21:45