Coin-operated (tele)scope

A scope should return a scope

The more you can stay on the ‘rails’ when coding Ruby on Rails applications the easier your life will be when maintaining the apps you’re building.

RailsConf 2024

I'm co-chairing RailsConf 2024 in Detroit May 7–9. Come and join us 

A good way of doing this is to try and stick to the patterns set out in the standard Rails APIs. One of the patterns you can use is to encompass regularly used queries as scopes.

Instead of…

…using methods inside your scopes that return an object.

class Message < ActiveRecord
  scope :sent, -> { where.not(sent_at: nil) }
  scope :recently_sent, -> { sent.order(sent_at: :desc) }
  scope :most_recently_sent, -> { recently_sent.first } # returns an object or nil
end

Always…

…return an ActiveRelation from a named scope.

class Message < ActiveRecord
  scope :sent, -> { where.not(sent_at: nil) }
  scope :recently_sent, -> { sent.order(sent_at: :desc) }

  def self.most_recently_sent
    recently_sent.first
  end
end

But why?

This change improves the organisation of your code, rather than changing how you’ll use your models

When you call .where or .order it returns an Active Relation ‘scope’ that can be chained together with further scopes. You encourage flexibility and reuse if you maintain this behaviour where all of your own named scopes are able to be chained with others.

The principle of least surprise is a solid heuristic with which to organise your code.

Why not?

You can get by by just being careful which scopes you use together, there is no “rule” against using .first or .last inside a scope.

Why not use this helpful heuristic to organise your code to guide your, or your co-workers, future use of the scopes you create.

Brighton Ruby 2024

Still running UK’s friendliest, Ruby event on Friday 28th June. Ice cream + Ruby 


Last updated on October 14th, 2018 by @andycroll

An email newsletter, with one Ruby/Rails technique delivered with a ‘why?’ and a ‘how?’ every two weeks. It’s deliberately brief, focussed & opinionated.