delete_paranoid 0.0.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +16 -2
- data/delete_paranoid.gemspec +1 -0
- data/lib/delete_paranoid.rb +24 -35
- data/lib/delete_paranoid/version.rb +1 -1
- data/test/helper.rb +5 -7
- data/test/test_delete_paranoid.rb +69 -43
- metadata +18 -4
data/README.rdoc
CHANGED
@@ -6,11 +6,25 @@ Soft Delete ActiveRecord instances.
|
|
6
6
|
class Blog < ActiveRecord::Base
|
7
7
|
acts_as_paranoid
|
8
8
|
end
|
9
|
+
blog = Blog.create! :name => 'foo'
|
9
10
|
|
10
|
-
|
11
|
-
blog.destroy
|
11
|
+
# soft delete the instance
|
12
|
+
blog.destroy
|
13
|
+
|
14
|
+
# query database for results *including* soft deleted objects
|
15
|
+
Blog.with_deleted do
|
16
|
+
Blog.all
|
17
|
+
end
|
18
|
+
|
19
|
+
# permenantly delete the instance from the database
|
20
|
+
Blog.delete! blog.id
|
12
21
|
|
13
22
|
== Features
|
23
|
+
* simple configuration
|
24
|
+
* preserves existing ActiveRecord API. No magical new API's to use when you want to soft delete a record
|
25
|
+
* automatically exclude soft deleted records from database queries (by default)
|
26
|
+
* support for querying database for all records (including soft deleted ones)
|
27
|
+
* support for permenantly deleting record from database
|
14
28
|
|
15
29
|
== Contributing
|
16
30
|
|
data/delete_paranoid.gemspec
CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.add_development_dependency(%q<bundler>, [">= 0"])
|
21
21
|
s.add_development_dependency(%q<sqlite3-ruby>, ["~> 1.3.2"])
|
22
22
|
s.add_development_dependency(%q<ruby-debug>, [">= 0"])
|
23
|
+
s.add_development_dependency(%q<timecop>, [">= 0"])
|
23
24
|
|
24
25
|
s.files = `git ls-files`.split("\n")
|
25
26
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/lib/delete_paranoid.rb
CHANGED
@@ -1,53 +1,42 @@
|
|
1
|
+
require 'active_support/all'
|
1
2
|
require 'active_record'
|
2
3
|
|
4
|
+
module ActiveRecord
|
5
|
+
class Relation
|
6
|
+
alias_method :delete_all!, :delete_all
|
7
|
+
def delete_all(conditions = nil)
|
8
|
+
delete_all!(conditions) unless @klass.paranoid?
|
9
|
+
update_all({:deleted_at => Time.now.utc}, conditions)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
3
14
|
module DeleteParanoid
|
4
15
|
module ActiveRecordExtensions
|
5
16
|
def acts_as_paranoid
|
6
|
-
class << self
|
7
|
-
alias_method :delete_all!, :delete_all
|
8
|
-
end
|
9
|
-
alias_method :destroy!, :destroy
|
10
17
|
default_scope where(:deleted_at => nil)
|
18
|
+
|
11
19
|
extend DeleteParanoid::ClassMethods
|
12
|
-
|
20
|
+
end
|
21
|
+
|
22
|
+
def paranoid?
|
23
|
+
false
|
13
24
|
end
|
14
25
|
end
|
15
|
-
|
26
|
+
|
16
27
|
module ClassMethods
|
28
|
+
# permenantly delete the record from the database
|
29
|
+
def delete!(id_or_array)
|
30
|
+
where(self.primary_key => id_or_array).delete_all!
|
31
|
+
end
|
32
|
+
# allow for queries within block to find soft deleted records
|
17
33
|
def with_deleted
|
18
34
|
self.unscoped do
|
19
35
|
yield
|
20
36
|
end
|
21
37
|
end
|
22
|
-
|
23
|
-
|
24
|
-
update_all ["deleted_at = ?", Time.now.utc], conditions
|
25
|
-
end
|
26
|
-
|
27
|
-
def destroy_all!(conditions = nil)
|
28
|
-
if conditions
|
29
|
-
where(conditions).destroy_all!
|
30
|
-
else
|
31
|
-
to_a.each {|object| object.destroy! }.tap { reset }
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
module InstanceMethods
|
37
|
-
def destroy
|
38
|
-
if persisted?
|
39
|
-
with_transaction_returning_status do
|
40
|
-
_run_destroy_callbacks do
|
41
|
-
self.deleted_at = Time.now.utc
|
42
|
-
self.class.delete_all :id => self.id
|
43
|
-
@destroyed = true
|
44
|
-
end
|
45
|
-
end
|
46
|
-
else
|
47
|
-
@destroyed = true
|
48
|
-
end
|
49
|
-
|
50
|
-
freeze
|
38
|
+
def paranoid?
|
39
|
+
true
|
51
40
|
end
|
52
41
|
end
|
53
42
|
end
|
data/test/helper.rb
CHANGED
@@ -11,6 +11,7 @@ require 'test/unit'
|
|
11
11
|
require 'shoulda'
|
12
12
|
require 'mocha'
|
13
13
|
require "ruby-debug"
|
14
|
+
require 'timecop'
|
14
15
|
|
15
16
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
16
17
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
@@ -20,22 +21,19 @@ require 'database_setup'
|
|
20
21
|
class Test::Unit::TestCase
|
21
22
|
|
22
23
|
def self.should_soft_destroy(subject)
|
23
|
-
should "assign deleted_at on #{subject}" do
|
24
|
-
assert_not_nil instance_variable_get(:"@#{subject}").deleted_at
|
25
|
-
end
|
26
24
|
should "set #{subject} to destroyed" do
|
27
25
|
assert instance_variable_get(:"@#{subject}").destroyed?
|
28
26
|
end
|
29
27
|
should "freeze #{subject}" do
|
30
28
|
assert instance_variable_get(:"@#{subject}").frozen?
|
31
29
|
end
|
32
|
-
should "not
|
30
|
+
should "not find #{subject} normally" do
|
33
31
|
destroyed_subject = instance_variable_get(:"@#{subject}")
|
34
32
|
assert_raises ActiveRecord::RecordNotFound do
|
35
33
|
destroyed_subject.class.find destroyed_subject.id
|
36
34
|
end
|
37
35
|
end
|
38
|
-
should "
|
36
|
+
should "find #{subject} when in with_deleted block" do
|
39
37
|
destroyed_subject = instance_variable_get(:"@#{subject}")
|
40
38
|
destroyed_subject.class.with_deleted do
|
41
39
|
assert_nothing_raised ActiveRecord::RecordNotFound do
|
@@ -46,13 +44,13 @@ class Test::Unit::TestCase
|
|
46
44
|
end
|
47
45
|
|
48
46
|
def self.should_hard_destroy(subject)
|
49
|
-
should "not
|
47
|
+
should "not find #{subject} normally" do
|
50
48
|
destroyed_subject = instance_variable_get(:"@#{subject}")
|
51
49
|
assert_raises ActiveRecord::RecordNotFound do
|
52
50
|
destroyed_subject.class.find destroyed_subject.id
|
53
51
|
end
|
54
52
|
end
|
55
|
-
should "not
|
53
|
+
should "not find #{subject} in with_deleted block" do
|
56
54
|
destroyed_subject = instance_variable_get(:"@#{subject}")
|
57
55
|
destroyed_subject.class.with_deleted do
|
58
56
|
assert_raises ActiveRecord::RecordNotFound do
|
@@ -11,39 +11,84 @@ class TestDeleteParanoid < Test::Unit::TestCase
|
|
11
11
|
class Comment < ActiveRecord::Base
|
12
12
|
acts_as_paranoid
|
13
13
|
attr_accessible :text
|
14
|
+
belongs_to :blog
|
14
15
|
include CallbackTester
|
15
16
|
end
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
|
18
|
+
class User < ActiveRecord::Base
|
19
|
+
end
|
20
|
+
context 'with non-paranoid activerecord class' do
|
21
|
+
should 'not be paranoid' do
|
22
|
+
assert !User.paranoid?
|
20
23
|
end
|
21
|
-
|
22
|
-
|
24
|
+
end
|
25
|
+
context 'with paranoid activerecord class' do
|
26
|
+
should 'be paranoid' do
|
27
|
+
assert Blog.paranoid?
|
23
28
|
end
|
24
29
|
end
|
25
|
-
context 'with paranoid class' do
|
26
|
-
|
27
|
-
|
30
|
+
context 'with instance of paranoid class' do
|
31
|
+
setup do
|
32
|
+
@blog = Blog.create! :title => 'foo'
|
28
33
|
end
|
29
|
-
|
30
|
-
context 'when on instance destroyed softly' do
|
34
|
+
context 'when destroying instance' do
|
31
35
|
setup do
|
32
|
-
@
|
33
|
-
@
|
36
|
+
@now = Time.now.utc
|
37
|
+
Timecop.travel @now do
|
38
|
+
@blog.destroy
|
39
|
+
end
|
34
40
|
end
|
35
41
|
|
36
42
|
should_soft_destroy :blog
|
37
43
|
should_trigger_destroy_callbacks :blog
|
38
44
|
should_not_trigger_update_callbacks :blog
|
45
|
+
should 'save deleted_at timestamp on database record' do
|
46
|
+
blog = Blog.find_by_sql(['SELECT deleted_at FROM blogs WHERE id = ?', @blog.id]).first
|
47
|
+
assert_not_nil blog
|
48
|
+
assert_not_nil blog.deleted_at
|
49
|
+
assert_equal @now.to_i, blog.deleted_at.to_i
|
50
|
+
end
|
39
51
|
end
|
40
|
-
|
41
|
-
context 'when an instance with dependents is destroyed softly' do
|
52
|
+
context 'when destroying instance with destroy_all' do
|
42
53
|
setup do
|
43
|
-
|
44
|
-
|
45
|
-
|
54
|
+
Blog.destroy_all :id => @blog.id
|
55
|
+
end
|
56
|
+
should "not find instance normally" do
|
57
|
+
assert_raises ActiveRecord::RecordNotFound do
|
58
|
+
Blog.find @blog.id
|
59
|
+
end
|
60
|
+
end
|
61
|
+
should "find instance when in with_deleted block" do
|
62
|
+
Blog.with_deleted do
|
63
|
+
assert_nothing_raised ActiveRecord::RecordNotFound do
|
64
|
+
Blog.find @blog.id
|
65
|
+
end
|
46
66
|
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
context "when destroying instance with delete_all!" do
|
70
|
+
setup do
|
71
|
+
@blog = Blog.create! :title => 'foo'
|
72
|
+
Blog.where({:id => @blog.id}).delete_all!
|
73
|
+
end
|
74
|
+
should_hard_destroy :blog
|
75
|
+
end
|
76
|
+
context "when destroying instance with delete!" do
|
77
|
+
setup do
|
78
|
+
@blog = Blog.create! :title => 'foo'
|
79
|
+
Blog.delete! @blog.id
|
80
|
+
end
|
81
|
+
should_hard_destroy :blog
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'with paranoid instance that has dependents' do
|
86
|
+
setup do
|
87
|
+
@blog = Blog.create!(:title => 'foo')
|
88
|
+
@comment = @blog.comments.create! :text => 'bar'
|
89
|
+
end
|
90
|
+
context 'when destroying paranoid instance' do
|
91
|
+
setup do
|
47
92
|
@blog.destroy
|
48
93
|
end
|
49
94
|
|
@@ -53,36 +98,17 @@ class TestDeleteParanoid < Test::Unit::TestCase
|
|
53
98
|
|
54
99
|
should_soft_destroy :comment
|
55
100
|
should_trigger_destroy_callbacks :comment
|
56
|
-
# should_not_trigger_update_callbacks :comment
|
57
101
|
end
|
58
|
-
|
59
|
-
context "when on instance destroyed hardly" do
|
60
|
-
setup do
|
61
|
-
@blog = Blog.create! :title => 'foo'
|
62
|
-
@blog.destroy!
|
63
|
-
end
|
64
|
-
|
65
|
-
should_hard_destroy :blog
|
66
|
-
should_trigger_destroy_callbacks :blog
|
67
|
-
should_not_trigger_update_callbacks :blog
|
68
|
-
end
|
69
|
-
|
70
|
-
context 'when an instance with dependents is destroyed hardly' do
|
102
|
+
context 'when destroying paranoid instance with delete_all!' do
|
71
103
|
setup do
|
72
|
-
|
73
|
-
@blog = Blog.create!(:title => 'foo').tap do |blog|
|
74
|
-
blog.comments << @comment
|
75
|
-
end
|
76
|
-
@blog.destroy!
|
104
|
+
Blog.where({:id => @blog.id}).delete_all!
|
77
105
|
end
|
78
106
|
|
79
107
|
should_hard_destroy :blog
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
# should_not_trigger_update_callbacks :comment
|
108
|
+
should 'not destroy associated comment' do
|
109
|
+
assert_not_nil @comment.reload
|
110
|
+
assert_nil @comment.blog
|
111
|
+
end
|
85
112
|
end
|
86
|
-
|
87
113
|
end
|
88
114
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delete_paranoid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
|
+
- 1
|
7
8
|
- 0
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.3
|
10
|
+
version: 1.0.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ryan Sonnek
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-04-
|
18
|
+
date: 2011-04-05 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -106,6 +106,20 @@ dependencies:
|
|
106
106
|
version: "0"
|
107
107
|
type: :development
|
108
108
|
version_requirements: *id006
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: timecop
|
111
|
+
prerelease: false
|
112
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
type: :development
|
122
|
+
version_requirements: *id007
|
109
123
|
description: flag database records as deleted and hide them from subsequent queries
|
110
124
|
email:
|
111
125
|
- ryan@codecrate.com
|