類別 Binding

類別 Binding 的物件封裝了程式碼中特定位置的執行內容,並保留此內容以供未來使用。可以在此內容中存取的變數、方法、self 的值,以及可能的迭代區塊,都會被保留下來。可以使用 Kernel#binding 建立 Binding 物件,並提供給 Kernel#set_trace_func 的 callback 和 TracePoint 的執行個體。

這些 binding 物件可以傳遞為 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 物件沒有類別特定的方法。

公開執行個體方法

eval(string [, filename [,lineno]]) → obj 按一下以切換來源

binding 的內容中評估 string 中的 Ruby 運算式。如果存在選擇性的 filenamelineno 參數,它們會在回報語法錯誤時使用。

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(show_code: true) 按一下以切換來源

開啟 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
local_variable_defined?(symbol) → obj 按一下以切換來源

如果存在一個名為 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));
}
local_variable_get(symbol) → obj 按一下以切換來源

傳回局部變數 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);
}
local_variable_set(symbol, obj) → obj 按一下以切換來源

將名為 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;
}
local_variables → Array 按一下以切換來源

傳回繫結的局部變數名稱,作為符號。

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);
}
receiver → object 按一下以切換來源

傳回繫結物件的繫結接收器。

static VALUE
bind_receiver(VALUE bindval)
{
    const rb_binding_t *bind;
    GetBindingPtr(bindval, bind);
    return vm_block_self(&bind->block);
}
source_location → [String, Integer] 按一下以切換來源

傳回繫結物件的 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);
}