Ruby元编程Skill ruby-metaprogramming

掌握Ruby元编程技能,用于动态代码生成、方法处理、反射和增强代码灵活性,适用于后端开发和高级Ruby编程。关键词:Ruby、元编程、动态方法、反射、代码生成、define_method、method_missing。

后端开发 0 次安装 0 次浏览 更新于 3/25/2026

name: ruby-metaprogramming user-invocable: false description: 当使用Ruby元编程特性时使用,包括动态方法定义、method_missing、class_eval、define_method和反射。 allowed-tools:

  • Bash
  • Read
  • Write
  • Edit

Ruby 元编程

掌握Ruby强大的元编程能力,编写编写代码的代码。Ruby的动态性使其在元编程方面特别出色。

动态方法定义

define_method

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 和 instance_eval

# class_eval - 在类的上下文中评估代码
class MyClass
end

MyClass.class_eval do
  def hello
    "Hello from class_eval"
  end
end

puts MyClass.new.hello

# instance_eval - 在实例的上下文中评估代码
obj = Object.new
obj.instance_eval do
  def greet
    "Hello from instance_eval"
  end
end

puts obj.greet

module_eval

module MyModule
end

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

puts MyModule.info

方法缺失

基本 method_missing

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}

常量缺失

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 和 public_send

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

  private

  def secret_method
    "This is private"
  end
end

calc = Calculator.new

# send 可以调用任何方法(包括私有方法)
puts calc.send(:add, 3, 4)           # 7
puts calc.send(:secret_method)       # "This is private"

# public_send 只调用公共方法
puts calc.public_send(:add, 3, 4)    # 7
# calc.public_send(:secret_method)   # NoMethodError

类宏

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"]

单例方法

obj = "hello"

# 在单个实例上定义方法
def obj.shout
  self.upcase + "!!!"
end

puts obj.shout  # "HELLO!!!"

# 使用 define_singleton_method
obj.define_singleton_method(:whisper) do
  self.downcase + "..."
end

puts obj.whisper  # "hello..."

特征类(单例类)

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

# 访问特征类
eigenclass = class << Person
  self
end

puts eigenclass  # #<Class:Person>

# 通过特征类添加类方法
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

反射和内省

对象内省

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

obj = MyClass.new

# 列出方法
puts obj.methods.include?(:public_method)
puts obj.private_methods.include?(:private_method)
puts obj.protected_methods.include?(:protected_method)

# 检查方法是否存在
puts obj.respond_to?(:public_method)       # true
puts obj.respond_to?(:private_method)      # false
puts obj.respond_to?(:private_method, true) # true(包括私有方法)

# 获取方法对象
method = obj.method(:public_method)
puts method.class  # Method

类内省

class Parent
  def parent_method; end
end

class Child < Parent
  def child_method; end
end

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

# 实例方法
puts Child.instance_methods(false)  # 仅Child的方法

# 类变量和实例变量
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]

钩子方法

继承钩子

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
# 输出:ChildClass inherited from BaseClass

方法钩子

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 和 extended

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 创建

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 使用
builder = RouteBuilder.new
builder.instance_eval do
  get "/users" do
    "List of users"
  end

  post "/users" do
    "Create user"
  end
end

puts builder.routes

动态类创建

# 动态创建类
MyClass = Class.new do
  define_method :greet do
    "Hello from dynamic class"
  end
end

puts MyClass.new.greet

# 创建带继承的类
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

对象扩展

module Greetable
  def greet
    "Hello!"
  end
end

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

# 仅此实例有该方法
another_obj = Object.new
# another_obj.greet  # NoMethodError

绑定和eval

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

b = get_binding("test")

# 在绑定上下文中评估代码
puts eval("param", b)      # "test"
puts eval("local_var", b)  # "local value"

# instance_eval 与绑定
class MyClass
  def initialize
    @value = 42
  end
end

obj = MyClass.new
puts obj.instance_eval { @value }  # 42

TracePoint

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

最佳实践

  1. 谨慎使用元编程 - 它可能使代码难以理解
  2. 使用 method_missing 时始终实现 respond_to_missing?
  3. 可能时优先使用 define_method 而不是 class_eval
  4. 大量文档化元编程 - 不清楚发生了什么
  5. 使用 public_send 而不是 send 以尊重可见性
  6. 缓存元编程的方法 以避免重复定义
  7. 彻底测试元编程代码 - 错误可能很细微

反模式

不要过度使用 method_missing - 它速度慢且难以调试 ❌ 不要将 eval 与用户输入一起使用 - 主要安全风险 ❌ 当简单代码有效时不要使用元编程 - 清晰度胜过聪明 ❌ 不要忘记在 method_missing 中调用 super不要创建没有文档的方法 - IDE 支持中断

常见用例

  • ORMs(ActiveRecord) - 动态查找器、关联
  • DSLs - 路由定义、配置
  • 装饰器 - 方法包装和增强
  • 模拟/存根 - 测试框架
  • 属性定义 - 带有行为的自定义访问器

相关技能

  • ruby-oop - 理解类和模块
  • ruby-blocks-procs-lambdas - 用于回调和动态行为
  • ruby-gems - 许多gem广泛使用元编程