GitHub Sale: sign up for any paid plan this week and pay nothing until January 1, 2009!  [ hide ]

public
Description: Rails plugin that provides the ability to soft delete models
Clone URL: git://github.com/ajh/acts_as_soft_deletable.git
ajh (author)
Thu May 15 14:42:07 -0700 2008
commit  4a63856c5eccd3c550041620c75eeca60fca4dfa
tree    ace898a7b2f984c6c01a002e9e2c156207f65941
parent  42b5ed405b984f691364c56a0c8b6331bfdf02e9
name age message
file COPYING Thu May 15 14:42:07 -0700 2008 Add GPL license and copyright notice. [ajh]
file README Thu May 15 14:42:07 -0700 2008 Add GPL license and copyright notice. [ajh]
file Rakefile Thu Mar 13 12:32:00 -0700 2008 make even more friendly to autotest [(no author)]
file init.rb Thu Mar 13 14:52:16 -0700 2008 add test helper and use them internally. Update... [(no author)]
directory lib/ Thu Mar 13 15:57:11 -0700 2008 add a comment [(no author)]
directory test/ Thu Mar 13 14:52:16 -0700 2008 add test helper and use them internally. Update... [(no author)]
README
Copyright (C) 2008 Substantial Consulting and Andrew Hartford <hartforda@gmail.com>

=== acts_as_soft_deletable ===

This plugin provides the ability to soft delete ActiveRecord models. When 
models are destroyed, they will be archived into a special deleted table. 
They can later be restored easily.

<pre>

class Artist < ActiveRecord::Base
  # This will wrap the destroy method to provide soft delete 
  # support and create a new ActiveRecord class called Artist::Deleted
  acts_as_soft_deletable
end

model = Artist.find(34)
model.destroy   # removes row from artists table, and adds a row to 
                # deleted_artists table

deleted = Artist::Deleted.find(34)
deleted.undestroy!  # adds the row back to the artists table, and removes 
                    # if from the deleted_artists table

restored = Artist.find(34) # The artist is restored with all the same 
                           # information. The updated_at column will be 
                           # Time.now if it exists.

</pre>

=== Compare to acts_as_paranoid ===

Acts_as_paranoid takes the approach of using a deleted_at flag in the models table. This has the drawback that finds on 
the model have to exclude deleted rows. This behaviour change turns out be be a challenge. Acts_as_paranoid tries to 
patch the ActiveRecord internals to accomplish this, but ActiveRecord could change in the future causing acts_as_parnoid 
to break. Also, there are some exotic finds that need to be considered like "has_many through with polymorphism". This 
doesn't exclude deleted models as it should in acts_as_paranoid (March 2008).

This plugin avoids the problem of changing find's behavior by allowing the row to really be 
deleted and archiving it into another table. The advantages are that its hook into ActiveRecord is really simple and has 
low risk of breaking in the future. The cost is that there are a bunch of deleted tables in the database that need to be 
maintained. For example, if the table adds a column in a future migration, then the deleted table needs that column as 
well.

This turns out to be easy to solve. A migration helper is available (see below) that will help keep the deleted table in 
sync. Also, a unit test helper is provided (again, see below) which adds unit tests to the model ensuring soft delete is 
working. If this is used, the test will fail if the deleted table gets out of sync.

=== Setup ===

---+ Model

Any ActiveRecord class that wants the soft delete functionality should add
the following line to their class definition:

<pre>
class SomeModel < ActiveRecord::Base
  acts_as_soft_deletable
  ...
</pre>

---+ Migration

and setup the deleted table with the following migration:

<pre>
class AddActsAsSoftDeletable < ActiveRecord::Migration
  def self.up
    SomeModel::Deleted.create_table
  end

  def self.down
    SomeModel::Deleted.drop_table
  end
end
</pre>

Any changes to the original table (such as adding a column) should be reflected in the deleted table. Use the 
update_columns method:

<pre>
class AddSkuColumn < ActiveRecord::Migration
  def self.up
    add_column 'items', 'sku', :string
    Item::Deleted.update_columns # will add sku column
  end

  def self.down
    remove_column 'items', 'sku'
    Item::Deleted.update_columns # will remove sku column
  end
end
</pre>

---+ Unit tests

A model's soft delete capabilities can be unit tests easily by using some provide asserts.

<pre>
  def test_soft_delete_works
    # will run the model through a destroy and undestroy while making sure all values were saved
    assert_model_soft_deletes( items(:radar_detector) )
  end
</pre>

=== TODO ===

make undestroying easier when there are lots of related rows to undestroy.