如何封装一组操作(其中大部分本质上是 .where(...))并将其应用于不同的模型,以使某些模型可能无法实现某些操作并应返回空集合。(非必要代码跳过)
我设计的(但不满意):
class ActivityFinder
def self.find(query, limit = nil)
activities = get_activities(query)
activities = activities.sort_by(&:created_at).reverse // some-kind of merge-sort
activities = activities.slice(0, limit) if limit.present?
activities
end
private
def self.get_activities(query)
activities = []
activities += query.apply(ModelA)
activities += query.apply(ModelB)
activities += query.apply(ModelC)
activities
end
end
class ActivityQuery
def created_before(time)
@created_before = time
self
end
def created_after(time)
@created_after = time
self
end
def apply(activity)
activity = activity.where("#{activity.table_name}.created_at < ?", @created_before) if @created_before.present?
activity = activity.where("#{activity.table_name}.created_at >= ?", @created_after) if @created_after.present?
// more operations, not all of them are suported by ModelC
rescue NoMethodError
return []
end
end
用法
query = ActivityQuery.new.created_before(last_time).with_hash(...)
activities = ActivityFinder.find(query)
我不喜欢的:
- NoMethodError 的救援
- 如果不同的模型对字段有不同的名称,则必须将其作为
case
查询中的语句处理,以这种方式将查询对象与每个模型耦合
所以我正在寻找更好实施的建议
更新
问题是我想传递从 ActiveModel 获得的任何对象,例如 ActiveRecord::Relation,所以我不能只用方法定义模块(并在需要时覆盖)并将其包含在我正在使用的模型中. 问题更多的是为干净的设计指明正确的方向,而不是关于实现细节,我会以某种方式弄清楚