Back to Tutorials
Jutsu>Rails6/8/2025

Rails Scopes

railsrubyscopes
Rails Scopes

Scopes are a great Rails tool to keep stuff DRY and well organized. It's not complicated though, it's just a set of pre-defined queries that can easily be chained to build complex queries. Let's see how to use them efficiently and what to do when they're not enough anymore.

Basics

Let's start with the basics. We'll use a sample model named Article to illustrate what scopes allow you to do.

This model has the following attributes:

  • title (string)
  • content (text)
  • published (boolean)
  • author_name (string)

On your homepage, you only want to show published articles of course. So in your action, you would use the following code to ensure that.

def index
  Article.where(published: true)
end

You will probably need to re-use this code somewhere else so it would be better to define it in the model directly. Plus, it has to do with querying data so it kinda belongs in the model and not the controller.

class Article < ActiveRecord::Base
    scope :published, -> { where(published: true) }
end

And now we can just do this in our controller and anywhere else we need it:

def index
  Article.published
end

Super clean! So that's how you use a scope. Note that scopes don't return an array of elements. Instead, they return an ActiveRecord::Relation.

With Parameters

Scopes can also receive parameters. Let's see how we can create a scope to get all the articles written by a specific author.

class Article < ActiveRecord::Base
    scope :author, -> (name) { where(author_name: name) }
end

Which allows us to do this in our controller:

def index
  Article.author(params[:author_name])
end

Note that you can give the name you want to this scope. Here are a few other options for you:

  • written_by
  • authored_by
  • by_author

Chaining

Now that you know how to create scopes, here's where they really shine: they're completely chainable and only one query will be executed!

So with this:

class Article < ActiveRecord::Base
    scope :published, -> { where(published: true) }
    scope :author, -> (name) { where(author_name: name) }
end

You can do that:

def index
  Article.published.author('thibault')
end

And Rails will generate one SQL query that will look something like this:

SELECT * FROM articles WHERE published = true AND author_name = 'thibault';

Lazy Loading

Note that the previous query is actually lazy-loaded so until you try to do something with the result, the actual SQL query won't happen.

For example, this does not trigger the query:

Article.published.author('thibault')

But this does:

Article.published.author('thibault').to_a

Class methods

You can also use class methods to define scopes.

class Article < ActiveRecord::Base

  scope :written_by_thibault, -> { where(author_name: 'thibault') }

  def self.published
    where(published: true)
  end

  def self.author(name)
    where(author_name: name)
  end

end

Defining scopes with the scope option is cleaner but if you need to do some kind of processing, you might want to put that inside a class method instead.

Interesting Methods

There are a bunch of interesting methods related to scopes.

  • default_scope

    Don't. Use. This.

    Default Scope allows you to set a scope that's always called when you access your model. Why you should not use it is explained in this stackoverflow answer.

  • unscoped

If you ever stumble upon code using a default scope and you want to access models that are outside of that scope, you can use the unscoped method like this:

Article.uncoped.some_other_scope
  • all

This method now returns a relation and can be used to get a scope without filtering.

articles = Article.all
articles = articles.published

all used to return an array and you had to use scoped instead.

  • none

Just the opposite of all. You're getting no record at all as an ActiveRecord::Relation.

Going further

You can also use scopes to do ordering, eager loading and anything else you want as long as you return a scope. In the following example, we pre-load the relation to make the query more efficient.

class User < ActiveRecord::Base
  attr_accessible :id, :username
  has_many :addresses

  scope :by_country, -> (name) { includes(:addresses).where("addresses.country = ?", "Thailand") }
end

class Address < ActiveRecord::Base
  attr_accessible :id, :street, :city, :country
  belongs_to :user
end

You can also create ordering scopes.

class Articles < ActiveRecord::Base
  scope :ordered, -> { order('created_at desc') }
end

The End

This is the end of the beginner's guide to scoping with Ruby on Rails. Scopes are truly awesome and will allow you to keep your code clean. DRY!

Comments

Loading comments...

Level Up Your Dev Skills & Income 💰💻

Learn how to sharpen your programming skills, monetize your expertise, and build a future-proof career — through freelancing, SaaS, digital products, or high-paying jobs.

Join 3,000+ developers learning how to earn more, improve their skills, and future-proof their careers.

Rails Scopes | Devmystify