ActiveRecord query objects
Table of Contents
:ID: 59ED4C2F-6166-4277-B3B5-3BAF6E8929F6
Isolate business logic from models.
See also ActiveRecord Querying
# Base class
# app/models/application_query.rb class ApplicationQuery def self.call(...) new.resolve(...) end attr_reader :relation def initialize(relation) @relation = relation end def resolve(*args) relation end end
# Subclass example
This is from tmp
# app/models/user/with_image_containing_memos.rb class User class WithImageContainingMemos < ApplicationQuery def initialize(relation = User.all) super(relation) end def resolve(image_count: 1) User.with( with_image_memos: Memo.where(image_attachment_count: image_count..) .select(:user_id) .distinct ).joins(:with_image_memos) end end end
# Adding scopes
One could decide to extract scopes used for a query object to the relevant model. Or they could be extracted to a module within the query object as a way of wrapping parts of the query in methods that organize the code and improve readabilty.
# app/models/user/with_image_containing_memos.rb class User class WithImageContainingMemos < ApplicationQuery module Scopes def with_image_count_gt(count) where(image_attachment_count: image_count..) end end def initialize(relation = User.all) super(relation) end def resolve(image_count: 1) User.with( with_image_memos: Memo.extending(Scopes) .with_image_count_gt(image_count) .select(:user_id) .distinct ).joins(:with_image_memos) end end end
# Attach query obj to scope
Query objects can be attached to active record scopes if you want, as long as
the object responds to call
class User < ApplicationRecord scope :with_image_containing_memos, WithImageContainingMemos end