【发布时间】:2010-11-24 07:30:28
【问题描述】:
基本上我想知道the following 是否可以在Ruby 中完成。
例如:
def bar(symbol)
# magic code goes here, it outputs "a = 100"
end
def foo
a = 100
bar(:a)
end
【问题讨论】:
标签: ruby
基本上我想知道the following 是否可以在Ruby 中完成。
例如:
def bar(symbol)
# magic code goes here, it outputs "a = 100"
end
def foo
a = 100
bar(:a)
end
【问题讨论】:
标签: ruby
您必须将foo 的上下文传递给bar:
def foo
a = 100
bar(:a, binding)
end
def bar(sym, b)
puts "#{sym} is #{eval(sym.to_s, b)}"
end
【讨论】:
在 1.8.X 或 1.9.X 的 Ruby 中,没有内置方法来获取调用者绑定。
您可以使用https://github.com/banister/binding_of_caller 来解决。
在 MRI 2.0 中,您可以使用 RubyVM::DebugInspector,请参阅:https://github.com/banister/binding_of_caller/blob/master/lib/binding_of_caller/mri2.rb
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
【讨论】:
这是一个更简单的语法破解,使用传入的块绑定:
def loginfo &block
what = yield.to_s
evaled = eval(what, block.binding)
Rails.logger.info "#{what} = #{evaled.inspect}"
end
这样称呼:
x = 1
loginfo{ :x }
将退出:
x = 1
【讨论】:
仅供参考,这是一种“骇人听闻的方式”。 这是我对众所周知的 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
目前在 1.9 中失败。[012]由于 ruby 的 set_trace_func 中的错误。
【讨论】:
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
【讨论】:
Reference 的实现可以通过调用 Binding#local_variable_get 和 Binding#vars.local_variable_set 来重做。虽然,当前的实现可能除了局部变量之外还允许调用读取器和写入器方法,而新的实现不会...