Skip to content

Instantly share code, notes, and snippets.

@stevenharman
Last active December 19, 2017 21:33
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save stevenharman/4346010 to your computer and use it in GitHub Desktop.
Save stevenharman/4346010 to your computer and use it in GitHub Desktop.
Build compose-able query objects by delaying query execution and delegating to the underlying `ActiveRecord::Relation` for everything else. #ruby #rails

Compose-able Query Objects in Rails

Example usage:

old_accounts = Account.where("created_at < ?", 1.month.ago)
old_abandoned_trials = AbandonedTrialQuery.new(old_accounts)

old_abandoned_trials.find_each do |account|
  account.send_offer_for_support
end

Hat-tips:

I've been using this pattern for a while, but this particular example is a re-imagining of @brynary's Extract Query Objects pattern.

require 'delegate'
class AbandonedTrialQuery < SimpleDelegator
def initialize(relation = Account.scoped)
super(relation.where(plan: nil, invites_count: 0))
end
end
@skyriverbend
Copy link

Nice, thanks for this example. Works a treat!

@rantler
Copy link

rantler commented Jul 3, 2013

How is this better than just using a scope?

class Account < ActiveRecord::Base
  scope :abandoned_trials, where(plan: nil, invites_count: 0).where("created_at < ?", 1.month.ago)
end

Account.abandoned_trials.each(&:send_offer_for_support)

@stevenharman
Copy link
Author

@rantler I'm not trying to make a value judgement here - as to it being better or worse than anything. As mentioned in the Gist description, this is simply another technique for slimming down fat ActiveRecord objects.

@stevenharman
Copy link
Author

Five years later... I'm searching the Internets for alternative techniques and/or implementations. I've not found anything so simple. Is this still a Good Idea™ in Rails-land circa 2017 (on version 5.1)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment