Ruby元编程实战
Json解析成对象,将属性动态定义方法
module ActsAsField
def self.included(base_class)
base_class.instance_variable_set(:@all_fields, [])
base_class.include Instance_Methods
base_class.extend Class_Methods
end
module Class_Methods
# 声明变量信息,并自动生成实例方法
def field(name, path)
instance_variable_get(:@all_fields) << name.to_sym
define_method(name.to_sym) do
case path
when String
data.dig(*path.split('.').map(&:to_sym))
when Proc
path.call(self)
end
end
end
def all_fields
instance_variable_get :@all_fields
end
end
module Instance_Methods
end
end
class Device
include ActsAsField
field :name, 'name'
field :age, 'age'
field :work_city, 'deep_info.work_city'
field :log, lambda { |source|
"#{source.name} - #{source.age}"
}
def data
# 也可以将data通过外部传入,在initialize时声明
{
name: 'lucas',
age: 24,
sex: :male,
deep_info: {
work_city: :sh
}
}
end
end
device = Device.new
p device.name
p device.age
p device.work_city
p device.log
p Device.all_fields
模拟 ActiveSupport::Concern
module MyConcern
def self.extended(base)
# 给具体的Concern设置一个类实例变量,来存储对应的依赖
base.instance_variable_set(:@dependents, [])
end
# 重写 Module#append_features 方法
# 在A模块儿被B include 时,Ruby会自动执行 A#append_features, 并传入B
# @param base [Module] include模块儿的类.
def append_features(base)
if base.instance_variable_defined?(:@dependents)
# 如果定义过说明 base 本身是一个Concern,自己是其中的一个依赖项
base.instance_variable_get(:@dependents) << self
# 不现在进行添加
return false
else
# 如果 base 是当前类的子类,则不添加,因为已经被继承了
return false if base < self
@dependents.each { |dep| base.include dep }
super
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
end
base.class_exec(&@include_block) if instance_variable_defined?(:@include_block)
end
def includes(&block)
@include_block = block
end
end
优化第一个实例中的代码
module MyConcern
def self.extended(base)
# 给具体的Concern设置一个类实例变量,来存储对应的依赖
base.instance_variable_set(:@dependents, [])
end
def append_features(base)
if base.instance_variable_defined?(:@dependents)
# 如果定义过说明 base 本身是一个Concern,自己是其中的一个依赖项
base.instance_variable_get(:@dependents) << self
# 不现在进行添加
return false
else
# 如果 base 是当前类的子类,则不添加,因为已经被继承了
return false if base < self
@dependents.each { |dep| base.include dep }
super
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
end
base.class_exec(&@include_block) if instance_variable_defined?(:@include_block)
end
def includes(&block)
@include_block = block
end
end
module ActsAsField
extend MyConcern
includes do
instance_variable_set(:@all_fields, [])
end
module ClassMethods
# 声明变量信息,并自动生成实例方法
def field(name, path)
instance_variable_get(:@all_fields) << name.to_sym
define_method(name.to_sym) do
case path
when String
data.dig(*path.split('.').map(&:to_sym))
when Proc
path.call(self)
end
end
end
def all_fields
instance_variable_get :@all_fields
end
end
end
class Device
include ActsAsField
field :name, 'name'
field :age, 'age'
field :work_city, 'deep_info.work_city'
field :log, lambda { |source|
"#{source.name} - #{source.age}"
}
def data
# 也可以将data通过外部传入,在initialize时声明
{
name: 'lucas',
age: 24,
sex: :male,
deep_info: {
work_city: :sh
}
}
end
end
device = Device.new
p device.name
p device.age
p device.work_city
p device.log
p Device.all_fields
灵活构造SQL查询
class Element
attr_accessor :name, :operator, :value
def initialize(name, operator, value)
@name = name
@operator = operator
@value = value
end
def to_sql
case operator
when '='
["#{name} = ?", value]
when '!='
["#{name} != ?", value]
when '>'
["#{name} > ?", value]
when '<'
["#{name} < ?", value]
when '>='
["#{name} >= ?", value]
when '<='
["#{name} <= ?", value]
when 'like'
["#{name} like ?", value]
when 'not like'
["#{name} not like ?", value]
when 'in'
["#{name} in (?)", value]
when 'not in'
["#{name} not in (?)", value]
else
raise ['Unkown operator', operator].join(': ')
end
end
end
class Elements
attr_accessor :childrens, :operator
def initialize(operator)
@operator = operator
@childrens = []
end
def all_of
element = Elements.new('and')
yield element
@childrens << element
end
def any_of
element = Elements.new('or')
yield element
@childrens << element
end
def field(name, operator, value)
element = Element.new(name, operator, value)
@childrens << element
end
def to_sql
sql = []
params = []
@childrens.each do |e|
r = e.to_sql
r << '0 = 1' if r.size.zero?
sql << r[0]
params += r[1, r.size]
end
# 增减判断防止无条件时生成空括号
params.unshift('(' + sql.join(" #{operator.upcase} ") + ')') unless sql.empty?
params
end
end
conditions = Elements.new('and')
# p "condition: #{conditions.to_sql}"
# conditions.all_of do |e|
# e.field('name', '=', '张三')
# e.field('age', '>', '18')
# end
# p "condition: #{conditions.to_sql}"
# conditions.any_of do |e|
# e.field('name', '=', '张三')
# e.field('age', '>', '18')
# end
# p "condition: #{conditions.to_sql}"
# conditions.field('name', '=', '张三')
# p "condition: #{conditions.to_sql}"
# conditions.all_of do |c|
# c.any_of do |e|
# e.field('name', '=', '张三')
# e.field('age', '>', '18')
# end
# c.field('name', '=', '张三')
# end
# p "condition: #{conditions.to_sql}"
组织功能模块儿,灵活自定义扩展
# 模型
class Product
end
# 搜索框架
module Search
# 定义类方法,对外提供入口
def self.define(model, &block)
# constantize 是 ActiveSupport::Inflector 中定义的
# klass = model.to_s.constantize
klass = model
# 传入自己可能自定义的方法
klass.instance_eval(&block)
# 扩展类通用方法
klass.extend ClassMethos
end
# 通用方法
module ClassMethos
def search
p 'I am searching...'
end
def sort
p 'I am sort...'
end
end
end
# 进行注册,这时候Product就拥有了Search中定义的类方法
Search.define Product do
# 传入一个block,可以自己定义如何操作内容
def sort
p 'I am not sort'
end
end
Product.search
Product.sort
这里其实更简单的方式是,定义通用Module,让别的类模块儿直接include,如果需要定义直接重写方法也能用。理解成本也低。
一定要慎用元编程,否则容易导致理解成本太高的问题
父类定义钩子,子类实现,父类调用
# 模拟Sideque::Worker,面向接口学习
require 'active_support/all'
module Worker
extend ActiveSupport::Concern
class_methods do
def perform_later
new.perform
end
end
end
class UserWorker
include Worker
def perform
p "I want do some thing"
end
end
UserWorker.perform_later
扩展缓存机制
# 学习通过DSL来优雅的进行扩展实现,这里实现更新,删除缓存的功能
require 'active_support/all'
# 使用DSL进行灵活扩展
module CacheExt
extend ActiveSupport::Concern
class_methods do
def cache
yield
end
def refresh(callback_name)
define_method :_refresh_cache do
p '刷新缓存。。。'
yield(self) if block_given?
end
send callback_name, :_refresh_cache
def delete(callback_name)
define_method :_delete_cache do
p '清除缓存。。。'
yield(self) if block_given?
end
send callback_name, :_delete_cache
end
end
end
end
class Product
include CacheExt
cache do
refresh :after_save do |_record|
p '保存后我刷新缓存'
end
delete :after_delete do |_record|
p '删除后我刷新缓存'
end
end
end
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 玲辰书斋!