Recently i was working on a personal Rails project and rediscovered the power of Scopes in Rails and thought of writing a blog about it.Scopes allow you to specify commonly-used queries which can be referenced as methods calls on models.
The Rails4 way is to define a lambda like so
# In your model
class Todo < ActiveRecord::Base
scope :completed -> { where(completed: true) }
end
A slightly more complex scope would be like so
# joins another model
class Todo < ActiveRecord::Base
scope :completed -> { where(completed: true) }
scope :with_category_by_slug, -> *args { joins(:category).where("categories.slug = ?", args.first) }
end
Default scope
Use this macro to set the default scope for all operations in your model like so
# default scope
class Todo < ActiveRecord::Base
default_scope -> { where(completed: false).order(:deadline) }
end
Unscoping
At times you would like to run the scopes without the default scopes and in that scenario use unscoped like so
# unscope
class Todo < ActiveRecord::Base
default_scope -> { where(mvp: false).order(:deadline) }\r\n scope :most_valued, -> { where(mvp: true).order(:deadline) }
end
# Usage
@mvp = Todo.unscoped.mvp
Scopes are extensible
One popular example is the Kaminari gem for pagination. One has to specify the page to fetch and how many per page like so
# extensible scope
scope :page, -> num
def per(num)\
# logic here
end
def total_pages
# logic here
end
def first_page?
# logic here
end
def last_page?
# logic here
end
end
# Usage
posts = Post.page(2).per(10)
posts.total_pages
posts.first_page?
posts.last_page?
Conclusion
I prefer to use scopes when the logic is very trivial and short and tend to use class methods when it is more involved.