soft_deletion 0.1.10 → 0.2.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/.gitignore +1 -0
- data/Gemfile +3 -4
- data/Gemfile.lock +13 -14
- data/Rakefile +3 -4
- data/Readme.md +5 -8
- data/gemfiles/rails2.gemfile +2 -3
- data/gemfiles/rails3.gemfile +2 -3
- data/lib/soft_deletion.rb +3 -125
- data/lib/soft_deletion/core.rb +119 -0
- data/lib/soft_deletion/setup.rb +32 -0
- data/lib/soft_deletion/version.rb +1 -1
- data/spec/soft_deletion_spec.rb +321 -0
- data/{test/test_helper.rb → spec/spec_helper.rb} +46 -18
- metadata +9 -8
- data/gemfiles/rails2.gemfile.lock +0 -33
- data/gemfiles/rails3.gemfile.lock +0 -46
- data/test/soft_deletion_test.rb +0 -286
data/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gemfiles/*.gemfile.lock
|
data/Gemfile
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
source :rubygems
|
|
2
2
|
gemspec
|
|
3
3
|
|
|
4
|
-
gem 'appraisal'
|
|
5
4
|
gem 'activerecord'
|
|
6
5
|
gem 'activesupport'
|
|
6
|
+
gem 'appraisal'
|
|
7
7
|
gem 'rake'
|
|
8
|
-
gem 'shoulda'
|
|
9
|
-
gem 'mocha'
|
|
10
8
|
gem 'sqlite3'
|
|
11
|
-
gem '
|
|
9
|
+
gem 'rspec'
|
|
10
|
+
gem 'database_cleaner'
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
soft_deletion (0.
|
|
4
|
+
soft_deletion (0.2.0)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: http://rubygems.org/
|
|
@@ -22,20 +22,20 @@ GEM
|
|
|
22
22
|
rake
|
|
23
23
|
arel (3.0.2)
|
|
24
24
|
builder (3.0.0)
|
|
25
|
+
database_cleaner (0.8.0)
|
|
26
|
+
diff-lcs (1.1.3)
|
|
25
27
|
i18n (0.6.0)
|
|
26
|
-
metaclass (0.0.1)
|
|
27
|
-
mocha (0.12.3)
|
|
28
|
-
metaclass (~> 0.0.1)
|
|
29
28
|
multi_json (1.3.6)
|
|
30
29
|
rake (0.9.2.2)
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
rspec (2.11.0)
|
|
31
|
+
rspec-core (~> 2.11.0)
|
|
32
|
+
rspec-expectations (~> 2.11.0)
|
|
33
|
+
rspec-mocks (~> 2.11.0)
|
|
34
|
+
rspec-core (2.11.1)
|
|
35
|
+
rspec-expectations (2.11.3)
|
|
36
|
+
diff-lcs (~> 1.1.3)
|
|
37
|
+
rspec-mocks (2.11.3)
|
|
37
38
|
sqlite3 (1.3.6)
|
|
38
|
-
test-unit (2.2.0)
|
|
39
39
|
tzinfo (0.3.33)
|
|
40
40
|
|
|
41
41
|
PLATFORMS
|
|
@@ -45,9 +45,8 @@ DEPENDENCIES
|
|
|
45
45
|
activerecord
|
|
46
46
|
activesupport
|
|
47
47
|
appraisal
|
|
48
|
-
|
|
48
|
+
database_cleaner
|
|
49
49
|
rake
|
|
50
|
-
|
|
50
|
+
rspec
|
|
51
51
|
soft_deletion!
|
|
52
52
|
sqlite3
|
|
53
|
-
test-unit (= 2.2)
|
data/Rakefile
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
require 'appraisal'
|
|
2
2
|
require 'bundler/gem_tasks'
|
|
3
|
+
require 'rspec/core/rake_task'
|
|
3
4
|
|
|
4
5
|
task :default do
|
|
5
|
-
sh "bundle exec rake appraisal:install && bundle exec rake appraisal
|
|
6
|
+
sh "bundle exec rake appraisal:install && bundle exec rake appraisal spec"
|
|
6
7
|
end
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
sh "ruby -Itest test/soft_deletion_test.rb"
|
|
10
|
-
end
|
|
9
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
11
10
|
|
|
12
11
|
# extracted from https://github.com/grosser/project_template
|
|
13
12
|
rule /^version:bump:.*/ do |t|
|
data/Readme.md
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
Explicit soft deletion for ActiveRecord via deleted_at and default scope
|
|
1
|
+
Explicit soft deletion for ActiveRecord via deleted_at + callbacks and optional default scope.<br/>
|
|
2
2
|
Not overwriting destroy or delete.
|
|
3
3
|
|
|
4
4
|
Install
|
|
5
5
|
=======
|
|
6
6
|
gem install soft_deletion
|
|
7
|
-
Or
|
|
8
|
-
|
|
9
|
-
rails plugin install git://github.com/grosser/soft_deletion.git
|
|
10
|
-
|
|
11
7
|
|
|
12
8
|
Usage
|
|
13
9
|
=====
|
|
14
|
-
|
|
10
|
+
require 'soft_deletion'
|
|
11
|
+
|
|
15
12
|
class User < ActiveRecord::Base
|
|
16
|
-
|
|
13
|
+
has_soft_deletion :default_scope => true
|
|
17
14
|
|
|
18
15
|
after_soft_delete :send_deletion_emails # Rails 2 + 3
|
|
19
16
|
set_callback :soft_delete, :after, :prepare_emails # Rails 3
|
|
@@ -44,7 +41,7 @@ Usage
|
|
|
44
41
|
|
|
45
42
|
TODO
|
|
46
43
|
====
|
|
47
|
-
- Rails 3
|
|
44
|
+
- Rails 3 with inspiration from https://github.com/JackDanger/permanent_records/blob/master/lib/permanent_records.rb
|
|
48
45
|
- maybe stuff from https://github.com/smoku/soft_delete
|
|
49
46
|
|
|
50
47
|
Authors
|
data/gemfiles/rails2.gemfile
CHANGED
data/gemfiles/rails3.gemfile
CHANGED
data/lib/soft_deletion.rb
CHANGED
|
@@ -1,129 +1,7 @@
|
|
|
1
1
|
require 'active_record'
|
|
2
2
|
require 'soft_deletion/version'
|
|
3
|
+
require 'soft_deletion/core'
|
|
3
4
|
require 'soft_deletion/dependency'
|
|
5
|
+
require 'soft_deletion/setup'
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
def self.included(base)
|
|
7
|
-
unless base.ancestors.include?(ActiveRecord::Base)
|
|
8
|
-
raise "You can only include this if #{base} extends ActiveRecord::Base"
|
|
9
|
-
end
|
|
10
|
-
base.extend(ClassMethods)
|
|
11
|
-
|
|
12
|
-
if base.respond_to?(:define_default_soft_delete_scope)
|
|
13
|
-
base.define_default_soft_delete_scope
|
|
14
|
-
elsif base.table_exists? && base.column_names.include?('deleted_at')
|
|
15
|
-
# Avoids a bad SQL request with versions of code without the column deleted_at (for example a migration prior to the migration
|
|
16
|
-
# that adds deleted_at)
|
|
17
|
-
base.send(:default_scope, :conditions => { :deleted_at => nil })
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# backport after_soft_delete so we can safely upgrade to rails 3
|
|
21
|
-
if ActiveRecord::VERSION::MAJOR > 2
|
|
22
|
-
base.define_callbacks :soft_delete
|
|
23
|
-
class << base
|
|
24
|
-
def before_soft_delete(*callbacks)
|
|
25
|
-
set_callback :soft_delete, :before, *callbacks
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def after_soft_delete(*callbacks)
|
|
29
|
-
set_callback :soft_delete, :after, *callbacks
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
else
|
|
33
|
-
base.define_callbacks :before_soft_delete
|
|
34
|
-
base.define_callbacks :after_soft_delete
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
module ClassMethods
|
|
39
|
-
def soft_delete_dependents
|
|
40
|
-
self.reflect_on_all_associations.
|
|
41
|
-
select { |a| [:destroy, :delete_all, :nullify].include?(a.options[:dependent]) }.
|
|
42
|
-
map(&:name)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def with_deleted
|
|
46
|
-
with_exclusive_scope do
|
|
47
|
-
yield self
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def mark_as_soft_deleted_sql
|
|
52
|
-
["deleted_at = ?", Time.now]
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def soft_delete_all!(ids_or_models)
|
|
56
|
-
ids_or_models = Array.wrap(ids_or_models)
|
|
57
|
-
|
|
58
|
-
if ids_or_models.first.is_a?(ActiveRecord::Base)
|
|
59
|
-
ids = ids_or_models.map(&:id)
|
|
60
|
-
models = ids_or_models
|
|
61
|
-
else
|
|
62
|
-
ids = ids_or_models
|
|
63
|
-
models = all(:conditions => { :id => ids })
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
transaction do
|
|
67
|
-
update_all(mark_as_soft_deleted_sql, :id => ids)
|
|
68
|
-
models.each do |model|
|
|
69
|
-
model.soft_delete_dependencies.each(&:soft_delete!)
|
|
70
|
-
model.run_callbacks ActiveRecord::VERSION::MAJOR > 2 ? :soft_delete : :after_soft_delete
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def deleted?
|
|
77
|
-
deleted_at.present?
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def mark_as_deleted
|
|
81
|
-
self.deleted_at = Time.now
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def mark_as_undeleted
|
|
85
|
-
self.deleted_at = nil
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def soft_delete!
|
|
89
|
-
_run_soft_delete { save! }
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def soft_delete(*args)
|
|
93
|
-
_run_soft_delete{ save(*args) }
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def soft_undelete!
|
|
97
|
-
self.class.transaction do
|
|
98
|
-
mark_as_undeleted
|
|
99
|
-
soft_delete_dependencies.each(&:soft_undelete!)
|
|
100
|
-
save!
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def soft_delete_dependencies
|
|
105
|
-
self.class.soft_delete_dependents.map { |dependent| Dependency.new(self, dependent) }
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
protected
|
|
109
|
-
|
|
110
|
-
def _run_soft_delete(&block)
|
|
111
|
-
self.class.transaction do
|
|
112
|
-
result = nil
|
|
113
|
-
if ActiveRecord::VERSION::MAJOR > 2
|
|
114
|
-
run_callbacks :soft_delete do
|
|
115
|
-
mark_as_deleted
|
|
116
|
-
soft_delete_dependencies.each(&:soft_delete!)
|
|
117
|
-
result = block.call
|
|
118
|
-
end
|
|
119
|
-
else
|
|
120
|
-
run_callbacks :before_soft_delete
|
|
121
|
-
mark_as_deleted
|
|
122
|
-
soft_delete_dependencies.each(&:soft_delete!)
|
|
123
|
-
result = block.call
|
|
124
|
-
run_callbacks :after_soft_delete
|
|
125
|
-
end
|
|
126
|
-
result
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
end
|
|
7
|
+
ActiveRecord::Base.send(:include, SoftDeletion::Setup)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
module SoftDeletion
|
|
2
|
+
module Core
|
|
3
|
+
def self.included(base)
|
|
4
|
+
unless base.ancestors.include?(ActiveRecord::Base)
|
|
5
|
+
raise "You can only include this if #{base} extends ActiveRecord::Base"
|
|
6
|
+
end
|
|
7
|
+
base.extend(ClassMethods)
|
|
8
|
+
|
|
9
|
+
# backport after_soft_delete so we can safely upgrade to rails 3
|
|
10
|
+
if ActiveRecord::VERSION::MAJOR > 2
|
|
11
|
+
base.define_callbacks :soft_delete
|
|
12
|
+
class << base
|
|
13
|
+
def before_soft_delete(*callbacks)
|
|
14
|
+
set_callback :soft_delete, :before, *callbacks
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def after_soft_delete(*callbacks)
|
|
18
|
+
set_callback :soft_delete, :after, *callbacks
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
base.define_callbacks :before_soft_delete
|
|
23
|
+
base.define_callbacks :after_soft_delete
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module ClassMethods
|
|
28
|
+
def soft_delete_dependents
|
|
29
|
+
self.reflect_on_all_associations.
|
|
30
|
+
select { |a| [:destroy, :delete_all, :nullify].include?(a.options[:dependent]) }.
|
|
31
|
+
map(&:name)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def with_deleted
|
|
35
|
+
with_exclusive_scope do
|
|
36
|
+
yield self
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def mark_as_soft_deleted_sql
|
|
41
|
+
["deleted_at = ?", Time.now]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def soft_delete_all!(ids_or_models)
|
|
45
|
+
ids_or_models = Array.wrap(ids_or_models)
|
|
46
|
+
|
|
47
|
+
if ids_or_models.first.is_a?(ActiveRecord::Base)
|
|
48
|
+
ids = ids_or_models.map(&:id)
|
|
49
|
+
models = ids_or_models
|
|
50
|
+
else
|
|
51
|
+
ids = ids_or_models
|
|
52
|
+
models = all(:conditions => { :id => ids })
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
transaction do
|
|
56
|
+
update_all(mark_as_soft_deleted_sql, :id => ids)
|
|
57
|
+
models.each do |model|
|
|
58
|
+
model.soft_delete_dependencies.each(&:soft_delete!)
|
|
59
|
+
model.run_callbacks ActiveRecord::VERSION::MAJOR > 2 ? :soft_delete : :after_soft_delete
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def deleted?
|
|
66
|
+
deleted_at.present?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def mark_as_deleted
|
|
70
|
+
self.deleted_at = Time.now
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def mark_as_undeleted
|
|
74
|
+
self.deleted_at = nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def soft_delete!
|
|
78
|
+
_run_soft_delete { save! }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def soft_delete(*args)
|
|
82
|
+
_run_soft_delete{ save(*args) }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def soft_undelete!
|
|
86
|
+
self.class.transaction do
|
|
87
|
+
mark_as_undeleted
|
|
88
|
+
soft_delete_dependencies.each(&:soft_undelete!)
|
|
89
|
+
save!
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def soft_delete_dependencies
|
|
94
|
+
self.class.soft_delete_dependents.map { |dependent| SoftDeletion::Dependency.new(self, dependent) }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
protected
|
|
98
|
+
|
|
99
|
+
def _run_soft_delete(&block)
|
|
100
|
+
self.class.transaction do
|
|
101
|
+
result = nil
|
|
102
|
+
if ActiveRecord::VERSION::MAJOR > 2
|
|
103
|
+
run_callbacks :soft_delete do
|
|
104
|
+
mark_as_deleted
|
|
105
|
+
soft_delete_dependencies.each(&:soft_delete!)
|
|
106
|
+
result = block.call
|
|
107
|
+
end
|
|
108
|
+
else
|
|
109
|
+
run_callbacks :before_soft_delete
|
|
110
|
+
mark_as_deleted
|
|
111
|
+
soft_delete_dependencies.each(&:soft_delete!)
|
|
112
|
+
result = block.call
|
|
113
|
+
run_callbacks :after_soft_delete
|
|
114
|
+
end
|
|
115
|
+
result
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module SoftDeletion
|
|
2
|
+
module Setup
|
|
3
|
+
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.extend ClassMethods
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
module ClassMethods
|
|
9
|
+
# When you call this, it will include the core module and its methods
|
|
10
|
+
#
|
|
11
|
+
# Options:
|
|
12
|
+
#
|
|
13
|
+
# *default_scope*, value: true/false
|
|
14
|
+
# If true, it will also define a default scope
|
|
15
|
+
#
|
|
16
|
+
# It will check if the column "deleted_at" exist before applying default scope
|
|
17
|
+
def has_soft_deletion(options={})
|
|
18
|
+
default_options = {:default_scope => false}
|
|
19
|
+
|
|
20
|
+
include SoftDeletion::Core
|
|
21
|
+
|
|
22
|
+
options = default_options.merge(options)
|
|
23
|
+
|
|
24
|
+
if options[:default_scope] && table_exists? && column_names.include?("deleted_at")
|
|
25
|
+
# Avoids a bad SQL request with versions of code without the column deleted_at (for example a migration prior to the migration
|
|
26
|
+
# that adds deleted_at)
|
|
27
|
+
default_scope :conditions => { :deleted_at => nil }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe SoftDeletion do
|
|
4
|
+
def self.successfully_soft_deletes
|
|
5
|
+
context "successfully soft deleted" do
|
|
6
|
+
before do
|
|
7
|
+
@category.soft_delete!
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should mark itself as deleted" do
|
|
11
|
+
@category.reload
|
|
12
|
+
@category.should be_deleted
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should soft delete its dependent associations" do
|
|
16
|
+
@forum.reload
|
|
17
|
+
@forum.should be_deleted
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.successfully_bulk_soft_deletes
|
|
23
|
+
context "successfully bulk soft deleted" do
|
|
24
|
+
before do
|
|
25
|
+
Category.soft_delete_all!(@category)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "should mark itself as deleted" do
|
|
29
|
+
@category.reload
|
|
30
|
+
@category.should be_deleted
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should soft delete its dependent associations" do
|
|
34
|
+
@forum.reload
|
|
35
|
+
@forum.should be_deleted
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
before do
|
|
41
|
+
clear_callbacks Category, :soft_delete
|
|
42
|
+
|
|
43
|
+
# Stub dump method calls
|
|
44
|
+
Category.any_instance.stub(:foo)
|
|
45
|
+
Category.any_instance.stub(:bar)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
describe "callbacks" do
|
|
49
|
+
describe ".before_soft_delete" do
|
|
50
|
+
it "should be called on soft-deletion" do
|
|
51
|
+
Category.before_soft_delete :foo
|
|
52
|
+
category = Category.create!
|
|
53
|
+
|
|
54
|
+
category.should_receive(:foo)
|
|
55
|
+
|
|
56
|
+
category.soft_delete!
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
describe ".after_soft_delete" do
|
|
61
|
+
it "should be called after soft-deletion" do
|
|
62
|
+
Category.after_soft_delete :foo
|
|
63
|
+
category = Category.create!
|
|
64
|
+
|
|
65
|
+
category.should_receive(:foo)
|
|
66
|
+
|
|
67
|
+
category.soft_delete!
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "should be called after bulk soft-deletion" do
|
|
71
|
+
Category.after_soft_delete :foo
|
|
72
|
+
category = Category.create!
|
|
73
|
+
|
|
74
|
+
category.should_receive(:foo)
|
|
75
|
+
|
|
76
|
+
Category.soft_delete_all!(category)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "should be call multiple after soft-deletion" do
|
|
80
|
+
Category.after_soft_delete :foo, :bar
|
|
81
|
+
category = Category.create!
|
|
82
|
+
|
|
83
|
+
category.should_receive(:foo)
|
|
84
|
+
category.should_receive(:bar)
|
|
85
|
+
|
|
86
|
+
category.soft_delete!
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "should not be called after normal destroy" do
|
|
90
|
+
Category.after_soft_delete :foo
|
|
91
|
+
category = Category.create!
|
|
92
|
+
|
|
93
|
+
category.should_not_receive(:foo)
|
|
94
|
+
|
|
95
|
+
category.destroy
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe "association" do
|
|
101
|
+
context "without dependent associations" do
|
|
102
|
+
it "should only soft-delete itself" do
|
|
103
|
+
category = NACategory.create!
|
|
104
|
+
category.soft_delete!
|
|
105
|
+
|
|
106
|
+
category.reload
|
|
107
|
+
category.should be_deleted
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
context "with independent associations" do
|
|
112
|
+
it "should not delete associations" do
|
|
113
|
+
category = IDACategory.create!
|
|
114
|
+
forum = category.forums.create!
|
|
115
|
+
category.soft_delete!
|
|
116
|
+
|
|
117
|
+
forum.reload
|
|
118
|
+
forum.should be_deleted
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
context "with dependent has_one association" do
|
|
123
|
+
before do
|
|
124
|
+
@category = HOACategory.create!
|
|
125
|
+
@forum = @category.create_forum
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
successfully_soft_deletes
|
|
129
|
+
successfully_bulk_soft_deletes
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
context "with dependent association that doesn't have soft deletion" do
|
|
133
|
+
before do
|
|
134
|
+
@category = DACategory.create!
|
|
135
|
+
@forum = @category.destroyable_forums.create!
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
context "successfully soft deleted" do
|
|
139
|
+
before do
|
|
140
|
+
@category.soft_delete!
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "should mark itself as deleted" do
|
|
144
|
+
@category.reload
|
|
145
|
+
@category.should be_deleted
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it "should not destroy dependent association" do
|
|
149
|
+
DestroyableForum.exists?(@forum.id).should be_true
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
context "with dependent has_many associations" do
|
|
155
|
+
before do
|
|
156
|
+
@category = Category.create!
|
|
157
|
+
@forum = @category.forums.create!
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
context "failing to soft delete" do
|
|
161
|
+
before do
|
|
162
|
+
@category.stub(:valid?).and_return(false)
|
|
163
|
+
expect{ @category.soft_delete! }.to raise_error(ActiveRecord::RecordInvalid)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "should not mark itself as deleted" do
|
|
167
|
+
@category.reload
|
|
168
|
+
@category.should_not be_deleted
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
it "should not soft delete its dependent associations" do
|
|
172
|
+
@forum.reload
|
|
173
|
+
@forum.should_not be_deleted
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
successfully_soft_deletes
|
|
178
|
+
successfully_bulk_soft_deletes
|
|
179
|
+
|
|
180
|
+
context "being restored from soft deletion" do
|
|
181
|
+
before do
|
|
182
|
+
@category.soft_delete!
|
|
183
|
+
Category.with_deleted do
|
|
184
|
+
@category.reload
|
|
185
|
+
@category.soft_undelete!
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
it "should not mark itself as deleted" do
|
|
190
|
+
@category.reload
|
|
191
|
+
@category.should_not be_deleted
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
it "should restore its dependent associations" do
|
|
195
|
+
@forum.reload
|
|
196
|
+
@forum.should_not be_deleted
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
context "a soft-deleted has-many category that nullifies forum references on delete" do
|
|
202
|
+
it "should nullify those references" do
|
|
203
|
+
category = NDACategory.create!
|
|
204
|
+
forum = category.forums.create!
|
|
205
|
+
category.soft_delete!
|
|
206
|
+
|
|
207
|
+
forum.reload
|
|
208
|
+
forum.should be_deleted
|
|
209
|
+
#forum.category_id.should be_nil # TODO
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
context "without deleted_at column" do
|
|
215
|
+
it "should default scope should not provoke an error" do
|
|
216
|
+
expect do
|
|
217
|
+
OriginalCategory.create!
|
|
218
|
+
end.to_not raise_error
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
describe ".soft_delete_all!" do
|
|
223
|
+
before do
|
|
224
|
+
@categories = 2.times.map { Category.create! }
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
context "by id" do
|
|
228
|
+
before do
|
|
229
|
+
Category.soft_delete_all!(@categories.map(&:id))
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
it "should delete all models" do
|
|
233
|
+
@categories.each do |category|
|
|
234
|
+
category.reload
|
|
235
|
+
category.should be_deleted
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
context "by model" do
|
|
241
|
+
before do
|
|
242
|
+
Category.soft_delete_all!(@categories)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
it "should delete all models" do
|
|
246
|
+
@categories.each do |category|
|
|
247
|
+
category.reload
|
|
248
|
+
category.should be_deleted
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
describe "overwritten default scope" do
|
|
255
|
+
it "should find even with deleted_at" do
|
|
256
|
+
forum = Cat1Forum.create(:deleted_at => Time.now)
|
|
257
|
+
|
|
258
|
+
Cat1Forum.find_by_id(forum.id).should_not be_nil
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
it "should not find by new scope" do
|
|
262
|
+
# create! does not work here on rails 2
|
|
263
|
+
forum = Cat1Forum.new
|
|
264
|
+
forum.category_id = 2
|
|
265
|
+
forum.save!
|
|
266
|
+
|
|
267
|
+
Cat1Forum.find_by_id(forum.id).should be_nil
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
describe "validations" do
|
|
272
|
+
it "should fail when validations fail" do
|
|
273
|
+
forum = ValidatedForum.create!(:category_id => 1)
|
|
274
|
+
forum.category_id = nil
|
|
275
|
+
|
|
276
|
+
expect do
|
|
277
|
+
forum.soft_delete!
|
|
278
|
+
end.to raise_error(ActiveRecord::RecordInvalid)
|
|
279
|
+
|
|
280
|
+
forum.reload
|
|
281
|
+
forum.should_not be_deleted
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
it "should pass when validations pass" do
|
|
285
|
+
forum = ValidatedForum.create!(:category_id => 1)
|
|
286
|
+
forum.soft_delete!
|
|
287
|
+
|
|
288
|
+
forum.reload
|
|
289
|
+
forum.should be_deleted
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
describe "#soft_delete" do
|
|
294
|
+
it "should return true if it succeeds" do
|
|
295
|
+
forum = ValidatedForum.create!(:category_id => 1)
|
|
296
|
+
|
|
297
|
+
forum.soft_delete.should be_true
|
|
298
|
+
forum.reload
|
|
299
|
+
forum.should be_deleted
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
it "should return false if validations fail" do
|
|
303
|
+
forum = ValidatedForum.create!(:category_id => 1)
|
|
304
|
+
forum.category_id = nil
|
|
305
|
+
|
|
306
|
+
forum.soft_delete.should be_false
|
|
307
|
+
forum.reload
|
|
308
|
+
forum.should_not be_deleted
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
it "should return true if validations are prevented and it succeeds" do
|
|
312
|
+
forum = ValidatedForum.create!(:category_id => 1)
|
|
313
|
+
forum.category_id = nil
|
|
314
|
+
skip_validations = (ActiveRecord::VERSION::MAJOR == 2 ? false : {:validate => false})
|
|
315
|
+
|
|
316
|
+
forum.soft_delete(skip_validations).should be_true
|
|
317
|
+
forum.reload
|
|
318
|
+
forum.should be_deleted
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
end
|
|
@@ -1,8 +1,25 @@
|
|
|
1
|
-
require 'active_support/test_case'
|
|
2
|
-
require 'shoulda'
|
|
3
1
|
require 'active_record'
|
|
2
|
+
require 'soft_deletion'
|
|
3
|
+
require 'database_cleaner'
|
|
4
|
+
require 'logger'
|
|
5
|
+
|
|
6
|
+
# ActiveRecord::Base.logger = Logger.new(STDOUT) # for easier debugging
|
|
7
|
+
|
|
8
|
+
RSpec.configure do |config|
|
|
9
|
+
config.before(:suite) do
|
|
10
|
+
# would be nicer to use transaction, but Rails 2 does not open a new transaction or savepoint
|
|
11
|
+
# when already inside a transaction
|
|
12
|
+
DatabaseCleaner.strategy = :truncation
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
config.before do
|
|
16
|
+
DatabaseCleaner.start
|
|
17
|
+
end
|
|
4
18
|
|
|
5
|
-
|
|
19
|
+
config.after do
|
|
20
|
+
DatabaseCleaner.clean
|
|
21
|
+
end
|
|
22
|
+
end
|
|
6
23
|
|
|
7
24
|
def clear_callbacks(model, callback)
|
|
8
25
|
if ActiveRecord::VERSION::MAJOR > 2
|
|
@@ -47,61 +64,73 @@ class ActiveRecord::Base
|
|
|
47
64
|
end
|
|
48
65
|
|
|
49
66
|
# setup models
|
|
50
|
-
require 'soft_deletion'
|
|
51
67
|
|
|
52
68
|
class Forum < ActiveRecord::Base
|
|
53
|
-
|
|
69
|
+
has_soft_deletion
|
|
70
|
+
|
|
54
71
|
belongs_to :category
|
|
55
72
|
end
|
|
56
73
|
|
|
57
74
|
class ValidatedForum < ActiveRecord::Base
|
|
58
75
|
silent_set_table_name 'forums'
|
|
59
|
-
|
|
76
|
+
|
|
77
|
+
has_soft_deletion
|
|
78
|
+
|
|
60
79
|
belongs_to :category
|
|
61
80
|
validates_presence_of :category_id
|
|
62
81
|
end
|
|
63
82
|
|
|
64
83
|
class Category < ActiveRecord::Base
|
|
65
|
-
|
|
84
|
+
has_soft_deletion
|
|
85
|
+
|
|
66
86
|
has_many :forums, :dependent => :destroy
|
|
67
87
|
end
|
|
68
88
|
|
|
69
89
|
# No association
|
|
70
90
|
class NACategory < ActiveRecord::Base
|
|
71
91
|
silent_set_table_name 'categories'
|
|
72
|
-
|
|
92
|
+
|
|
93
|
+
has_soft_deletion
|
|
73
94
|
end
|
|
74
95
|
|
|
75
96
|
# Independent association
|
|
76
97
|
class IDACategory < ActiveRecord::Base
|
|
77
98
|
silent_set_table_name 'categories'
|
|
78
|
-
|
|
99
|
+
|
|
100
|
+
has_soft_deletion
|
|
101
|
+
|
|
79
102
|
has_many :forums, :dependent => :destroy, :foreign_key => :category_id
|
|
80
103
|
end
|
|
81
104
|
|
|
82
105
|
# Nullified dependent association
|
|
83
106
|
class NDACategory < ActiveRecord::Base
|
|
84
107
|
silent_set_table_name 'categories'
|
|
85
|
-
|
|
108
|
+
|
|
109
|
+
has_soft_deletion
|
|
110
|
+
|
|
86
111
|
has_many :forums, :dependent => :destroy, :foreign_key => :category_id
|
|
87
112
|
end
|
|
88
113
|
|
|
89
114
|
# Has ome association
|
|
90
115
|
class HOACategory < ActiveRecord::Base
|
|
91
116
|
silent_set_table_name 'categories'
|
|
92
|
-
|
|
117
|
+
|
|
118
|
+
has_soft_deletion
|
|
119
|
+
|
|
93
120
|
has_one :forum, :dependent => :destroy, :foreign_key => :category_id
|
|
94
121
|
end
|
|
95
122
|
|
|
96
123
|
# Class without column deleted_at
|
|
97
124
|
class OriginalCategory < ActiveRecord::Base
|
|
98
|
-
|
|
125
|
+
has_soft_deletion
|
|
99
126
|
end
|
|
100
127
|
|
|
101
128
|
# Has many destroyable association
|
|
102
129
|
class DACategory < ActiveRecord::Base
|
|
103
130
|
silent_set_table_name 'categories'
|
|
104
|
-
|
|
131
|
+
|
|
132
|
+
has_soft_deletion
|
|
133
|
+
|
|
105
134
|
has_many :destroyable_forums, :dependent => :destroy, :foreign_key => :category_id
|
|
106
135
|
end
|
|
107
136
|
|
|
@@ -112,18 +141,17 @@ end
|
|
|
112
141
|
|
|
113
142
|
# test that it does not blow up when the table is not yet defined (e.g. in rake db:reset)
|
|
114
143
|
class NoTable < ActiveRecord::Base
|
|
115
|
-
|
|
144
|
+
has_soft_deletion
|
|
116
145
|
end
|
|
117
146
|
|
|
118
147
|
# Forum with other default scope
|
|
119
148
|
class Cat1Forum < ActiveRecord::Base
|
|
120
149
|
silent_set_table_name 'forums'
|
|
121
150
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
151
|
+
has_soft_deletion
|
|
152
|
+
default_scope :conditions => {:category_id => 1}
|
|
153
|
+
|
|
125
154
|
|
|
126
|
-
include SoftDeletion
|
|
127
155
|
belongs_to :category
|
|
128
156
|
end
|
|
129
157
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: soft_deletion
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-
|
|
12
|
+
date: 2012-10-15 00:00:00.000000000 Z
|
|
13
13
|
dependencies: []
|
|
14
14
|
description:
|
|
15
15
|
email: michael@grosser.it
|
|
@@ -17,6 +17,7 @@ executables: []
|
|
|
17
17
|
extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
|
19
19
|
files:
|
|
20
|
+
- .gitignore
|
|
20
21
|
- .travis.yml
|
|
21
22
|
- Appraisals
|
|
22
23
|
- Gemfile
|
|
@@ -25,15 +26,15 @@ files:
|
|
|
25
26
|
- Rakefile
|
|
26
27
|
- Readme.md
|
|
27
28
|
- gemfiles/rails2.gemfile
|
|
28
|
-
- gemfiles/rails2.gemfile.lock
|
|
29
29
|
- gemfiles/rails3.gemfile
|
|
30
|
-
- gemfiles/rails3.gemfile.lock
|
|
31
30
|
- lib/soft_deletion.rb
|
|
31
|
+
- lib/soft_deletion/core.rb
|
|
32
32
|
- lib/soft_deletion/dependency.rb
|
|
33
|
+
- lib/soft_deletion/setup.rb
|
|
33
34
|
- lib/soft_deletion/version.rb
|
|
34
35
|
- soft_deletion.gemspec
|
|
35
|
-
-
|
|
36
|
-
-
|
|
36
|
+
- spec/soft_deletion_spec.rb
|
|
37
|
+
- spec/spec_helper.rb
|
|
37
38
|
homepage: http://github.com/grosser/soft_deletion
|
|
38
39
|
licenses:
|
|
39
40
|
- MIT
|
|
@@ -49,7 +50,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
49
50
|
version: '0'
|
|
50
51
|
segments:
|
|
51
52
|
- 0
|
|
52
|
-
hash: -
|
|
53
|
+
hash: -3275884841840712801
|
|
53
54
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
55
|
none: false
|
|
55
56
|
requirements:
|
|
@@ -58,7 +59,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
58
59
|
version: '0'
|
|
59
60
|
segments:
|
|
60
61
|
- 0
|
|
61
|
-
hash: -
|
|
62
|
+
hash: -3275884841840712801
|
|
62
63
|
requirements: []
|
|
63
64
|
rubyforge_project:
|
|
64
65
|
rubygems_version: 1.8.24
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
PATH
|
|
2
|
-
remote: /Users/mgrosser/code/tools/soft_deletion
|
|
3
|
-
specs:
|
|
4
|
-
soft_deletion (0.1.9)
|
|
5
|
-
|
|
6
|
-
GEM
|
|
7
|
-
remote: http://rubygems.org/
|
|
8
|
-
specs:
|
|
9
|
-
activerecord (2.3.14)
|
|
10
|
-
activesupport (= 2.3.14)
|
|
11
|
-
activesupport (2.3.14)
|
|
12
|
-
appraisal (0.4.1)
|
|
13
|
-
bundler
|
|
14
|
-
rake
|
|
15
|
-
mocha (0.9.12)
|
|
16
|
-
rake (0.9.2.2)
|
|
17
|
-
shoulda (2.10.3)
|
|
18
|
-
sqlite3 (1.3.6)
|
|
19
|
-
test-unit (2.2.0)
|
|
20
|
-
|
|
21
|
-
PLATFORMS
|
|
22
|
-
ruby
|
|
23
|
-
|
|
24
|
-
DEPENDENCIES
|
|
25
|
-
activerecord (= 2.3.14)
|
|
26
|
-
activesupport (= 2.3.14)
|
|
27
|
-
appraisal
|
|
28
|
-
mocha
|
|
29
|
-
rake
|
|
30
|
-
shoulda
|
|
31
|
-
soft_deletion!
|
|
32
|
-
sqlite3
|
|
33
|
-
test-unit (= 2.2)
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
PATH
|
|
2
|
-
remote: /Users/mgrosser/code/tools/soft_deletion
|
|
3
|
-
specs:
|
|
4
|
-
soft_deletion (0.1.9)
|
|
5
|
-
|
|
6
|
-
GEM
|
|
7
|
-
remote: http://rubygems.org/
|
|
8
|
-
specs:
|
|
9
|
-
activemodel (3.2.3)
|
|
10
|
-
activesupport (= 3.2.3)
|
|
11
|
-
builder (~> 3.0.0)
|
|
12
|
-
activerecord (3.2.3)
|
|
13
|
-
activemodel (= 3.2.3)
|
|
14
|
-
activesupport (= 3.2.3)
|
|
15
|
-
arel (~> 3.0.2)
|
|
16
|
-
tzinfo (~> 0.3.29)
|
|
17
|
-
activesupport (3.2.3)
|
|
18
|
-
i18n (~> 0.6)
|
|
19
|
-
multi_json (~> 1.0)
|
|
20
|
-
appraisal (0.4.1)
|
|
21
|
-
bundler
|
|
22
|
-
rake
|
|
23
|
-
arel (3.0.2)
|
|
24
|
-
builder (3.0.0)
|
|
25
|
-
i18n (0.6.0)
|
|
26
|
-
mocha (0.9.12)
|
|
27
|
-
multi_json (1.3.4)
|
|
28
|
-
rake (0.9.2.2)
|
|
29
|
-
shoulda (2.10.3)
|
|
30
|
-
sqlite3 (1.3.6)
|
|
31
|
-
test-unit (2.2.0)
|
|
32
|
-
tzinfo (0.3.33)
|
|
33
|
-
|
|
34
|
-
PLATFORMS
|
|
35
|
-
ruby
|
|
36
|
-
|
|
37
|
-
DEPENDENCIES
|
|
38
|
-
activerecord (= 3.2.3)
|
|
39
|
-
activesupport (= 3.2.3)
|
|
40
|
-
appraisal
|
|
41
|
-
mocha
|
|
42
|
-
rake
|
|
43
|
-
shoulda
|
|
44
|
-
soft_deletion!
|
|
45
|
-
sqlite3
|
|
46
|
-
test-unit (= 2.2)
|
data/test/soft_deletion_test.rb
DELETED
|
@@ -1,286 +0,0 @@
|
|
|
1
|
-
require File.expand_path '../test_helper', __FILE__
|
|
2
|
-
|
|
3
|
-
class SoftDeletionTest < ActiveSupport::TestCase
|
|
4
|
-
def assert_deleted(resource)
|
|
5
|
-
resource.class.with_deleted do
|
|
6
|
-
resource.reload
|
|
7
|
-
assert resource.deleted?
|
|
8
|
-
end
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def assert_not_deleted(resource)
|
|
12
|
-
resource.reload
|
|
13
|
-
assert !resource.deleted?
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def self.successfully_soft_deletes
|
|
17
|
-
context "successfully soft deleted" do
|
|
18
|
-
setup do
|
|
19
|
-
@category.soft_delete!
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
should "mark itself as deleted" do
|
|
23
|
-
assert_deleted @category
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
should "soft delete its dependent associations" do
|
|
27
|
-
assert_deleted @forum
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def self.successfully_bulk_soft_deletes
|
|
33
|
-
context "successfully bulk soft deleted" do
|
|
34
|
-
setup do
|
|
35
|
-
Category.soft_delete_all!(@category)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
should "mark itself as deleted" do
|
|
39
|
-
assert_deleted @category
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
should "soft delete its dependent associations" do
|
|
43
|
-
assert_deleted @forum
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
setup do
|
|
49
|
-
clear_callbacks Category, :soft_delete
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
context ".before_soft_delete" do
|
|
53
|
-
should "be called on soft-deletion" do
|
|
54
|
-
Category.before_soft_delete :foo
|
|
55
|
-
category = Category.create!
|
|
56
|
-
category.expects(:foo)
|
|
57
|
-
category.soft_delete!
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
context ".after_soft_delete" do
|
|
62
|
-
should "be called after soft-deletion" do
|
|
63
|
-
Category.after_soft_delete :foo
|
|
64
|
-
category = Category.create!
|
|
65
|
-
category.expects(:foo)
|
|
66
|
-
category.soft_delete!
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
should "be called after bulk soft-deletion" do
|
|
70
|
-
Category.after_soft_delete :foo
|
|
71
|
-
category = Category.create!
|
|
72
|
-
category.expects(:foo)
|
|
73
|
-
Category.soft_delete_all!(category)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
should "be call multiple after soft-deletion" do
|
|
77
|
-
Category.after_soft_delete :foo, :bar
|
|
78
|
-
category = Category.create!
|
|
79
|
-
category.expects(:foo)
|
|
80
|
-
category.expects(:bar)
|
|
81
|
-
category.soft_delete!
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
should "not be called after normal destroy" do
|
|
85
|
-
Category.after_soft_delete :foo
|
|
86
|
-
category = Category.create!
|
|
87
|
-
category.expects(:foo).never
|
|
88
|
-
category.destroy
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
context "without dependent associations" do
|
|
93
|
-
should "only soft-delete itself" do
|
|
94
|
-
category = NACategory.create!
|
|
95
|
-
category.soft_delete!
|
|
96
|
-
assert_deleted category
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
context "with independent associations" do
|
|
101
|
-
should "not delete associations" do
|
|
102
|
-
category = IDACategory.create!
|
|
103
|
-
forum = category.forums.create!
|
|
104
|
-
category.soft_delete!
|
|
105
|
-
assert_deleted forum
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
context "with dependent has_one association" do
|
|
110
|
-
setup do
|
|
111
|
-
@category = HOACategory.create!
|
|
112
|
-
@forum = @category.create_forum
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
successfully_soft_deletes
|
|
116
|
-
successfully_bulk_soft_deletes
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
context "with dependent association that doesn't have soft deletion" do
|
|
120
|
-
setup do
|
|
121
|
-
@category = DACategory.create!
|
|
122
|
-
@forum = @category.destroyable_forums.create!
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
context "successfully soft deleted" do
|
|
126
|
-
setup do
|
|
127
|
-
@category.soft_delete!
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
should "mark itself as deleted" do
|
|
131
|
-
assert_deleted @category
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
should "not destroy dependent association" do
|
|
135
|
-
assert DestroyableForum.exists?(@forum.id)
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
context "with dependent has_many associations" do
|
|
141
|
-
setup do
|
|
142
|
-
@category = Category.create!
|
|
143
|
-
@forum = @category.forums.create!
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
context "failing to soft delete" do
|
|
147
|
-
setup do
|
|
148
|
-
@category.stubs(:valid?).returns(false)
|
|
149
|
-
assert_raise(ActiveRecord::RecordInvalid) { @category.soft_delete! }
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
should "not mark itself as deleted" do
|
|
153
|
-
assert_not_deleted @category
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
should "not soft delete its dependent associations" do
|
|
157
|
-
assert_not_deleted @forum
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
successfully_soft_deletes
|
|
162
|
-
successfully_bulk_soft_deletes
|
|
163
|
-
|
|
164
|
-
context "being restored from soft deletion" do
|
|
165
|
-
setup do
|
|
166
|
-
@category.soft_delete!
|
|
167
|
-
Category.with_deleted do
|
|
168
|
-
@category.reload
|
|
169
|
-
@category.soft_undelete!
|
|
170
|
-
@category.reload
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
should "not mark itself as deleted" do
|
|
175
|
-
assert_not_deleted @category
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
should "restore its dependent associations" do
|
|
179
|
-
assert_not_deleted @forum
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
context "a soft-deleted has-many category that nullifies forum references on delete" do
|
|
185
|
-
should "nullify those references" do
|
|
186
|
-
category = NDACategory.create!
|
|
187
|
-
forum = category.forums.create!
|
|
188
|
-
category.soft_delete!
|
|
189
|
-
assert_deleted forum
|
|
190
|
-
#assert_nil forum.category_id # TODO
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
|
|
194
|
-
context "without deleted_at column" do
|
|
195
|
-
should "default scope should not provoke an error" do
|
|
196
|
-
assert_nothing_raised do
|
|
197
|
-
OriginalCategory.create!
|
|
198
|
-
end
|
|
199
|
-
end
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
context ".soft_delete_all!" do
|
|
203
|
-
setup do
|
|
204
|
-
@categories = 2.times.map { Category.create! }
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
context "by id" do
|
|
208
|
-
setup do
|
|
209
|
-
Category.soft_delete_all!(@categories.map(&:id))
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
should "delete all models" do
|
|
213
|
-
@categories.each do |category|
|
|
214
|
-
assert_deleted category
|
|
215
|
-
end
|
|
216
|
-
end
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
context "by model" do
|
|
220
|
-
setup do
|
|
221
|
-
Category.soft_delete_all!(@categories)
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
should "delete all models" do
|
|
225
|
-
@categories.each do |category|
|
|
226
|
-
assert_deleted category
|
|
227
|
-
end
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
context "overwritten default scope" do
|
|
233
|
-
should "find even with deleted_at" do
|
|
234
|
-
forum = Cat1Forum.create(:deleted_at => Time.now)
|
|
235
|
-
assert Cat1Forum.find_by_id(forum.id)
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
should "not find by new scope" do
|
|
239
|
-
# create! does not work here on rails 2
|
|
240
|
-
forum = Cat1Forum.new
|
|
241
|
-
forum.category_id = 2
|
|
242
|
-
forum.save!
|
|
243
|
-
assert_nil Cat1Forum.find_by_id(forum.id)
|
|
244
|
-
end
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
context "validations" do
|
|
248
|
-
should "fail when validations fail" do
|
|
249
|
-
forum = ValidatedForum.create!(:category_id => 1)
|
|
250
|
-
forum.category_id = nil
|
|
251
|
-
assert_raise ActiveRecord::RecordInvalid do
|
|
252
|
-
forum.soft_delete!
|
|
253
|
-
end
|
|
254
|
-
assert_not_deleted forum
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
should "pass when validations pass" do
|
|
258
|
-
forum = ValidatedForum.create!(:category_id => 1)
|
|
259
|
-
forum.soft_delete!
|
|
260
|
-
assert_deleted forum
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
context "#soft_delete" do
|
|
265
|
-
should "return true if it succeeds" do
|
|
266
|
-
forum = ValidatedForum.create!(:category_id => 1)
|
|
267
|
-
assert_equal true, forum.soft_delete
|
|
268
|
-
assert_deleted forum
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
should "return false if validations fail" do
|
|
272
|
-
forum = ValidatedForum.create!(:category_id => 1)
|
|
273
|
-
forum.category_id = nil
|
|
274
|
-
assert_equal false, forum.soft_delete
|
|
275
|
-
assert_not_deleted forum
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
should "return true if validations are prevented and it succeeds" do
|
|
279
|
-
forum = ValidatedForum.create!(:category_id => 1)
|
|
280
|
-
forum.category_id = nil
|
|
281
|
-
skip_validations = (ActiveRecord::VERSION::MAJOR == 2 ? false : {:validate => false})
|
|
282
|
-
assert_equal true, forum.soft_delete(skip_validations)
|
|
283
|
-
assert_deleted forum
|
|
284
|
-
end
|
|
285
|
-
end
|
|
286
|
-
end
|