Hiding data in Rails with default_scope
One of ActiveRecord's less well known features is the default_scope
class
method. I've just added support for deleting cards to Agile
Planner. Rather than destroy users' data
immediately when they click the delete button, I wanted to give people an
opportunity to recover from mistakes, but it doesn't feel like the right time
to implement a full undo system just yet.
My solution was to hide cards from the user interface after they've been deleted, and to write a cron job that will periodically remove old deleted cards from the database.
Yes, this is a bit basic, but it's not worth me adding full undo/redo support yet (there are plenty of higher priority things to work on). If a user deletes a card by mistake, and they really need the data that's on it, they can always ask me to restore it from backup. Rather than digging through my backups I'll just be able to toggle a setting on the card to bring it back to life. And you never know, accidental deletion might be a very rare event. I'll improve it if I need to.
The relevant sections of my ActiveRecord models look like this:
class Iteration < ActiveRecord::Base
has_many :cards
end
class Card < ActiveRecord::Base
belongs_to :iteration
default_scope where(deleted: false)
end
So to find all the cards that belong to an iteration and haven't been deleted I can just use the cards scope. It'll filter all the deleted cards out automatically:
iteration.cards # <- no deleted cards here
If you want to get at the deleted cards, you can use the unscoped
method to
remove the default scope.
Card.unscoped { iteration.cards } # <- also returns deleted cards
It's such a simple approach that it took me less than an hour to develop and deploy the deletion of cards into production.
A word of warning
The unscoped
method isn't something that I'd advocate using very often.
Opportunities for misleading yourself and your co-workers are legion, but when
it's a good fit for a problem like this it comes in very handy. I really
didn't want to trawl round the app to find all of the methods that iterate
over cards and manually filter out the deleted ones.