Ordering, Limits and Scopes
"The five newest available books" appears on the home page, in an email and in the API. Writing Book.where(available: true).order(created_at: :desc).limit(5) in three places is three chances to get it slightly different. Scopes give the pieces names, and ordering methods finish the query.
The rule
Name the filter as a scope, then chain: filter, order, limit.
Ordering and limiting:
Book.order(:title) # A to Z
Book.order(pages: :desc) # longest first
Book.order(author: :asc, title: :asc) # by author, then title
Book.order(created_at: :desc).limit(5) # five newest
Book.order(:id).limit(20).offset(40) # the third page of 20
Book.order(:title).reverse_order # flip an existing orderScopes are named, reusable relations defined on the model:
class Book < ApplicationRecord
scope :available, -> { where(available: true) }
scope :long, -> { where("pages > ?", 500) }
scope :newest, -> { order(created_at: :desc) }
scope :by_author, ->(name) { where(author: name) }
end
Book.available.newest.limit(5)
Book.available.long.by_author("Tolkien")A scope is a lambda (->) that returns a relation. It can take arguments. Scopes also work through associations: member.reviewed_books.available.
A scope and a class method (def self.available; where(available: true); end) are almost the same. The difference: if a scope's body returns nil or false, the scope returns all instead, so a chain never breaks.
default_scope applies a condition to every query on the model. It sounds convenient but surprises people later, because it also affects creates and associations. Prefer named scopes.
The shortcut
Read the request as a sentence: "[which] [in what order] [how many]". Each "which" phrase becomes a scope; the order becomes order; the count becomes limit. Chain them in that order.
Which
"available", "by Pratchett": scopes
availableandby_author("Pratchett").
The trap is limit without order. The database is free to return rows in any order, so "the first five" changes between runs.
Drills
Answer fast — trust the rule.
Which call lists books with the most pages first?
How do you define a scope for books under 200 pages?
Pages hold 20 books each. Which call gets page 3?
A scope's lambda returns nil for a blank argument. What does calling the scope return?
Remember
- Name the filter as a scope, then chain: filter, order, limit.
- Shortcut: which, in what order, how many.
- Trap: limit without order, and default_scope surprises.
