Query Scopes¶
Scopes allow you to define common, reusable query fragments for your models. They help keep your code DRY (Don't Repeat Yourself) and improve the readability of your business logic by giving meaningful names to complex queries.
Defining Scopes¶
You define a scope using the scope macro. The first argument is the name of the scope, and the second is a lambda that defines the query logic.
class User < Ralph::Model
table :users
column id : Int64, primary: true
column active : Bool
column age : Int32
# A simple scope with no arguments
scope :active, ->(q : Ralph::Query::Builder) { q.where("active = ?", true) }
# A scope that takes arguments
scope :older_than, ->(q : Ralph::Query::Builder, age : Int32) {
q.where("age > ?", age)
}
end
Important: Since Ralph's
Query::Builderis immutable, the lambda must return the modified builder. Chained method calls likeq.where(...).order(...)return the new builder automatically.
Using Scopes¶
Scopes are available as class methods on your model. They return a Query::Builder instance, which means you can chain them with other builder methods or even other scopes.
# Get all active users
active_users = User.active
# Chain with other query methods
recent_active_users = User.active.order("created_at", :desc).limit(5)
# Use a scope with arguments
adults = User.older_than(18)
Scope Composition¶
One of the most powerful features of scopes is the ability to combine them. You can chain multiple scopes together to build complex queries from simple building blocks.
The merge method takes another builder and combines its conditions with the current one. Note that since scopes return builders, you call the first scope as a method and then merge the result of the second.
Anonymous Scopes¶
If you have a one-off query customization that doesn't warrant a named scope, you can use the scoped class method. This is essentially an alias for query.
Best Practices¶
Give Meaningful Names¶
Scopes should describe what they are filtering for, not the underlying implementation.
- Good:
scope :published,scope :recent,scope :admins - Bad:
scope :status_is_2,scope :created_after_last_week
Keep Scopes Simple¶
Each scope should ideally handle one responsibility. Combine simple scopes to create complex queries rather than creating one giant, complex scope.
Avoid Default Scopes¶
Ralph intentionally avoids "default scopes" (queries that are applied to every find/all call). Default scopes often lead to confusion and bugs when you need to bypass them. Instead, prefer explicit named scopes like User.active.