ruby 32 lines · 7 steps

How scopes compose in a Rails model

An Order model layers associations, enums, and reusable scopes that build on each other with subqueries.

Explained by highlit
1class Order < ApplicationRecord
2 belongs_to :customer
3 has_many :line_items
4 has_many :products, through: :line_items
5 
6 enum status: { pending: 0, paid: 1, shipped: 2, cancelled: 3 }
7 
8 scope :recently_paid, -> {
9 where(status: :paid).where(paid_at: 30.days.ago..)
10 }
11 
12 scope :for_active_customers, -> {
13 where(customer_id: Customer.active.select(:id))
14 }
15 
16 scope :containing_discounted_products, -> {
17 discounted = Product.where("discount_percentage > 0").select(:id)
18 where(id: LineItem.where(product_id: discounted).select(:order_id))
19 }
20 
21 scope :high_value_repeat_business, -> {
22 repeat_buyers = Order.paid
23 .group(:customer_id)
24 .having("COUNT(*) >= ?", 3)
25 .select(:customer_id)
26 
27 for_active_customers
28 .where(customer_id: repeat_buyers)
29 .where("total_cents >= ?", 25_000)
30 .order(total_cents: :desc)
31 }
32end
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Named scopes return relations, so they chain and nest inside other scopes without triggering queries early.
  2. 2Passing a relation to where builds a SQL subquery, keeping filtering in the database instead of loading records into Ruby.
  3. 3Small, single-purpose scopes compose into complex business queries that stay readable and reusable.

Related explainers

Share this explainer

Here's the card — post it anywhere.

How scopes compose in a Rails model — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code