Michael Anhari

Why I no longer define ActiveRecord scopes with class methods

A telescope facing a floor to ceiling window.

In Rails you can define methods that filter query results, commonly referred to as "scopes", by using the scope method from ActiveRecord and by adding a class method to your model.

Using the scope method involves passing it a lambda:

class Post
  scope :published, -> { where(published: true) }
end

This is essentially syntactic sugar for defining a class method:

class Post < ApplicationRecord
  def self.published
    where(published: true)
  end
end

One important difference when using scope

Sometimes you'll want a conditional statement in your scope to ensure they work when their parameters aren't present.

For example:

class Post < ApplicationRecord
  scope :created_before, ->(time) { where("created_at < ?", time) if time.present? }
end

This will behave similarly to a class method like before:

class Post < ApplicationRecord
  def self.created_before(time)
    where("created_at < ?", time) if time.present?
  end
end

However, there is one important difference between the two. Directly from the Rails guides:

A scope will always return an ActiveRecord::Relation object, even if the conditional evaluates to false, whereas a class method, will return nil. This can cause NoMethodError when chaining class methods with conditionals, if any of the conditionals return false.

This is why I have started to write my scope methods using scope. Keeping your ActiveRecord query methods chainable keeps your application flowing.

Newsletter

I'm working on sending out a weekly newsletter. I'll make it as easy as possible to unsubscribe at any time.