forked from ajh/acts_as_soft_deletable
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add rake task to build rdoc. Reformat README for rdoc. Fixup rdoc in code.
- Loading branch information
Showing
4 changed files
with
86 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,103 +1,91 @@ | ||
Copyright (C) 2008 Substantial and Andy Hartford <hartforda @ gmail.com> | ||
|
||
=== acts_as_soft_deletable === | ||
== 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 | ||
acts_as_soft_deletable # This will wrap the destroy method to provide soft delete | ||
# support and create a new ActiveRecord class called Artist::Deleted | ||
end | ||
|
||
class Artist < ActiveRecord::Base | ||
acts_as_soft_deletable # This will wrap the destroy method to provide soft delete | ||
# support and create a new ActiveRecord class called Artist::Deleted | ||
end | ||
model = Artist.find(34) | ||
model.destroy # removes row from artists table, and adds a row to | ||
# deleted_artists table | ||
|
||
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 | ||
|
||
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. | ||
|
||
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 === | ||
== Compare to acts_as_paranoid | ||
|
||
Acts_as_paranoid takes the approach of using a deleted_at flag in the models table. If the deleted_at column has a value, the row is considered 'deleted'. The problem with this approach is that all finds on the model have to exclude 'deleted' rows. This turns out be be a challenge. Acts_as_paranoid patches the ActiveRecord internals to accomplish this, but it is fragile and could break with future changes to ActiveRecord. Also, some of the more exotic finds currently don't work (has_many :through with polymorphism as of March 2008) and supporting them means running on an upgrade treadmill to keep up with the evolution of ActiveRecord. | ||
|
||
This plugin avoids these problems by allowing the row to be deleted and archiving it into another table. The behavior of ActiveRecord::Base#Find doesn't have to change which should mean that this plugin is more immune to breaking due to ActiveRecord development. Queries on the live table will also be faster in the case of lots of deleted rows, because they will be in a seperate table. The tradeoff is that the deleted table needs to be maintained along with the live table. For example, if the artists table adds a column in a future migration, then the deleted_artists table needs that column as well. | ||
|
||
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 === | ||
== Setup | ||
|
||
---+ Model | ||
=== 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> | ||
class SomeModel < ActiveRecord::Base | ||
acts_as_soft_deletable | ||
... | ||
|
||
---+ Migration | ||
=== Migration | ||
|
||
and setup the deleted table with the following migration: | ||
|
||
<pre> | ||
class AddActsAsSoftDeletable < ActiveRecord::Migration | ||
def self.up | ||
SomeModel::Deleted.create_table | ||
end | ||
class AddActsAsSoftDeletable < ActiveRecord::Migration | ||
def self.up | ||
SomeModel::Deleted.create_table | ||
end | ||
|
||
def self.down | ||
SomeModel::Deleted.drop_table | ||
end | ||
end | ||
</pre> | ||
def self.down | ||
SomeModel::Deleted.drop_table | ||
end | ||
end | ||
|
||
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 | ||
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> | ||
def self.down | ||
remove_column 'items', 'sku' | ||
Item::Deleted.update_columns # will remove sku column | ||
end | ||
end | ||
|
||
Note that update_columns will happily delete columns if asked which will lose data forever. I'm not sure if this behavior is fine or horrifying, but it's definitely something to keep in mind. I'd appreciate feedback on this. | ||
|
||
---+ Unit tests | ||
=== Unit tests | ||
|
||
A model's soft delete capabilities can be easily unit tested by using this provided assert: | ||
|
||
<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> | ||
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 | ||
|
||
This was developed with Test::Unit in mind. Not sure how well it works with rspec. | ||
|
||
=== Thanks === | ||
=== Thanks | ||
|
||
Substantial, my employer for letting me release this | ||
acts_as_paranoid and technoweenie, for a plugin I've got good years of use out of | ||
acts_as_versioned, who's approach influenced this plugin | ||
Danimal, for the feedback on rubyonrails-talk | ||
* Substantial, my employer for letting me release this | ||
* acts_as_paranoid and technoweenie, for a plugin I've got good years of use out of | ||
* acts_as_versioned, who's approach influenced this plugin | ||
* Danimal, for the feedback on rubyonrails-talk |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters