Dec 2014

23

Scopes in Rails

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.

References




Tags: ActiveRecord