delete_paranoid 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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