Why I no longer define ActiveRecord scopes with class methods
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 tofalse
, whereas a class method, will returnnil
. This can causeNoMethodError
when chaining class methods with conditionals, if any of the conditionals returnfalse
.
This is why I have started to write my scope methods using scope
. Keeping your ActiveRecord
query methods chainable keeps your application flowing.