delete_paranoid 1.0.1 → 1.0.2

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.
@@ -5,8 +5,11 @@ module ActiveRecord
5
5
  class Relation
6
6
  alias_method :delete_all!, :delete_all
7
7
  def delete_all(conditions = nil)
8
- delete_all!(conditions) unless @klass.paranoid?
9
- update_all({:deleted_at => Time.now.utc}, conditions)
8
+ if @klass.paranoid?
9
+ update_all({:deleted_at => Time.now.utc}, conditions)
10
+ else
11
+ delete_all!(conditions)
12
+ end
10
13
  end
11
14
  end
12
15
  end
@@ -1,3 +1,3 @@
1
1
  module DeleteParanoid
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.2"
3
3
  end
@@ -14,5 +14,30 @@ ActiveRecord::Schema.define(:version => 2) do
14
14
  t.column :text, :string
15
15
  t.column :deleted_at, :timestamp
16
16
  end
17
+ create_table :links, :force => true do |t|
18
+ t.column :blog_id, :integer
19
+ t.column :name, :string
20
+ end
21
+ end
22
+
23
+ class Blog < ActiveRecord::Base
24
+ has_many :comments, :dependent => :destroy
25
+ has_many :links, :dependent => :destroy
26
+ acts_as_paranoid
27
+ attr_accessible :title
28
+ include CallbackMatcher::ActiveRecordHooks
29
+ end
30
+
31
+ class Comment < ActiveRecord::Base
32
+ acts_as_paranoid
33
+ attr_accessible :text
34
+ belongs_to :blog
35
+ include CallbackMatcher::ActiveRecordHooks
36
+ end
37
+
38
+ class Link < ActiveRecord::Base
39
+ belongs_to :blog
40
+ attr_accessible :name
41
+ include CallbackMatcher::ActiveRecordHooks
17
42
  end
18
43
 
data/test/helper.rb CHANGED
@@ -16,130 +16,12 @@ require 'timecop'
16
16
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
17
17
  $LOAD_PATH.unshift(File.dirname(__FILE__))
18
18
  require 'delete_paranoid'
19
+ require 'matchers/destroy_matcher'
20
+ require 'matchers/callback_matcher'
19
21
  require 'database_setup'
20
22
 
21
23
  class Test::Unit::TestCase
22
-
23
- def self.should_soft_destroy(subject)
24
- should "set #{subject} to destroyed" do
25
- assert instance_variable_get(:"@#{subject}").destroyed?
26
- end
27
- should "freeze #{subject}" do
28
- assert instance_variable_get(:"@#{subject}").frozen?
29
- end
30
- should "not find #{subject} normally" do
31
- destroyed_subject = instance_variable_get(:"@#{subject}")
32
- assert_raises ActiveRecord::RecordNotFound do
33
- destroyed_subject.class.find destroyed_subject.id
34
- end
35
- end
36
- should "find #{subject} when in with_deleted block" do
37
- destroyed_subject = instance_variable_get(:"@#{subject}")
38
- destroyed_subject.class.with_deleted do
39
- assert_nothing_raised ActiveRecord::RecordNotFound do
40
- assert_not_nil destroyed_subject.class.find destroyed_subject.id
41
- end
42
- end
43
- end
44
- end
45
-
46
- def self.should_hard_destroy(subject)
47
- should "not find #{subject} normally" do
48
- destroyed_subject = instance_variable_get(:"@#{subject}")
49
- assert_raises ActiveRecord::RecordNotFound do
50
- destroyed_subject.class.find destroyed_subject.id
51
- end
52
- end
53
- should "not find #{subject} in with_deleted block" do
54
- destroyed_subject = instance_variable_get(:"@#{subject}")
55
- destroyed_subject.class.with_deleted do
56
- assert_raises ActiveRecord::RecordNotFound do
57
- destroyed_subject.class.find destroyed_subject.id
58
- end
59
- end
60
- end
61
- end
62
-
63
- def self.should_trigger_destroy_callbacks(subject)
64
- should "call before_destroy callbacks on #{subject}" do
65
- assert instance_variable_get(:"@#{subject}").called_before_destroy
66
- end
67
- should "call after_destroy callbacks on #{subject}" do
68
- assert instance_variable_get(:"@#{subject}").called_after_destroy
69
- end
70
- should "call after_commit_on_destroy callbacks on #{subject}" do
71
- assert instance_variable_get(:"@#{subject}").called_after_commit_on_destroy
72
- end
73
- end
74
-
75
- def self.should_not_trigger_update_callbacks(subject)
76
- should "not call before_update callbacks on #{subject}" do
77
- assert !instance_variable_get(:"@#{subject}").called_before_update
78
- end
79
- should "not call after_update callbacks on #{subject}" do
80
- assert !instance_variable_get(:"@#{subject}").called_after_update
81
- end
82
- should "not call after_commit_on_update callbacks on #{subject}" do
83
- assert !instance_variable_get(:"@#{subject}").called_after_commit_on_update
84
- end
85
- end
86
-
24
+ extend DestroyMatcher::MatcherMethods
25
+ extend CallbackMatcher::MatcherMethods
87
26
  end
88
27
 
89
-
90
- module CallbackTester
91
- ATTRS = %w{ called_before_destroy called_after_destroy called_after_commit_on_destroy called_before_update called_after_update called_after_commit_on_update }
92
-
93
- def self.included(base)
94
- base.class_eval do
95
- attr_accessor *ATTRS
96
-
97
- before_update :call_me_before_update
98
- after_update :call_me_after_update
99
-
100
- before_destroy :call_me_before_destroy
101
- after_destroy :call_me_after_destroy
102
-
103
- after_commit :call_me_after_commit_on_destroy, :on => :destroy
104
- after_commit :call_me_after_commit_on_update, :on => :update
105
-
106
- alias_method_chain :initialize, :callback_init
107
- end
108
- end
109
-
110
- def initialize_with_callback_init(*args)
111
- reset_callback_flags!
112
- initialize_without_callback_init(*args)
113
- end
114
-
115
- def reset_callback_flags!
116
- ATTRS.each do |attr|
117
- send("#{attr}=", false)
118
- end
119
- end
120
-
121
- def call_me_before_destroy
122
- @called_before_destroy = true
123
- end
124
-
125
- def call_me_after_destroy
126
- @called_after_destroy = true
127
- end
128
-
129
- def call_me_after_commit_on_destroy
130
- @called_after_commit_on_destroy = true
131
- end
132
-
133
- def call_me_before_update
134
- @called_before_update = true
135
- end
136
-
137
- def call_me_after_update
138
- @called_after_update = true
139
- end
140
-
141
- def call_me_after_commit_on_update
142
- @called_after_commit_on_update = true
143
- end
144
-
145
- end
@@ -0,0 +1,90 @@
1
+
2
+ class CallbackMatcher
3
+ CALLBACK_EVENTS = [:before, :after, :after_commit_on]
4
+ CALLBACK_TYPES = [:create, :update, :destroy, :save]
5
+
6
+ module MatcherMethods
7
+
8
+ def trigger_callbacks_for(callback_types)
9
+ CallbackMatcher.new Array.wrap(callback_types)
10
+ end
11
+
12
+ end
13
+
14
+ module ActiveRecordHooks
15
+
16
+ def self.included(base)
17
+ base.class_eval do
18
+ class << self
19
+ attr_accessor :callback_tester_attrs
20
+ end
21
+ @callback_tester_attrs = []
22
+ CALLBACK_EVENTS.each do |ce|
23
+ CALLBACK_TYPES.each do |ct|
24
+ callback_name = :"#{ce}_#{ct}"
25
+ callback_attr = :"called_#{callback_name}"
26
+ callback_method, has_on_option = (ce.to_s =~ /_on/ ? [ce.to_s.gsub('_on',''), true] : [callback_name, false])
27
+ @callback_tester_attrs << callback_attr
28
+ attr_accessor callback_attr
29
+ send( callback_method, (has_on_option ? {:on => ct} : {})) {
30
+ instance_variable_set(:"@#{callback_attr}", true)
31
+ }
32
+
33
+ define_method :"#{callback_attr}?" do
34
+ instance_variable_get(:"@#{callback_attr}")
35
+ end
36
+ end
37
+ end
38
+ alias_method_chain :initialize, :callback_init
39
+ end
40
+ end
41
+
42
+ def initialize_with_callback_init(*args)
43
+ reset_callback_flags!
44
+ initialize_without_callback_init(*args)
45
+ end
46
+
47
+ def reset_callback_flags!
48
+ self.class.callback_tester_attrs.each do |attr|
49
+ send("#{attr}=", false)
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ def initialize(callback_types)
56
+ @callback_types = callback_types
57
+ end
58
+
59
+ def failure_message
60
+ "Expected #{@subject} #{expectation}:"
61
+ end
62
+
63
+ def negative_failure_message
64
+ "Did not expect #{@subject} #{expectation}:"
65
+ end
66
+
67
+ def description
68
+ "check that #{@callback_types.join(', ')} callbacks were called"
69
+ end
70
+
71
+ def expectation
72
+ @expectations.join("\n")
73
+ end
74
+
75
+ def matches?(subject)
76
+ @subject = subject
77
+ @expectations = []
78
+ result = true
79
+ @callback_types.each do |ct|
80
+ CALLBACK_EVENTS.each do |ce|
81
+ called = @subject.send(:"called_#{ce}_#{ct}?")
82
+ result &&= called
83
+ @expectations << "#{ce}_#{ct} callbacks to be triggered"
84
+ end
85
+ end
86
+ result
87
+ end
88
+
89
+ end
90
+
@@ -0,0 +1,80 @@
1
+ module DeleteParanoid
2
+ class DestroyMatcher
3
+ def softly
4
+ @softly = true
5
+ self
6
+ end
7
+ def and_freeze
8
+ @frozen = true
9
+ self
10
+ end
11
+ def and_mark_as_destroyed
12
+ @destroyed = true
13
+ self
14
+ end
15
+ def description
16
+ "destroy the subject"
17
+ end
18
+ def matches?(subject)
19
+ @subject = subject
20
+ errors.empty?
21
+ end
22
+ def failure_message
23
+ "Expected #{@subject.inspect} to be destroyed: #{errors.join("\n")}"
24
+ end
25
+ def errors
26
+ return @errors if @errors
27
+ @errors = []
28
+ @errors << "was found in database" if subject_found?
29
+ @errors << "was not found with_deleted in database" if softly? && !subject_found_with_deleted?
30
+ @errors << "did not populate deleted_at timestamp" if softly? && !subject_deleted_at?
31
+ @errors << "did not freeze instance" if frozen? && !@subject.frozen?
32
+ @errors << "did not destroy instance" if destroyed? && !@subject.destroyed?
33
+ @errors
34
+ end
35
+ def softly?
36
+ !!@softly
37
+ end
38
+ def frozen?
39
+ !!@frozen
40
+ end
41
+ def destroyed?
42
+ !!@destroyed
43
+ end
44
+ def subject_found?
45
+ !!@subject.class.find(@subject.id)
46
+ rescue ActiveRecord::RecordNotFound
47
+ false
48
+ end
49
+ def subject_found_with_deleted?
50
+ @subject.class.with_deleted do
51
+ !!@subject.class.find(@subject.id)
52
+ end
53
+ rescue ActiveRecord::RecordNotFound
54
+ false
55
+ end
56
+ def subject_deleted_at?
57
+ @subject.class.with_deleted do
58
+ @subject.class.find(@subject.id).deleted_at
59
+ end
60
+ rescue ActiveRecord::RecordNotFound
61
+ false
62
+ end
63
+
64
+ end
65
+ end
66
+
67
+ class DestroyMatcher
68
+ module MatcherMethods
69
+ def soft_destroy
70
+ DestroyMatcher.new :soft
71
+ end
72
+ def hard_destroy
73
+ DestroyMatcher.new :hard
74
+ end
75
+ def destroy_subject
76
+ DeleteParanoid::DestroyMatcher.new
77
+ end
78
+ end
79
+ end
80
+
@@ -1,125 +1,119 @@
1
1
  require File.join(File.dirname(__FILE__), 'helper')
2
2
 
3
3
  class TestDeleteParanoid < Test::Unit::TestCase
4
- class Blog < ActiveRecord::Base
5
- has_many :comments, :dependent => :destroy
6
- acts_as_paranoid
7
- attr_accessible :title
8
- include CallbackTester
9
- end
10
-
11
- class Comment < ActiveRecord::Base
12
- acts_as_paranoid
13
- attr_accessible :text
14
- belongs_to :blog
15
- include CallbackTester
16
- end
17
-
18
- class User < ActiveRecord::Base
19
- end
20
4
  context 'with non-paranoid activerecord class' do
21
5
  should 'not be paranoid' do
22
- assert !User.paranoid?
6
+ assert !Link.paranoid?
23
7
  end
24
8
  end
9
+
25
10
  context 'with paranoid activerecord class' do
26
11
  should 'be paranoid' do
27
12
  assert Blog.paranoid?
28
13
  end
29
14
  end
15
+
30
16
  context 'with instance of paranoid class' do
31
17
  setup do
32
18
  @blog = Blog.create! :title => 'foo'
33
19
  end
34
20
  context 'when destroying instance with instance.destroy' do
35
- setup do
36
- @now = Time.now.utc
37
- Timecop.travel @now do
38
- @blog.destroy
39
- end
21
+ subject do
22
+ @blog.destroy
23
+ @blog
40
24
  end
41
25
 
42
- should_soft_destroy :blog
43
- should_trigger_destroy_callbacks :blog
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
26
+ should destroy_subject.softly.and_freeze.and_mark_as_destroyed
27
+ should trigger_callbacks_for :destroy
28
+ should_not trigger_callbacks_for :update
51
29
  end
52
30
  context 'when destroying instance with Class.destroy_all' do
53
- setup do
31
+ subject do
54
32
  Blog.destroy_all :id => @blog.id
33
+ @blog
55
34
  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
66
- end
67
- end
35
+ should destroy_subject.softly
68
36
  end
69
37
  context "when destroying instance with Class.delete_all!" do
70
- setup do
38
+ subject do
71
39
  Blog.where({:id => @blog.id}).delete_all!
40
+ @blog
72
41
  end
73
- should_hard_destroy :blog
42
+ should destroy_subject
74
43
  end
75
44
  context "when destroying instance with Class.delete!" do
76
- setup do
45
+ subject do
77
46
  Blog.delete! @blog.id
47
+ @blog
78
48
  end
79
- should_hard_destroy :blog
49
+ should destroy_subject
80
50
  end
81
51
  context 'when destroying instance with instance.destroy!' do
82
- setup do
52
+ subject do
83
53
  @blog.destroy!
54
+ @blog
84
55
  end
85
- should_hard_destroy :blog
86
- should_trigger_destroy_callbacks :blog
56
+ should destroy_subject
57
+ should trigger_callbacks_for :destroy
58
+ should_not trigger_callbacks_for :update
87
59
  end
88
60
  context 'when destroying instance with instance.delete!' do
89
- setup do
61
+ subject do
90
62
  @blog.delete!
63
+ @blog
91
64
  end
92
- should_hard_destroy :blog
65
+ should destroy_subject
93
66
  end
94
67
  end
95
68
 
96
- context 'with paranoid instance that has dependents' do
69
+ context 'with paranoid instance that has belongs to paranoid instance' do
97
70
  setup do
98
71
  @blog = Blog.create!(:title => 'foo')
99
72
  @comment = @blog.comments.create! :text => 'bar'
100
73
  end
101
- context 'when destroying paranoid instance' do
102
- setup do
74
+ context 'when destroying parent paranoid instance with destroy' do
75
+ subject do
103
76
  @blog.destroy
77
+ @comment
104
78
  end
105
79
 
106
- should_soft_destroy :blog
107
- should_trigger_destroy_callbacks :blog
108
- should_not_trigger_update_callbacks :blog
109
-
110
- should_soft_destroy :comment
111
- should_trigger_destroy_callbacks :comment
80
+ should destroy_subject.softly.and_freeze.and_mark_as_destroyed
81
+ should trigger_callbacks_for :destroy
82
+ #should_not trigger_callbacks_for :update
112
83
  end
113
- context 'when destroying paranoid instance with delete_all!' do
114
- setup do
84
+ context 'when destroying parent paranoid instance with delete_all!' do
85
+ subject do
115
86
  Blog.where({:id => @blog.id}).delete_all!
87
+ @comment
116
88
  end
117
89
 
118
- should_hard_destroy :blog
119
- should 'not destroy associated comment' do
120
- assert_not_nil @comment.reload
121
- assert_nil @comment.blog
90
+ should_not destroy_subject
91
+ end
92
+ end
93
+
94
+ context 'with non-paranoid instance that has belongs to paranoid instance' do
95
+ setup do
96
+ @blog = Blog.create!(:title => 'foo')
97
+ @link = @blog.links.create! :name => 'bar'
98
+ end
99
+ context 'when destroying parent paranoid instance with destroy' do
100
+ subject do
101
+ @blog.destroy
102
+ @link
103
+ end
104
+
105
+ should destroy_subject.and_freeze.and_mark_as_destroyed
106
+ should trigger_callbacks_for :destroy
107
+ #should_not trigger_callbacks_for :update
108
+ end
109
+ context 'when destroying parent paranoid instance with delete_all!' do
110
+ subject do
111
+ Blog.where({:id => @blog.id}).delete_all!
112
+ @link
122
113
  end
114
+
115
+ should_not destroy_subject
123
116
  end
124
117
  end
125
118
  end
119
+
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: 21
4
+ hash: 19
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 0
9
- - 1
10
- version: 1.0.1
9
+ - 2
10
+ version: 1.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Ryan Sonnek
@@ -141,6 +141,8 @@ files:
141
141
  - test/database.yml
142
142
  - test/database_setup.rb
143
143
  - test/helper.rb
144
+ - test/matchers/callback_matcher.rb
145
+ - test/matchers/destroy_matcher.rb
144
146
  - test/test_delete_paranoid.rb
145
147
  has_rdoc: true
146
148
  homepage: http://github.com/wireframe/delete_paranoid
@@ -180,4 +182,6 @@ test_files:
180
182
  - test/database.yml
181
183
  - test/database_setup.rb
182
184
  - test/helper.rb
185
+ - test/matchers/callback_matcher.rb
186
+ - test/matchers/destroy_matcher.rb
183
187
  - test/test_delete_paranoid.rb