類別 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
物件沒有類別特定的方法。
公開執行個體方法
在 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); }
將名為 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); }