QueryObject for Rails - how to encapsulate set of operations on ActiveRecord -


how can encapsulate set of operations (most of .where(...)) , apply different models, in way of models may not implement of operations , should return empty collections. (non-essential code skipped)

what have designed (but not satisfied with):

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 of them suported modelc   rescue nomethoderror     return []   end end 

usage

query = activityquery.new.created_before(last_time).with_hash(...) activities = activityfinder.find(query) 

what don't like:

  • the rescue nomethoderror
  • if different models has different name field, has handled case statement in query, coupling in way query object each of models

so i'm searching suggestions better implementation

update

the problem want pass object got activemodel, e.g. activerecord::relation, can't define module methods (and override when needed) , include in models i'm using. question more point in right direction clean design, , not implementation details, i'll figure out somehow

to avoid tight coupling, put model specific code model:

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 

now have consistent interface can program against:

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 

no need rescue nomethoderror , no more fiddling model's implementation details.


Comments

Popular posts from this blog

Why does Ruby on Rails generate add a blank line to the end of a file? -

keyboard - Smiles and long press feature in Android -

node.js - Bad Request - node js ajax post -