Ruby 默认提供了define_method
等工具用于动态定义实例方法,但貌似没提供动态定义类的方法。
所谓动态定义类,不是指动态创建类,而是指类名是用字符串或符号动态给定的。看完《Ruby 元编程》后,我尝试着自己实现一个:
module Kernel
def define_class(name, ancestor = Object)
Object.const_set(name, Class.new(ancestor))
Object.const_get(name).class_eval(&Proc.new) if block_given?
Object.const_get(name) # return defined class always
end
end
你可能会困惑,动态定义类有什么用?我遇到的一个应用场景就是用在ActiveRecord
同时访问多个数据库时,需要定义多个ActiveRecord::Base
的子类,如下:
def LocalBase < ActiveRecord::Base
self.abstract_class = true
establish_connection adapter: "sqlite3", database: "local.db"
end
def RemoteBase < ActiveRecord::Base
self.abstract_class = true
establish_connection adapter: "sqlite3", database: "remote.db"
end
可以看出里面有重复的代码,使用define_class
就能规避这些重复的代码:
YAML.load(File.read("db.yaml")).each do |name, info|
define_class(name, ActiveRecord::Base) do
self.abstract_class = true
establish_connection info
end
end
同时,我把数据库连接信息移到了db.yaml
文件中:
LocalBase:
adapter: sqlite3
database: local.db
RemoteBase:
adapter: sqlite3
database: remote.db