Ruby 元编程深入解析
原创2024/1/8大约 3 分钟
Ruby 元编程深入解析
Ruby 的元编程能力让代码更加灵活和强大,理解元编程是掌握 Ruby 的关键。
什么是元编程
元编程是指编写能够编写代码的代码,Ruby 提供了丰富的元编程特性。
对象模型
类和对象
class Person
attr_accessor :name
def initialize(name)
@name = name
end
end
person = Person.new("Alice")
# 每个对象都有一个类
person.class # => Person
# 类也是对象
Person.class # => Class
# 查看祖先链
Person.ancestors # => [Person, Object, Kernel, BasicObject]方法查找
class Animal
def speak
"Some sound"
end
end
class Dog < Animal
def speak
"Woof!"
end
end
dog = Dog.new
# 方法查找路径: dog -> Dog -> Animal -> Object -> Kernel -> BasicObject
dog.speak # => "Woof!"动态方法
define_method
class DynamicMethods
[: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
obj = DynamicMethods.new
obj.name = "Alice"
obj.name # => "Alice"method_missing
class OpenStruct
def initialize
@attributes = {}
end
def method_missing(method_name, *args)
if method_name.to_s.end_with?('=')
attribute = method_name.to_s.chop
@attributes[attribute] = args.first
elsif @attributes.key?(method_name.to_s)
@attributes[method_name.to_s]
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
method_name.to_s.end_with?('=') ||
@attributes.key?(method_name.to_s) ||
super
end
end
obj = OpenStruct.new
obj.name = "Bob"
obj.name # => "Bob"类宏
attr_accessor 的实现
class Module
def my_attr_accessor(*attributes)
attributes.each do |attribute|
# Getter
define_method(attribute) do
instance_variable_get("@#{attribute}")
end
# Setter
define_method("#{attribute}=") do |value|
instance_variable_set("@#{attribute}", value)
end
end
end
end
class Person
my_attr_accessor :name, :age
end自定义类宏
class ActiveRecord::Base
def self.validates_presence_of(*attributes)
attributes.each do |attribute|
define_method("valid_#{attribute}?") do
!send(attribute).nil? && !send(attribute).empty?
end
end
end
end
class User < ActiveRecord::Base
validates_presence_of :name, :email
end代码块和闭包
instance_eval 和 class_eval
class Person
def initialize
@name = "Alice"
end
end
person = Person.new
# instance_eval - 在对象上下文中执行
person.instance_eval do
puts @name # => "Alice"
@age = 25
end
# class_eval - 在类上下文中执行
Person.class_eval do
def greet
"Hello, I'm #{@name}"
end
end
person.greet # => "Hello, I'm Alice"DSL 设计
class Route
def initialize(&block)
@routes = []
instance_eval(&block)
end
def get(path, &handler)
@routes << { method: :get, path: path, handler: handler }
end
def post(path, &handler)
@routes << { method: :post, path: path, handler: handler }
end
def routes
@routes
end
end
# 使用 DSL
routes = Route.new do
get '/users' do
# 处理逻辑
end
post '/users' do
# 处理逻辑
end
endHook 方法
inherited
class BaseController
def self.inherited(subclass)
puts "#{subclass} inherited from #{self}"
subclass.instance_variable_set(:@actions, [])
end
def self.action(name)
@actions ||= []
@actions << name
end
end
class UsersController < BaseController
action :index
action :show
end
# 输出: UsersController inherited from BaseControllerincluded
module Logging
def self.included(base)
base.extend(ClassMethods)
base.include(InstanceMethods)
end
module ClassMethods
def log_calls_to(*methods)
methods.each do |method|
original_method = instance_method(method)
define_method(method) do |*args|
puts "Calling #{method}"
original_method.bind(self).call(*args)
end
end
end
end
module InstanceMethods
def log(message)
puts "[LOG] #{message}"
end
end
end
class MyClass
include Logging
def do_something
log "Doing something"
end
log_calls_to :do_something
end单例类
obj = "hello"
# 给单个对象添加方法
def obj.shout
upcase + "!"
end
obj.shout # => "HELLO!"
# 其他字符串对象没有这个方法
"world".shout # => NoMethodError
# 访问单例类
singleton_class = class << obj
self
end
singleton_class.instance_methods(false) # => [:shout]实用示例
自动属性追踪
module AttributeTracker
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def track_changes_to(*attributes)
attributes.each do |attribute|
define_method("#{attribute}=") do |value|
old_value = instance_variable_get("@#{attribute}")
instance_variable_set("@#{attribute}", value)
@changes ||= {}
@changes[attribute] = [old_value, value] if old_value != value
end
define_method(attribute) do
instance_variable_get("@#{attribute}")
end
end
define_method(:changes) do
@changes || {}
end
end
end
end
class User
include AttributeTracker
track_changes_to :name, :email
end
user = User.new
user.name = "Alice"
user.name = "Bob"
user.changes # => {:name=>["Alice", "Bob"]}最佳实践
- 谨慎使用: 元编程很强大,但也容易滥用
- 保持可读性: 不要为了炫技而使用元编程
- 文档化: 元编程代码需要更好的文档
- 性能考虑: method_missing 等特性有性能开销
- 测试覆盖: 元编程代码需要充分测试
总结
Ruby 的元编程能力让代码更加灵活和简洁,但需要谨慎使用。理解对象模型和方法查找机制是掌握元编程的基础。