gpt4 book ai didi

ruby-on-rails - Rails 的 QueryObject - 如何封装 ActiveRecord 上的一组操作

转载 作者:行者123 更新时间:2023-12-03 15:56:06 31 4
gpt4 key购买 nike

如何封装一组操作(其中大部分本质上是 .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,所以我不能只用方法定义一个模块(并在需要时覆盖)并将它包含在我正在使用的模型中。问题更多的是为干净的设计指明正确的方向,而不是关于实现细节,我会以某种方式弄清楚

    最佳答案

    为避免紧密耦合,请将模型特定代码放入模型中:

    class ModelA < ActiveRecord::Base
    scope :created_before, ->(timestamp) { where("created_at < ?", timestamp) }
    scope :created_after, ->(timestamp) { where("created_at >= ?", timestamp) }
    scope :with_hash, ->(hash) { ... }
    end

    class ModelB < ActiveRecord::Base
    scope :created_before, ->(timestamp) { where("other_column < ?", timestamp) }
    scope :created_after, ->(timestamp) { where("other_column >= ?", timestamp) }
    scope :with_hash, where('false') # empty, chain-able collection
    end

    现在你有了一个一致的接口(interface),你可以针对它进行编程:
    class ActivityQuery

    def apply(activity)
    activity = activity.scoped
    activity = activity.created_before(@created_before) if @created_before.present?
    activity = activity.created_after(@created_after) if @created_after.present?
    activity = activity.with_hash(@hash) if @hash.present?
    activity
    end

    end

    不用抢救 NoMethodError并且不再摆弄模型的实现细节。

    关于ruby-on-rails - Rails 的 QueryObject - 如何封装 ActiveRecord 上的一组操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16082615/

    31 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com