GGistDev

Scope and Binding in Ruby

Understand self, visibility, lexical scope, and bindings.

self and execution context

self is the current receiver; it changes across class, module, and method bodies.

self          # top-level => main
class C
  self       # => C (inside class body)
  def who
    self    # => instance of C
  end
end

Method visibility

public (default), protected (callable by instances of same class), private (no explicit receiver).

class Account
  def transfer_to(other, amt)
    debit(amt)
    other.credit(amt)   # allowed: protected
  end

  protected
  def credit(amt); @bal += amt end

  private
  def debit(amt); @bal -= amt end
end

Lexical scope and closures

Blocks/procs capture surrounding locals; methods do not capture outer locals.

prefix = "[x]"
["a","b"].map { |s| "#{prefix} #{s}" }

Binding and eval (use sparingly)

binding captures local scope; eval/instance_eval execute code in a context.

b = binding
x = 1
eval("x + 1", b)  # => 2

obj = "str"
obj.instance_eval { self.upcase }   # => "STR"

class_eval and define_method

Evaluate code in a class/module context or define methods dynamically.

class Model
end

Model.class_eval do
  define_method(:id) { @id ||= 0 }
end

Constant lookup and Module.nesting

Lexical first, then ancestors; use :: for explicit lookup.

module A; X = 1; end
module B; X = 2; end
module A; module C; end; end
A::C.module_eval { Module.nesting }  # => [A::C, A]

Summary

  • self identifies the current receiver and changes with context
  • Visibility controls how methods are invoked
  • Closures capture locals; binding/instance_eval are powerful but use sparingly
  • Constants resolve lexically first, then via ancestors