类 Binding
类 Binding
的对象封装了代码中某个特定位置的执行上下文,并保留此上下文以供将来使用。可以在此上下文中访问的变量、方法、self
的值以及可能的迭代器块都将被保留。Binding
对象可以使用 Kernel#binding
创建,并可供 Kernel#set_trace_func
的回调和 TracePoint
的实例使用。
这些绑定对象可以作为 Kernel#eval
方法的第二个参数传递,为评估建立一个环境。
class Demo def initialize(n) @secret = n end def get_binding binding end end k1 = Demo.new(99) b1 = k1.get_binding k2 = Demo.new(-3) b2 = k2.get_binding eval("@secret", b1) #=> 99 eval("@secret", b2) #=> -3 eval("@secret") #=> nil
Binding
对象没有特定于类的任何方法。
公共实例方法
在 binding 的上下文中评估 string 中的 Ruby 表达式。如果存在可选的 filename 和 lineno 参数,则在报告语法错误时将使用它们。
def get_binding(param) binding end b = get_binding("hello") b.eval("param") #=> "hello"
static VALUE bind_eval(int argc, VALUE *argv, VALUE bindval) { VALUE args[4]; rb_scan_args(argc, argv, "12", &args[0], &args[2], &args[3]); args[1] = bindval; return rb_f_eval(argc+1, args, Qnil /* self will be searched in eval */); }
打开一个 IRB
会话,其中调用 binding.irb
,允许进行交互式调试。你可以调用当前作用域中可用的任何方法或变量,并在需要时改变状态。
给定一个名为 potato.rb
的 Ruby 文件,其中包含以下代码
class Potato def initialize @cooked = false binding.irb puts "Cooked potato: #{@cooked}" end end Potato.new
运行 ruby potato.rb
将打开一个 IRB
会话,其中调用 binding.irb
,你将看到以下内容
$ ruby potato.rb From: potato.rb @ line 4 : 1: class Potato 2: def initialize 3: @cooked = false => 4: binding.irb 5: puts "Cooked potato: #{@cooked}" 6: end 7: end 8: 9: Potato.new irb(#<Potato:0x00007feea1916670>):001:0>
你可以键入任何有效的 Ruby 代码,它将在当前上下文中进行评估。这允许你在无需重复运行代码的情况下进行调试
irb(#<Potato:0x00007feea1916670>):001:0> @cooked => false irb(#<Potato:0x00007feea1916670>):002:0> self.class => Potato irb(#<Potato:0x00007feea1916670>):003:0> caller.first => ".../2.5.1/lib/ruby/2.5.0/irb/workspace.rb:85:in `eval'" irb(#<Potato:0x00007feea1916670>):004:0> @cooked = true => true
你可以使用 exit
命令退出 IRB
会话。请注意,退出将在 binding.irb
暂停执行的位置恢复执行,如你可以从本例中打印到标准输出的内容中看到的那样
irb(#<Potato:0x00007feea1916670>):005:0> exit Cooked potato: true
有关更多信息,请参见 IRB
。
# File lib/irb.rb, line 1534 def irb(show_code: true) # Setup IRB with the current file's path and no command line arguments IRB.setup(source_location[0], argv: []) # Create a new workspace using the current binding workspace = IRB::WorkSpace.new(self) # Print the code around the binding if show_code is true STDOUT.print(workspace.code_around_binding) if show_code # Get the original IRB instance debugger_irb = IRB.instance_variable_get(:@debugger_irb) irb_path = File.expand_path(source_location[0]) if debugger_irb # If we're already in a debugger session, set the workspace and irb_path for the original IRB instance debugger_irb.context.workspace = workspace debugger_irb.context.irb_path = irb_path # If we've started a debugger session and hit another binding.irb, we don't want to start an IRB session # instead, we want to resume the irb:rdbg session. IRB::Debug.setup(debugger_irb) IRB::Debug.insert_debug_break debugger_irb.debug_break else # If we're not in a debugger session, create a new IRB instance with the current workspace binding_irb = IRB::Irb.new(workspace) binding_irb.context.irb_path = irb_path binding_irb.run(IRB.conf) binding_irb.debug_break end end
如果局部变量 symbol
存在,则返回 true
。
def foo a = 1 binding.local_variable_defined?(:a) #=> true binding.local_variable_defined?(:b) #=> false end
此方法是以下代码的简短版本
binding.eval("defined?(#{symbol}) == 'local-variable'")
static VALUE bind_local_variable_defined_p(VALUE bindval, VALUE sym) { ID lid = check_local_id(bindval, &sym); const rb_binding_t *bind; const rb_env_t *env; if (!lid) return Qfalse; GetBindingPtr(bindval, bind); env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block)); return RBOOL(get_local_variable_ptr(&env, lid)); }
返回局部变量 symbol
的值。
def foo a = 1 binding.local_variable_get(:a) #=> 1 binding.local_variable_get(:b) #=> NameError end
此方法是以下代码的简短版本
binding.eval("#{symbol}")
static VALUE bind_local_variable_get(VALUE bindval, VALUE sym) { ID lid = check_local_id(bindval, &sym); const rb_binding_t *bind; const VALUE *ptr; const rb_env_t *env; if (!lid) goto undefined; GetBindingPtr(bindval, bind); env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block)); if ((ptr = get_local_variable_ptr(&env, lid)) != NULL) { return *ptr; } sym = ID2SYM(lid); undefined: rb_name_err_raise("local variable `%1$s' is not defined for %2$s", bindval, sym); UNREACHABLE_RETURN(Qundef); }
Set
局部变量名为 symbol
的 obj
。
def foo a = 1 bind = binding bind.local_variable_set(:a, 2) # set existing local variable `a' bind.local_variable_set(:b, 3) # create new local variable `b' # `b' exists only in binding p bind.local_variable_get(:a) #=> 2 p bind.local_variable_get(:b) #=> 3 p a #=> 2 p b #=> NameError end
此方法的行为与以下代码类似
binding.eval("#{symbol} = #{obj}")
如果 obj
可以转储为 Ruby 代码。
static VALUE bind_local_variable_set(VALUE bindval, VALUE sym, VALUE val) { ID lid = check_local_id(bindval, &sym); rb_binding_t *bind; const VALUE *ptr; const rb_env_t *env; if (!lid) lid = rb_intern_str(sym); GetBindingPtr(bindval, bind); env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block)); if ((ptr = get_local_variable_ptr(&env, lid)) == NULL) { /* not found. create new env */ ptr = rb_binding_add_dynavars(bindval, bind, 1, &lid); env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block)); } #if YJIT_STATS rb_yjit_collect_binding_set(); #endif RB_OBJ_WRITE(env, ptr, val); return val; }
返回绑定局部变量的名称作为符号。
def foo a = 1 2.times do |n| binding.local_variables #=> [:a, :n] end end
此方法是以下代码的简短版本
binding.eval("local_variables")
static VALUE bind_local_variables(VALUE bindval) { const rb_binding_t *bind; const rb_env_t *env; GetBindingPtr(bindval, bind); env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block)); return rb_vm_env_local_variables(env); }
返回绑定对象的绑定接收器。
static VALUE bind_receiver(VALUE bindval) { const rb_binding_t *bind; GetBindingPtr(bindval, bind); return vm_block_self(&bind->block); }
返回绑定对象的 Ruby 源文件名和行号。
static VALUE bind_location(VALUE bindval) { VALUE loc[2]; const rb_binding_t *bind; GetBindingPtr(bindval, bind); loc[0] = pathobj_path(bind->pathobj); loc[1] = INT2FIX(bind->first_lineno); return rb_ary_new4(2, loc); }