Provides a simple mapping between scopes and controller parameters for Ruby On Rails
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



Provides a simple mapping between scopes and controller parameters for Ruby On Rails 5.


Just add this into your Gemfile:

gem 'scoped_from'

Then, just run bundle install.


First, a model with some scopes:

class Post < ActiveRecord::Base

  scope :commented, where('comments_count > 0')

  scope :created_between, lambda { |after, before|
    where('created_at >= ? AND created_at <= ?', after, before)

  scope :search, lambda { |pattern|
    where('body LIKE ?', "%#{pattern}%")

  scope :with_category, lambda { |category_id|
    where(:category_id, category_id)


After, a controller:

class PostsController < ActionController::Base

  def index
    @posts = Post.scoped_from(params)


Then, it just filter your model from params:


Accepted scopes

All scopes can be mapped with scoped_from method except scopes taking a lambda (or a Proc) with an arity greater than 1 (for example: created_between in the above code).

Scopes with no argument are invoked if parameter value is evaluated as true. It includes "true", "yes", "y", "on", and "1" strings.

Columns are also automatically scoped.

Scopes restriction

You can restrict mapping to some scopes with :only option:

@posts = Post.scoped_from(params, only: ['commented', 'search'])

You can also exclude some scopes from mapping with :except option:

@posts = Post.scoped_from(params, except: 'commented')

Mapping order

If you need to map an SQL order, just pass order parameter:

@posts = Post.scoped_from(order: 'created_at')

Order direction can be specified using a dot, space or : as delimiter:

@posts = Post.scoped_from(order: 'created_at.desc')

Note that order is SQL safe with scoped_from method (columns names are checked).

Some cool stuff

If your provide an array as parameter value, scope is invoked with each item of the array:

@posts = Post.scoped_from(search: ['bar', 'foo'])

is equivalent to

@posts ='bar').search('foo')

You may also not want to filter on columns, just specify :exclude_columns option:

@posts = Post.scoped_from(params, exclude_columns: true)

A query string can also be given to scoped_from method:

@posts = Post.scoped_from('with_category=24&search[]=foo&search[]=bar')

Returned scope from scoped_from method gives access to an internal query object:

@posts = Post.scoped_from(params)
@query = @posts.query

This query provides you some convenience methods like params, order_column and order_direction. This object can also be used to save user's search into a database or other storage system.

But, you may also have to subclass this query class. You have to create a subclass of ScopedFrom::Query named #{RecordClassName}Query. Here is an example:

class PostQuery < ScopedFrom::Query

  def category
    Category.find_by_id(params[:with_category]) if params[:with_category]


This class has to be in load path.

Then into a view:

<% if @query.category %>
  <p>All posts of category <%= %></p>
<% else %>
  <p>All posts</p>
<% end %>

Executing test suite

This project is fully tested with Rspec 3. Just run bundle exec rake (after a bundle install).