๐Ÿ”Ž

Scopes & Query Interface

Name and manage reusable queries

Scopes name and define frequently used query conditions on models.

class Post < ApplicationRecord
  scope :published, -> { where(status: 'published') }
  scope :recent, -> { order(created_at: :desc) }
  scope :by_author, ->(user) { where(user: user) }
  scope :this_month, -> { where(created_at: Time.current.beginning_of_month..) }
end

Usage:

Post.published                        # only published posts
Post.published.recent                 # chaining
Post.published.by_author(user).recent # combining multiple scopes
Post.published.count                  # combine with aggregation

Query Interface key methods:

  • where โ€” conditions (AND)

  • or โ€” OR conditions

  • not โ€” NOT conditions

  • order โ€” sorting

  • limit / offset โ€” paging

  • includes โ€” Eager Loading

  • joins โ€” INNER JOIN

  • left_joins โ€” LEFT OUTER JOIN

  • select / pluck โ€” select specific columns

Queries use Lazy Loading โ€” SQL only executes when data is actually needed.

Key Points

1

Define scope with scope :name, -> { where(...) }

2

Call with Model.scope_name (works like class method)

3

Chain multiple scopes for complex queries

4

Scope with args: scope :by_status, ->(s) { where(status: s) }

5

default_scope auto-applies to all queries (use with caution)

6

Lazy Loading: SQL executes when .to_a, .each, .count etc. are called

Pros

  • Code reuse (DRY)
  • Improved readability โ€” query intent is clear
  • Flexible composition through chaining
  • Lazy Loading prevents unnecessary queries

Cons

  • default_scope causes unexpected behavior
  • Too many scopes make code hard to trace
  • Complex queries may be clearer in raw SQL
  • Watch for side effects in scopes

Use Cases

Status filters (published, draft) Date range filters (this_week, this_month) Sorting (recent, oldest) User filters (by_author)