ruby-metaprogramming

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Ruby Metaprogramming

Ruby元编程

Master Ruby's powerful metaprogramming capabilities to write code that writes code. Ruby's dynamic nature makes it exceptionally good at metaprogramming.
掌握Ruby强大的元编程能力,编写能生成代码的代码。Ruby的动态特性使其在元编程方面表现格外出色。

Dynamic Method Definition

动态方法定义

define_method

define_method

ruby
class Person
  [:name, :age, :email].each do |attribute|
    define_method(attribute) do
      instance_variable_get("@#{attribute}")
    end

    define_method("#{attribute}=") do |value|
      instance_variable_set("@#{attribute}", value)
    end
  end
end

person = Person.new
person.name = "Alice"
puts person.name  # "Alice"
ruby
class Person
  [:name, :age, :email].each do |attribute|
    define_method(attribute) do
      instance_variable_get("@#{attribute}")
    end

    define_method("#{attribute}=") do |value|
      instance_variable_set("@#{attribute}", value)
    end
  end
end

person = Person.new
person.name = "Alice"
puts person.name  # "Alice"

class_eval and instance_eval

class_eval 和 instance_eval

ruby
undefined
ruby
undefined

class_eval - Evaluates code in context of a class

class_eval - 在类的上下文中执行代码

class MyClass end
MyClass.class_eval do def hello "Hello from class_eval" end end
puts MyClass.new.hello
class MyClass end
MyClass.class_eval do def hello "Hello from class_eval" end end
puts MyClass.new.hello

instance_eval - Evaluates code in context of an instance

instance_eval - 在实例的上下文中执行代码

obj = Object.new obj.instance_eval do def greet "Hello from instance_eval" end end
puts obj.greet
undefined
obj = Object.new obj.instance_eval do def greet "Hello from instance_eval" end end
puts obj.greet
undefined

module_eval

module_eval

ruby
module MyModule
end

MyModule.module_eval do
  def self.info
    "Module metaprogramming"
  end
end

puts MyModule.info
ruby
module MyModule
end

MyModule.module_eval do
  def self.info
    "Module metaprogramming"
  end
end

puts MyModule.info

Method Missing

Method Missing

Basic method_missing

基础method_missing

ruby
class DynamicFinder
  def initialize(data)
    @data = data
  end

  def method_missing(method_name, *args)
    if method_name.to_s.start_with?("find_by_")
      attribute = method_name.to_s.sub("find_by_", "")
      @data.find { |item| item[attribute.to_sym] == args.first }
    else
      super
    end
  end

  def respond_to_missing?(method_name, include_private = false)
    method_name.to_s.start_with?("find_by_") || super
  end
end

users = [
  { name: "Alice", age: 30 },
  { name: "Bob", age: 25 }
]

finder = DynamicFinder.new(users)
puts finder.find_by_name("Alice")  # {:name=>"Alice", :age=>30}
ruby
class DynamicFinder
  def initialize(data)
    @data = data
  end

  def method_missing(method_name, *args)
    if method_name.to_s.start_with?("find_by_")
      attribute = method_name.to_s.sub("find_by_", "")
      @data.find { |item| item[attribute.to_sym] == args.first }
    else
      super
    end
  end

  def respond_to_missing?(method_name, include_private = false)
    method_name.to_s.start_with?("find_by_") || super
  end
end

users = [
  { name: "Alice", age: 30 },
  { name: "Bob", age: 25 }
]

finder = DynamicFinder.new(users)
puts finder.find_by_name("Alice")  # {:name=>"Alice", :age=>30}

Const Missing

Const Missing

ruby
class DynamicConstants
  def self.const_missing(const_name)
    puts "Constant #{const_name} not found, creating it..."
    const_set(const_name, "Dynamic value for #{const_name}")
  end
end

puts DynamicConstants::SOMETHING  # "Dynamic value for SOMETHING"
ruby
class DynamicConstants
  def self.const_missing(const_name)
    puts "Constant #{const_name} not found, creating it..."
    const_set(const_name, "Dynamic value for #{const_name}")
  end
end

puts DynamicConstants::SOMETHING  # "Dynamic value for SOMETHING"

send and public_send

send 和 public_send

ruby
class Calculator
  def add(x, y)
    x + y
  end

  private

  def secret_method
    "This is private"
  end
end

calc = Calculator.new
ruby
class Calculator
  def add(x, y)
    x + y
  end

  private

  def secret_method
    "This is private"
  end
end

calc = Calculator.new

send can call any method (including private)

send 可以调用任意方法(包括私有方法)

puts calc.send(:add, 3, 4) # 7 puts calc.send(:secret_method) # "This is private"
puts calc.send(:add, 3, 4) # 7 puts calc.send(:secret_method) # "This is private"

public_send only calls public methods

public_send 仅调用公共方法

puts calc.public_send(:add, 3, 4) # 7
puts calc.public_send(:add, 3, 4) # 7

calc.public_send(:secret_method) # NoMethodError

calc.public_send(:secret_method) # NoMethodError

undefined
undefined

Class Macros

类宏

ruby
class ActiveModel
  def self.attr_with_history(attribute)
    define_method(attribute) do
      instance_variable_get("@#{attribute}")
    end

    define_method("#{attribute}=") do |value|
      history = instance_variable_get("@#{attribute}_history") || []
      history << value
      instance_variable_set("@#{attribute}_history", history)
      instance_variable_set("@#{attribute}", value)
    end

    define_method("#{attribute}_history") do
      instance_variable_get("@#{attribute}_history") || []
    end
  end
end

class Person < ActiveModel
  attr_with_history :name
end

person = Person.new
person.name = "Alice"
person.name = "Alicia"
puts person.name_history.inspect  # ["Alice", "Alicia"]
ruby
class ActiveModel
  def self.attr_with_history(attribute)
    define_method(attribute) do
      instance_variable_get("@#{attribute}")
    end

    define_method("#{attribute}=") do |value|
      history = instance_variable_get("@#{attribute}_history") || []
      history << value
      instance_variable_set("@#{attribute}_history", history)
      instance_variable_set("@#{attribute}", value)
    end

    define_method("#{attribute}_history") do
      instance_variable_get("@#{attribute}_history") || []
    end
  end
end

class Person < ActiveModel
  attr_with_history :name
end

person = Person.new
person.name = "Alice"
person.name = "Alicia"
puts person.name_history.inspect  # ["Alice", "Alicia"]

Singleton Methods

单例方法

ruby
obj = "hello"
ruby
obj = "hello"

Define method on single instance

为单个实例定义方法

def obj.shout self.upcase + "!!!" end
puts obj.shout # "HELLO!!!"
def obj.shout self.upcase + "!!!" end
puts obj.shout # "HELLO!!!"

Using define_singleton_method

使用 define_singleton_method

obj.define_singleton_method(:whisper) do self.downcase + "..." end
puts obj.whisper # "hello..."
undefined
obj.define_singleton_method(:whisper) do self.downcase + "..." end
puts obj.whisper # "hello..."
undefined

Eigenclass (Singleton Class)

Eigenclass(单例类)

ruby
class Person
  def self.species
    "Homo sapiens"
  end
end
ruby
class Person
  def self.species
    "Homo sapiens"
  end
end

Accessing eigenclass

访问 eigenclass

eigenclass = class << Person self end
puts eigenclass # #Class:Person
eigenclass = class << Person self end
puts eigenclass # #Class:Person

Adding class methods via eigenclass

通过 eigenclass 添加类方法

class Person class << self def count @@count ||= 0 end
def increment_count
  @@count ||= 0
  @@count += 1
end
end end
Person.increment_count puts Person.count # 1
undefined
class Person class << self def count @@count ||= 0 end
def increment_count
  @@count ||= 0
  @@count += 1
end
end end
Person.increment_count puts Person.count # 1
undefined

Reflection and Introspection

反射与自省

Object Introspection

对象自省

ruby
class MyClass
  def public_method; end
  protected
  def protected_method; end
  private
  def private_method; end
end

obj = MyClass.new
ruby
class MyClass
  def public_method; end
  protected
  def protected_method; end
  private
  def private_method; end
end

obj = MyClass.new

List methods

列出方法

puts obj.methods.include?(:public_method) puts obj.private_methods.include?(:private_method) puts obj.protected_methods.include?(:protected_method)
puts obj.methods.include?(:public_method) puts obj.private_methods.include?(:private_method) puts obj.protected_methods.include?(:protected_method)

Check method existence

检查方法是否存在

puts obj.respond_to?(:public_method) # true puts obj.respond_to?(:private_method) # false puts obj.respond_to?(:private_method, true) # true (include private)
puts obj.respond_to?(:public_method) # true puts obj.respond_to?(:private_method) # false puts obj.respond_to?(:private_method, true) # true (包含私有方法)

Get method object

获取方法对象

method = obj.method(:public_method) puts method.class # Method
undefined
method = obj.method(:public_method) puts method.class # Method
undefined

Class Introspection

类自省

ruby
class Parent
  def parent_method; end
end

class Child < Parent
  def child_method; end
end
ruby
class Parent
  def parent_method; end
end

class Child < Parent
  def child_method; end
end

Inheritance chain

继承链

puts Child.ancestors # [Child, Parent, Object, Kernel, BasicObject]
puts Child.ancestors # [Child, Parent, Object, Kernel, BasicObject]

Instance methods

实例方法

puts Child.instance_methods(false) # Only Child's methods
puts Child.instance_methods(false) # 仅Child的方法

Class variables and instance variables

类变量和实例变量

class Person @@count = 0 def initialize(name) @name = name end end
puts Person.class_variables # [:@@count] person = Person.new("Alice") puts person.instance_variables # [:@name]
undefined
class Person @@count = 0 def initialize(name) @name = name end end
puts Person.class_variables # [:@@count] person = Person.new("Alice") puts person.instance_variables # [:@name]
undefined

Hook Methods

钩子方法

Inheritance Hooks

继承钩子

ruby
class BaseClass
  def self.inherited(subclass)
    puts "#{subclass} inherited from #{self}"
    subclass.instance_variable_set(:@inherited_at, Time.now)
  end
end

class ChildClass < BaseClass
end
ruby
class BaseClass
  def self.inherited(subclass)
    puts "#{subclass} inherited from #{self}"
    subclass.instance_variable_set(:@inherited_at, Time.now)
  end
end

class ChildClass < BaseClass
end

Output: ChildClass inherited from BaseClass

输出: ChildClass inherited from BaseClass

undefined
undefined

Method Hooks

方法钩子

ruby
module Monitored
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def method_added(method_name)
      puts "Method #{method_name} was added to #{self}"
    end

    def method_removed(method_name)
      puts "Method #{method_name} was removed from #{self}"
    end
  end
end

class MyClass
  include Monitored

  def my_method
  end
  # Output: Method my_method was added to MyClass
end
ruby
module Monitored
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def method_added(method_name)
      puts "Method #{method_name} was added to #{self}"
    end

    def method_removed(method_name)
      puts "Method #{method_name} was removed from #{self}"
    end
  end
end

class MyClass
  include Monitored

  def my_method
  end
  # 输出: Method my_method was added to MyClass
end

included and extended

included 和 extended

ruby
module MyModule
  def self.included(base)
    puts "#{self} included in #{base}"
    base.extend(ClassMethods)
  end

  def self.extended(base)
    puts "#{self} extended by #{base}"
  end

  module ClassMethods
    def class_method
      "I'm a class method"
    end
  end

  def instance_method
    "I'm an instance method"
  end
end

class MyClass
  include MyModule  # Adds instance_method as instance method
end

class AnotherClass
  extend MyModule   # Adds instance_method as class method
end
ruby
module MyModule
  def self.included(base)
    puts "#{self} included in #{base}"
    base.extend(ClassMethods)
  end

  def self.extended(base)
    puts "#{self} extended by #{base}"
  end

  module ClassMethods
    def class_method
      "I'm a class method"
    end
  end

  def instance_method
    "I'm an instance method"
  end
end

class MyClass
  include MyModule  # 将instance_method添加为实例方法
end

class AnotherClass
  extend MyModule   # 将instance_method添加为类方法
end

DSL Creation

DSL创建

ruby
class RouteBuilder
  def initialize
    @routes = {}
  end

  def get(path, &block)
    @routes[path] = { method: :get, handler: block }
  end

  def post(path, &block)
    @routes[path] = { method: :post, handler: block }
  end

  def routes
    @routes
  end
end
ruby
class RouteBuilder
  def initialize
    @routes = {}
  end

  def get(path, &block)
    @routes[path] = { method: :get, handler: block }
  end

  def post(path, &block)
    @routes[path] = { method: :post, handler: block }
  end

  def routes
    @routes
  end
end

DSL usage

DSL使用示例

builder = RouteBuilder.new builder.instance_eval do get "/users" do "List of users" end
post "/users" do "Create user" end end
puts builder.routes
undefined
builder = RouteBuilder.new builder.instance_eval do get "/users" do "List of users" end
post "/users" do "Create user" end end
puts builder.routes
undefined

Dynamic Class Creation

动态类创建

ruby
undefined
ruby
undefined

Create class dynamically

动态创建类

MyClass = Class.new do define_method :greet do "Hello from dynamic class" end end
puts MyClass.new.greet
MyClass = Class.new do define_method :greet do "Hello from dynamic class" end end
puts MyClass.new.greet

Create class with inheritance

创建带继承关系的类

Parent = Class.new do def parent_method "From parent" end end
Child = Class.new(Parent) do def child_method "From child" end end
child = Child.new puts child.parent_method puts child.child_method
undefined
Parent = Class.new do def parent_method "From parent" end end
Child = Class.new(Parent) do def child_method "From child" end end
child = Child.new puts child.parent_method puts child.child_method
undefined

Object Extension

对象扩展

ruby
module Greetable
  def greet
    "Hello!"
  end
end

obj = Object.new
obj.extend(Greetable)
puts obj.greet  # "Hello!"
ruby
module Greetable
  def greet
    "Hello!"
  end
end

obj = Object.new
obj.extend(Greetable)
puts obj.greet  # "Hello!"

Only this instance has the method

只有这个实例拥有该方法

another_obj = Object.new
another_obj = Object.new

another_obj.greet # NoMethodError

another_obj.greet # NoMethodError

undefined
undefined

Binding and eval

Binding 和 eval

ruby
def get_binding(param)
  local_var = "local value"
  binding
end

b = get_binding("test")
ruby
def get_binding(param)
  local_var = "local value"
  binding
end

b = get_binding("test")

Evaluate code in the binding context

在binding上下文中执行代码

puts eval("param", b) # "test" puts eval("local_var", b) # "local value"
puts eval("param", b) # "test" puts eval("local_var", b) # "local value"

instance_eval with binding

结合binding使用instance_eval

class MyClass def initialize @value = 42 end end
obj = MyClass.new puts obj.instance_eval { @value } # 42
undefined
class MyClass def initialize @value = 42 end end
obj = MyClass.new puts obj.instance_eval { @value } # 42
undefined

TracePoint

TracePoint

ruby
trace = TracePoint.new(:call, :return) do |tp|
  puts "#{tp.event}: #{tp.method_id} in #{tp.defined_class}"
end

trace.enable

def my_method
  "Hello"
end

my_method

trace.disable
ruby
trace = TracePoint.new(:call, :return) do |tp|
  puts "#{tp.event}: #{tp.method_id} in #{tp.defined_class}"
end

trace.enable

def my_method
  "Hello"
end

my_method

trace.disable

Best Practices

最佳实践

  1. Use metaprogramming sparingly - it can make code hard to understand
  2. Always implement respond_to_missing? when using method_missing
  3. Prefer define_method over class_eval when possible
  4. Document metaprogramming heavily - it's not obvious what's happening
  5. Use public_send over send to respect visibility
  6. Cache metaprogrammed methods to avoid repeated definition
  7. Test metaprogrammed code thoroughly - bugs can be subtle
  1. 谨慎使用元编程 - 它会让代码难以理解
  2. 使用method_missing时务必实现respond_to_missing?
  3. 可能的话优先使用define_method而非class_eval
  4. 为元编程代码添加详细文档 - 其逻辑并不直观
  5. 优先使用public_send而非send 以遵循可见性规则
  6. 缓存元编程生成的方法 避免重复定义
  7. 全面测试元编程代码 - 潜在的bug可能很隐蔽

Anti-Patterns

反模式

Don't overuse method_missing - it's slow and hard to debug ❌ Don't use eval with user input - major security risk ❌ Don't metaprogram when simple code works - clarity over cleverness ❌ Don't forget to call super in method_missing ❌ Don't create methods without documenting them - IDE support breaks
不要过度使用method_missing - 速度慢且难以调试 ❌ 不要将eval与用户输入结合使用 - 存在重大安全风险 ❌ 能用简单代码实现时不要用元编程 - 清晰胜于技巧 ❌ 在method_missing中不要忘记调用super不要在未文档化的情况下创建方法 - 会破坏IDE支持

Common Use Cases

常见用例

  • ORMs (ActiveRecord) - Dynamic finders, associations
  • DSLs - Route definitions, configurations
  • Decorators - Method wrapping and enhancement
  • Mocking/Stubbing - Test frameworks
  • Attribute definition - Custom accessors with behavior
  • ORM框架(如ActiveRecord)- 动态查找器、关联关系
  • DSL - 路由定义、配置
  • 装饰器 - 方法包装与增强
  • Mock/Stub - 测试框架
  • 属性定义 - 带自定义行为的访问器

Related Skills

相关技能

  • ruby-oop - Understanding classes and modules
  • ruby-blocks-procs-lambdas - For callbacks and dynamic behavior
  • ruby-gems - Many gems use metaprogramming extensively
  • ruby-oop - 理解类与模块
  • ruby-blocks-procs-lambdas - 用于回调和动态行为
  • ruby-gems - 许多gem都大量使用元编程