Goal
You’ll be able to write model scopes for reusable queries, and understand why scopes are genuinely better than repeating the same query condition across multiple places in your codebase.
Learn
A scope defines a reusable, named query on a model:
class Product < ApplicationRecord
scope :in_stock, -> { where("stock > 0") }
scope :expensive, -> { where("price > 100") }
end
Product.in_stock # all in-stock products
Product.in_stock.expensive # scopes can be chained together Without scopes, you’d need to repeat Product.where("stock > 0") everywhere in your codebase that needed this specific condition — a controller, a background job, a view helper, each writing the same raw condition independently. A scope centralizes that logic in exactly one place.
This connects directly to the same reasoning from Part 5.4 in the earlier Python course about why functions matter: if the definition of “in stock” ever needs to change (say, to also exclude discontinued items), you’d update the scope definition in exactly one place, and every single usage across the entire codebase automatically reflects the new logic — versus needing to find and update every individually duplicated raw query condition, risking missing one.
Scopes can also accept arguments:
scope :cheaper_than, ->(price) { where("price < ?", price) }
Product.cheaper_than(50) Decision Task
A codebase has Product.where(“stock > 0”) repeated in 8 different places across controllers, jobs, and views. Before reading on: what real problem does this create if the business logic for “in stock” needs to change later, say to also check a separate is_discontinued flag?
Show Answer
Every single one of those 8 places needs to be found and individually updated to reflect the new logic, with real risk of missing one and leaving inconsistent behavior somewhere in the codebase. Defining this as a single scope method instead means updating the logic in exactly one place, with all 8 usages automatically reflecting the change immediately, with zero risk of an inconsistently-updated location being missed.
Common Mistake
Repeating the same raw where condition across multiple controllers, views, or background jobs instead of centralizing it into a named scope. This creates exactly the same “update in many places, risk missing one” maintenance problem covered for functions and CSS variables in earlier courses — the specific mechanism differs, but the underlying reasoning for centralizing repeated logic is identical.
Practice Questions
1. Write a scope called recent that returns products created within the last 7 days.
Show Answer
scope :recent, -> { where("created_at > ?", 7.days.ago) }
2. Write a scope called by_category that accepts a category argument and filters products by it.
Show Answer
scope :by_category, ->(category) { where(category: category) }
3. Can scopes be chained together, like Product.in_stock.expensive? What does this actually do?
Show Answer
Yes — chaining scopes combines their conditions, returning only records matching both conditions simultaneously (in stock AND expensive, in this example).
4. True or False: using a scope is functionally identical to repeating the same where condition manually everywhere, just with different syntax.
Show Answer
False in practical terms — while the query result may be identical, a scope centralizes the logic in one place, meaning future changes only need to happen once, unlike manually repeated conditions scattered across the codebase.
5. Why does centralizing a query condition into a scope matter more as a codebase grows larger?
Show Answer
The more places a condition is duplicated, the higher the real risk of inconsistency when that logic needs to change, and the more tedious and error-prone finding every instance becomes.
Try It Yourself
Without looking back, write a scope called low_stock that returns products where stock is less than 10.
Show Answer
scope :low_stock, -> { where("stock < 10") }
Quick Check
1. What does a scope define?
Show Answer
A reusable, named query on a model.
2. Can scopes be chained together?
Show Answer
Yes, combining their conditions.
3. What real problem does centralizing a query into a scope solve, compared to repeating it everywhere?
Show Answer
It avoids needing to find and update every duplicated instance individually if the underlying logic ever changes, reducing risk of inconsistency.
4. Can a scope accept arguments?
Show Answer
Yes, using the ->(argument) { } syntax.
5. Write the basic syntax pattern for defining a scope.
Show Answer
scope :name, -> { query_condition }