ALERT: ActiveRecord#order and ActiveRecord#default_scope are dangerous
Consider this model:
class Transaction
default_scope -> { order("created_at ASC") }
def self.latest
order('created_at DESC')
end
def self.approved_for_customer(customer)
where('approved is not null and customer_id = ?', customer).order('approved_at DESC')
end
end
And this controller:
class TransactionsWorker
def perform(...)
transaction = Transaction.approved_for_customer.latest # WTF!
end
end
The order will be "ORDER BY created_at ASC, approved_at DESC, created_at DESC", not "created_at DESC".
Solutions:
- Don't return an instead of ActiveRecord::Relation if you use order, return an array (.all, .first, etc)
- Don't use order and default_scope at all in models, ordering usually belongs in the business-logic layer.
- Use reorder as the last method call if chaining ActiveRecord::Relation, e.g. Transaction.where('...').reorder(:approved_at)
A safer model:
class Transaction
def self.latest
order('created_at DESC').all
end
def self.for_customer(customer)
where('approved is not null and customer_id = ?', customer).order('approved_at DESC').all
end
end