Sonntag, 13. April 2014

Soft delete your ActiveRecord!

Some models should not be deleted, but deactivated for very comprehensible reasons:
  1. Restoring the record
  2. Documenting the record in pre deletion points in time
  3. Statistics
The migration for adding the new boolean 'active' column to the Person model:
rails g migration add_active_to_people active:boolean
and running rails db:migrate. The model:
class Person < ActiveRecord::Base
  attr_accessible :name
  validates :active, :inclusion => { :in => [true, false] }
  before_validation :activate, 
    :on => :create,
    :if => Proc.new {|r| r.active.nil? }

  def self.active
    where :active => true
  end 

  def activate
    self.active = true
    self
  end 

  def deactivate
    self.active = false
    self
  end 
end
The model expects the boolean active flag to be set with nothing else than true or false.
Furthermore every new person record will be initialized with true in a callback before validating it. That means if nothing else was set, the instance method activate is called, which does exactly set the flag to true by default.
A named scope appends a selection to the finder chain, returning all active people. A usage example:
Person.active
If you are not familiar with ActiveRecord scopes read 'Scope the model!'.
The second instance method deactivate deactivates the record. In fact it prepares soft deletion by setting the 'active' flag to false like:
Person.find(1).deactivate
and touching he database:
Person.find(1).deactivate.save
which will generate the SQL on a MariaDB system:
UPDATE 'people' SET 'active' = 0, WHERE 'people'.'id' = 1
Well that could be wrapped behind:
def deactivate!
  deactivate.save!
end
Another reasonable data type for soft deletion columns is datetime like deleted_at. I would suggest using it, when the timestamp of the deletion has to be documented as well.

Supported by Ruby 2.1.1 and Ruby on Rails 3.2.17