destroyed_at 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 072d8f78b587daebb48a6fa406287342c2e1ee3a
4
- data.tar.gz: 6e963e35a47e92e114fc14038e2e8cc0734bf4b0
3
+ metadata.gz: cb825af31fcc38174f287636b93d18b81e14304e
4
+ data.tar.gz: b3ff1f1fea7d4b1a7c6d08637f891cfdf069b122
5
5
  SHA512:
6
- metadata.gz: 1fe52260f693cdd042dc38b26b55c51fbd2b1ee3043c5ef7273154fd78d3b91c54dc84a36a8a70e92498df6a5ec3b1dfd3a0628540cac6c35f66a8aceeb00171
7
- data.tar.gz: 10eeacbd7a71381ddd8401d9069f4cc5d3b1a195b0338d4a46b33d780327b2a57041b3af1ced43e1fa193f25c741ed59c0e47c0bff86cc3e5511bba992d96be7
6
+ metadata.gz: 7d3261f0ff7ab631122308add947cdf6b6e3dccfcbb274453db7b8905b7888babbfda33b76bb412b5056ffbc4019d7b59d53f9423f930a8b14fa668279f55754
7
+ data.tar.gz: 7e3edf023b3691dd43033a61f3ee287b36e3f73ec3120f1f300dc3a0cb2681e16ee744e931a9aa9435a2d71c04ec5a2059dc762acba6948b86eb5f3715aec2cc
data/.travis.yml CHANGED
@@ -1,2 +1,9 @@
1
1
  rvm:
2
- - 2.0.0
2
+ - 2.0.0
3
+
4
+ notifications:
5
+ hipchat:
6
+ rooms:
7
+ secure: HD93T2k0sXQYPk/SLwROvvbQmMDqOxxdgd3eKhocYmH/nKcU/+p3xIls1qstnUh+G4kVwj2ZhWkYg7lfg4rmDfO7RlKWA9zTX3Aaed4cSF/Od54S1Dqwl12KGSITs/nrY2W5zQ+JJ5chFKRYuiRpqb4cPQeafJojccHdaYjYShM=
8
+ template:
9
+ - '@all %{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message}'
data/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ ## 0.3.0
2
+
3
+ * Fix issue with has_many destroy against regular models - Brian
4
+ Cardarella
5
+ * Relation.destoyed removes the destoyed scope and adds a scope
6
+ for records that have been destoyed - Dan McClain
7
+ * Added /restore route for restorable resources - Brian Cardarella &
8
+ Romina Vargas
data/README.md CHANGED
@@ -37,6 +37,15 @@ end
37
37
  Each model's table that is expected to have this behavior **must** have
38
38
  a `destroyed_at` column of type `DateTime`.
39
39
 
40
+ It is recommended that you add an index on the model's `destroyed_at` column,
41
+ so that your database does not have to do a table scan for every query.
42
+ Only Postgres' query indexes will be of any benefit:
43
+
44
+ ```db
45
+ CREATE INDEX ON users WHERE destroyed_at IS NULL;
46
+ CREATE INDEX ON users WHERE destroyed_at IS NOT NULL;
47
+ ```
48
+
40
49
  ## Usage ##
41
50
  Allows you to "destroy" an object without deleting the record or
42
51
  associated records.
@@ -61,6 +70,16 @@ user.destroyed_at
61
70
  # => <DateTime>
62
71
  ```
63
72
 
73
+ #### Warning: Dependent relations with destroy ####
74
+ Be careful when destroying parent relationships that have `dependent:
75
+ :destroy`. If the child
76
+ relationship does not also `include DestroyedAt`, then when you call
77
+ `#destroy` on the parent instance, you will delete the child record,
78
+ rather than simply marking it `destroyed_at`.
79
+
80
+ The same goes for destroying through relations that omit `include
81
+ DestroyedAt`, even if the parent and child models include the mixin.
82
+
64
83
  ### Restoring ####
65
84
  When you'd like to "restore" a record, call the `#restore` method on
66
85
  the instance. This will set its `#destroyed_at` value to `nil`, thereby
@@ -83,15 +102,25 @@ user.destroyed_at
83
102
  # => nil
84
103
  ```
85
104
 
105
+ ### Destroyed Scope ###
106
+ When you include the `DestroyedAt` module, it sets a default scope of
107
+ `where(destroyed_at: nil)`. It also defines a scope called `.destroyed`
108
+ which removes the default scope and finds the records of a relation
109
+ `where.not(destroyed_at: nil)`. This is different than calling `unscoped`
110
+ on the class/relation. Where unscoped will remove all scoping from a
111
+ relation, `.destroyed` only removes the default scope generated by the
112
+ `DestroyedAt` module, making it safe to call on relations.
113
+
86
114
  ### Callbacks ###
87
- `before_restore` and `after_restore` callbacks are added to your
88
- model. They work similarly to the `before_destroy` and `after_destroy`
115
+ `before_restore`, `after_restore` and `around_restore` callbacks are added to your
116
+ model. They work similarly to the `before_destroy`, `after_destroy` and `around_restore`
89
117
  callbacks.
90
118
 
91
119
  ```ruby
92
120
  class User < ActiveRecord::Base
93
121
  before_restore :before_restore_action
94
122
  after_restore :after_restore_action
123
+ around_restore :around_restore_action
95
124
 
96
125
  private
97
126
 
@@ -102,6 +131,11 @@ class User < ActiveRecord::Base
102
131
  def after_restore_action
103
132
  ...
104
133
  end
134
+
135
+ def around_restore_action
136
+ # before around
137
+ yield # restoring...
138
+ # after around
105
139
  end
106
140
  ```
107
141
 
data/destroyed_at.gemspec CHANGED
@@ -19,9 +19,11 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  spec.required_ruby_version = '>= 2.0'
21
21
 
22
+ spec.add_runtime_dependency "activerecord", "~> 4.0.0"
23
+ spec.add_runtime_dependency 'actionpack', '~> 4.0.0'
24
+
22
25
  spec.add_development_dependency "bundler", "~> 1.3"
23
26
  spec.add_development_dependency "rake"
24
- spec.add_development_dependency "activerecord", "~> 4.0.0"
25
27
  spec.add_development_dependency "minitest"
26
28
  spec.add_development_dependency "m"
27
29
  spec.add_development_dependency "sqlite3"
data/lib/destroyed_at.rb CHANGED
@@ -2,6 +2,7 @@ require 'destroyed_at/version'
2
2
  require 'destroyed_at/belongs_to_association'
3
3
  require 'destroyed_at/has_many_association'
4
4
  require 'destroyed_at/has_one_association'
5
+ require 'destroyed_at/mapper'
5
6
 
6
7
  module DestroyedAt
7
8
  def self.included(klass)
@@ -9,6 +10,17 @@ module DestroyedAt
9
10
  default_scope { where(destroyed_at: nil) }
10
11
  after_initialize :_set_destruction_state
11
12
  define_model_callbacks :restore
13
+ extend ClassMethods
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+ def destroyed
19
+ query = all.with_default_scope
20
+ query.where_values.reject! do |node|
21
+ Arel::Nodes::Equality === node && node.left.name == 'destroyed_at' && node.right.nil?
22
+ end
23
+ query.where.not(destroyed_at: nil)
12
24
  end
13
25
  end
14
26
 
@@ -2,7 +2,13 @@ module DestroyedAt
2
2
  module HasManyAssociation
3
3
  def delete_records(records, method)
4
4
  if method == :destroy
5
- records.each { |r| r.destroy(owner.destroyed_at) }
5
+ records.each do |r|
6
+ if r.respond_to?(:destroyed_at)
7
+ r.destroy(owner.destroyed_at)
8
+ else
9
+ r.destroy
10
+ end
11
+ end
6
12
  update_counter(-records.length) unless inverse_updates_counter_cache?
7
13
  else
8
14
  super
@@ -0,0 +1,26 @@
1
+ require 'action_dispatch'
2
+
3
+ module DestroyedAt::Routes
4
+ def set_member_mappings_for_resource
5
+ member do
6
+ put :restore if parent_resource.actions.include?(:restore)
7
+ end
8
+ super
9
+ end
10
+ end
11
+
12
+ ActionDispatch::Routing::Mapper.send(:prepend, DestroyedAt::Routes)
13
+
14
+ module DestroyedAt::Resource
15
+ def default_actions
16
+ actions = super
17
+ if self.name.camelcase.singularize.constantize.included_modules.include?(DestroyedAt)
18
+ actions << :restore
19
+ end
20
+
21
+ actions
22
+ end
23
+ end
24
+
25
+ ActionDispatch::Routing::Mapper::Resources::SingletonResource.send(:prepend, DestroyedAt::Resource)
26
+ ActionDispatch::Routing::Mapper::Resource.send(:prepend, DestroyedAt::Resource)
@@ -0,0 +1,26 @@
1
+ require 'action_dispatch'
2
+
3
+ module DestroyedAt::Mapper
4
+ module Routes
5
+ def set_member_mappings_for_resource
6
+ member do
7
+ put :restore if parent_resource.actions.include?(:restore)
8
+ end
9
+ super
10
+ end
11
+ end
12
+
13
+ module Resource
14
+ def default_actions
15
+ actions = super
16
+ if self.name.camelcase.singularize.constantize.included_modules.include?(DestroyedAt)
17
+ actions << :restore
18
+ end
19
+
20
+ actions
21
+ end
22
+ end
23
+ end
24
+
25
+ ActionDispatch::Routing::Mapper.send(:prepend, DestroyedAt::Mapper::Routes)
26
+ ActionDispatch::Routing::Mapper::Resource.send(:prepend, DestroyedAt::Mapper::Resource)
@@ -1,3 +1,3 @@
1
1
  module DestroyedAt
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -1,200 +1,175 @@
1
1
  require 'test_helper'
2
2
 
3
- describe 'Destroying AR models' do
4
- it 'Calling destroy on a model should only safe destroy record' do
5
- user = User.create
6
- user.destroy
7
- User.all.must_be_empty
8
- User.unscoped.load.wont_be_empty
9
- end
3
+ describe 'destroying an activerecord instance' do
4
+ let(:post) { Post.create }
10
5
 
11
- it 'Destroying a model will set the timestamp it was destroyed at' do
6
+ it 'sets the timestamp it was destroyed at' do
12
7
  time = Time.now
13
8
  Timecop.freeze(time) do
14
- user = User.create
15
- user.destroy
16
- user.destroyed_at.must_equal time
9
+ post = Post.create
10
+ post.destroy
11
+ post.destroyed_at.must_equal time
17
12
  end
18
13
  end
19
14
 
20
- it 'can restore records' do
21
- user = User.create(:destroyed_at => DateTime.current)
22
- User.all.must_be_empty
23
- user.reload
24
- user.restore
25
- user.destroyed_at.must_be_nil
26
- User.all.wont_be_empty
27
- end
28
-
29
- it 'will run destroy callbacks' do
30
- person = Person.create
31
- person.before_flag.wont_equal true
32
- person.after_flag.wont_equal true
33
- person.destroy
34
- person.before_flag.must_equal true
35
- person.after_flag.must_equal true
36
- end
37
-
38
- it 'will run restore callbacks' do
39
- person = Person.create(:destroyed_at => DateTime.current)
40
- person.before_flag.wont_equal true
41
- person.after_flag.wont_equal true
42
- person.restore
43
- person.before_flag.must_equal true
44
- person.after_flag.must_equal true
45
- end
46
-
47
- it 'will properly destroy relations' do
48
- user = User.create(:profile => Profile.new, :car => Car.new)
49
- user.reload
50
- user.profile.wont_be_nil
51
- user.car.wont_be_nil
52
- user.destroy
53
- Profile.count.must_equal 0
54
- Profile.unscoped.count.must_equal 1
55
- Car.count.must_equal 0
56
- Car.unscoped.count.must_equal 0
57
- end
58
-
59
- it 'can restore relationships' do
60
- timestamp = DateTime.current
61
- user = User.create(:destroyed_at => timestamp)
62
- Profile.create(:destroyed_at => timestamp, :user => user)
63
- Profile.count.must_equal 0
64
- user.reload
65
- user.restore
66
- Profile.count.must_equal 1
67
- end
68
-
69
- it 'will not restore relationships that have no destroy dependency' do
70
- user = User.create(:destroyed_at => DateTime.current, :show => Show.new(:destroyed_at => DateTime.current))
71
- Show.count.must_equal 0
72
- user.restore
73
- Show.count.must_equal 0
74
- end
75
-
76
- it 'will respect has many associations' do
77
- timestamp = DateTime.current
78
- user = User.create(:destroyed_at => timestamp)
79
- Dinner.create(:destroyed_at => timestamp, :user => user)
80
- Dinner.count.must_equal 0
81
- user.reload
82
- user.restore
83
- Dinner.count.must_equal 1
84
- end
85
-
86
- it 'will respect has many through associations' do
87
- timestamp = DateTime.current
88
- user = User.create(:destroyed_at => timestamp)
89
- car = Car.create
90
- fleet = Fleet.create(:destroyed_at => timestamp, :car => car)
91
- user.fleets = [fleet]
92
- user.cars.count.must_equal 0
93
- user.reload
94
- user.restore
95
- user.cars.count.must_equal 1
96
- end
97
-
98
- it 'properly sets #destroyed?' do
99
- user = User.create
100
- user.destroy
101
- user.destroyed?.must_equal true
102
- user = User.unscoped.last
103
- user.destroyed?.must_equal true
104
- user.restore
105
- user.destroyed?.must_equal false
106
- end
107
-
108
- it 'properly selects columns' do
109
- User.create
110
- User.select(:id).must_be_kind_of ActiveRecord::Relation
111
- end
112
-
113
- it 'only destroys and restores related dependents' do
114
- 2.times do
115
- User.create(dinners: [Dinner.create, Dinner.create, Dinner.create])
116
- end
15
+ it 'does not delete the record' do
16
+ post.destroy
17
+ Post.all.must_be_empty
18
+ Post.unscoped.load.wont_be_empty
19
+ end
117
20
 
118
- User.first.destroy
119
- Dinner.count.must_equal 3
120
- User.first.destroy
121
- Dinner.count.must_equal 0
122
- User.unscoped.first.restore
123
- Dinner.count.must_equal 3
21
+ it 'sets #destroyed?' do
22
+ post.destroy
23
+ post.destroyed?.must_equal true
24
+ post = Post.unscoped.last
25
+ post.destroyed?.must_equal true
26
+ post.restore
27
+ post.destroyed?.must_equal false
124
28
  end
125
29
 
126
- it 'skips callbacks' do
127
- user = User.create
128
- user.destroy
129
- user.before_update_count.must_equal nil
130
- user.restore
131
- user.before_update_count.must_equal nil
30
+ it 'runs destroy callbacks' do
31
+ post.destroy_callback_count.must_equal nil
32
+ post.destroy
33
+ post.destroy_callback_count.must_equal 1
132
34
  end
133
35
 
134
- it 'skips validations on restore' do
135
- user = User.create
136
- user.validation_count.must_equal 1
137
- user.destroy
138
- user.validation_count.must_equal 1
139
- user.restore
140
- user.validation_count.must_equal 1
36
+ it 'does not run update callbacks' do
37
+ post.destroy
38
+ post.update_callback_count.must_equal nil
39
+ post.restore
40
+ post.update_callback_count.must_equal nil
141
41
  end
142
42
 
143
43
  it 'stays persisted after destruction' do
144
- user = User.create
145
- user.destroy
146
- user.persisted?.must_equal true
44
+ post.destroy
45
+ post.persisted?.must_equal true
147
46
  end
148
47
 
149
- it 'does not allow new records with destroyed_at columns present to be marked persisted' do
150
- user = User.new(destroyed_at: Time.now)
151
- user.persisted?.must_equal false
48
+ it 'destroys dependent relation with DestroyedAt' do
49
+ post.comments.create
50
+ Post.count.must_equal 1
51
+ Comment.count.must_equal 1
52
+ post.destroy
53
+ Post.count.must_equal 0
54
+ Comment.count.must_equal 0
55
+ end
56
+
57
+ it 'destroys dependent through relation with DestroyedAt' do
58
+ commenter = Commenter.create
59
+ Comment.create(:post => post, :commenter => commenter)
60
+
61
+ Commenter.count.must_equal 1
62
+ Comment.count.must_equal 1
63
+ post.destroy
64
+ Commenter.count.must_equal 1
65
+ Comment.count.must_equal 0
66
+ end
67
+
68
+ it 'deletes dependent relations without DestroyedAt' do
69
+ category = Category.create
70
+ Categorization.create(:category => category, :post => post)
71
+ post.categorizations.count.must_equal 1
72
+ post.destroy
73
+ Categorization.unscoped.count.must_equal 0
74
+ end
75
+ end
76
+
77
+ describe 'restoring an activerecord instance' do
78
+ let(:author) { Author.create }
79
+ let(:timestamp) { DateTime.current }
80
+ let(:post) { Post.create(:destroyed_at => timestamp) }
81
+
82
+ it 'restores the record' do
83
+ Post.all.must_be_empty
84
+ post.reload
85
+ post.restore
86
+ post.destroyed_at.must_be_nil
87
+ Post.all.wont_be_empty
88
+ end
89
+
90
+ it 'runs the restore callbacks' do
91
+ post.restore_callback_count.must_equal nil
92
+ post.restore
93
+ post.restore_callback_count.must_equal 1
94
+ end
95
+
96
+ it 'does not run restore validations' do
97
+ initial_count = post.validation_count
98
+ post.restore
99
+ initial_count.must_equal post.validation_count
152
100
  end
153
101
 
102
+ it 'restores a dependent has_many relation with DestroyedAt' do
103
+ Comment.create(:destroyed_at => timestamp, :post => post)
104
+ Comment.count.must_equal 0
105
+ post.reload
106
+ post.restore
107
+ Comment.count.must_equal 1
108
+ end
109
+
110
+ it 'does not restore a non-dependent relation with DestroyedAt' do
111
+ Post.count.must_equal 0
112
+ Author.count.must_equal 0
113
+ post.reload
114
+ post.restore
115
+ Post.count.must_equal 1
116
+ Author.count.must_equal 0
117
+ end
118
+
119
+ it 'restores a dependent through relation with DestroyedAt' do
120
+ commenter = Commenter.create
121
+ Comment.create(:post => post, :commenter => commenter, :destroyed_at => timestamp)
122
+
123
+ Commenter.count.must_equal 1
124
+ Comment.count.must_equal 0
125
+ post.reload
126
+ post.restore
127
+ Commenter.count.must_equal 1
128
+ Comment.count.must_equal 1
129
+ end
130
+
131
+ it 'restores only the dependent relationships destroyed when the parent was destroyed' do
132
+ post = Post.create
133
+ comment_1 = Comment.create(post: post, destroyed_at: Time.now - 1.day)
134
+ comment_2 = Comment.create(post: post)
135
+ post.destroy
136
+ post.reload # We have to reload the object before restoring in the test
137
+ # because the in memory object has greater precision than
138
+ # the database records
139
+ post.restore
140
+ post.comments.wont_include comment_1
141
+ post.comments.must_include comment_2
142
+ end
143
+ end
144
+
145
+ describe 'deleting a record' do
154
146
  it 'is not persisted after deletion' do
155
- user = User.create
156
- user.delete
157
- user.persisted?.must_equal false
147
+ post = Post.create
148
+ post.delete
149
+ post.persisted?.must_equal false
158
150
  end
159
151
 
160
152
  it 'can delete destroyed records and they are marked as not persisted' do
161
- user = User.create
162
- user.destroy
163
- user.persisted?.must_equal true
164
- user.delete
165
- user.persisted?.must_equal false
166
- end
167
-
168
- it 'only restores dependencies destroyed with parent with has many' do
169
- user = User.create
170
- dinner_one = Dinner.create(user: user, destroyed_at: Time.now - 1.day)
171
- dinner_two = Dinner.create(user: user)
172
- user.destroy
173
- user.restore
174
- user.dinners.include?(dinner_one).must_equal false
175
- user.dinners.include?(dinner_two).must_equal true
176
- end
177
-
178
- it 'only restores dependencies destroyed with parent with has one' do
179
- user = User.create
180
- profile_1 = Profile.create(user: user, destroyed_at: Time.now - 1.day)
181
- profile_2 = Profile.create(user: user)
182
- user.destroy
183
- user.restore
184
- user.profile.must_equal profile_2
185
- profile_1.reload
186
- profile_1.destroyed_at.wont_equal nil
187
- end
188
-
189
- it 'destroys and restores dependencies in a belongs_to relationship' do
190
- user = User.create
191
- show = Show.create(user: user)
192
- show.destroy
193
- show.reload
194
- user.reload
195
- user.destroyed_at.wont_equal nil
196
- show.restore
197
- user.reload
198
- user.destroyed_at.must_equal nil
153
+ post = Post.create
154
+ post.destroy
155
+ post.persisted?.must_equal true
156
+ post.delete
157
+ post.persisted?.must_equal false
158
+ end
159
+ end
160
+
161
+ describe 'destroying an activerecord instance without DestroyedAt' do
162
+ it 'does not impact ActiveRecord::Relation.destroy' do
163
+ post = Post.create
164
+ categorization = Categorization.create(post: post)
165
+ post.categorizations.destroy(categorization.id)
166
+ post.categorizations.must_be_empty
167
+ end
168
+ end
169
+
170
+ describe 'creating a destroyed record' do
171
+ it 'does not allow new records with destroyed_at columns present to be marked persisted' do
172
+ post = Post.new(destroyed_at: Time.now)
173
+ post.persisted?.must_equal false
199
174
  end
200
175
  end
@@ -0,0 +1,72 @@
1
+ require 'test_helper'
2
+ require 'action_dispatch'
3
+ require 'action_dispatch/routing/route_set'
4
+ require 'action_controller'
5
+
6
+ class CommentsController < ActionController::Base; end
7
+
8
+ class MapperTest < ActiveSupport::TestCase
9
+ setup do
10
+ @set = ActionDispatch::Routing::RouteSet.new
11
+ end
12
+
13
+ test 'adds restore route for DestroyedAt model plural resource' do
14
+ draw do
15
+ resources :comments
16
+ end
17
+
18
+ params = @set.recognize_path('/comments/:id/restore', method: 'put')
19
+ assert_equal params[:controller], 'comments'
20
+ assert_equal params[:action], 'restore'
21
+ end
22
+
23
+ test 'adds restore route for DestroyedAt model singular resource' do
24
+ draw do
25
+ resource :comment
26
+ end
27
+
28
+ params = @set.recognize_path('/comment/restore', method: 'put')
29
+ assert_equal params[:controller], 'comments'
30
+ assert_equal params[:action], 'restore'
31
+ end
32
+
33
+ test 'does not add restore route for non DestroyedAt plural resource' do
34
+ draw do
35
+ resources :authors
36
+ end
37
+
38
+ begin
39
+ params = @set.recognize_path('/authors/:id/restore', method: 'put')
40
+ assert false, 'this should not be reached'
41
+ rescue ActionController::RoutingError
42
+ assert true, 'path not recognized'
43
+ end
44
+ end
45
+
46
+ test 'does not add restore route for non DestroyedAt singular resource' do
47
+ draw do
48
+ resource :author
49
+ end
50
+
51
+ begin
52
+ params = @set.recognize_path('/author/restore', method: 'put')
53
+ assert false, 'this should not be reached'
54
+ rescue ActionController::RoutingError
55
+ assert true, 'path not recognized'
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def draw(&block)
62
+ @set.draw(&block)
63
+ end
64
+
65
+ def url_helpers
66
+ @set.url_helpers
67
+ end
68
+
69
+ def clear!
70
+ @set.clear!
71
+ end
72
+ end
@@ -0,0 +1,52 @@
1
+ require 'test_helper'
2
+
3
+ describe 'Scopes' do
4
+ let(:post) { Post.create! }
5
+ let(:comment_1) { post.comments.create! }
6
+ let(:comment_2) { post.comments.create! }
7
+ let(:comment_3) { Comment.create! }
8
+ let(:comment_4) { Comment.create! }
9
+
10
+ before do
11
+ comment_1
12
+ comment_2.destroy
13
+ comment_3
14
+ comment_4.destroy
15
+ end
16
+
17
+ describe '.destroyed' do
18
+ context 'Called on model' do
19
+ let(:destroyed_comments) { Comment.destroyed }
20
+
21
+ it 'returns records that have been destroyed' do
22
+ destroyed_comments.must_include comment_2
23
+ destroyed_comments.must_include comment_4
24
+ end
25
+
26
+ it 'does not return current records' do
27
+ destroyed_comments.wont_include comment_1
28
+ destroyed_comments.wont_include comment_3
29
+ end
30
+ end
31
+
32
+ context 'Called on relation' do
33
+ let(:destroyed_comments) { post.comments.destroyed }
34
+
35
+ it 'returns destroyed records beloning in the relation' do
36
+ destroyed_comments.must_include comment_2
37
+ end
38
+
39
+ it 'does not return destroyed records that are outside the relation' do
40
+ destroyed_comments.wont_include comment_4
41
+ end
42
+
43
+ it 'does not return current records in the relation' do
44
+ destroyed_comments.wont_include comment_1
45
+ end
46
+
47
+ it 'does not return current records that are outside the relation' do
48
+ destroyed_comments.wont_include comment_3
49
+ end
50
+ end
51
+ end
52
+ end
data/test/test_helper.rb CHANGED
@@ -27,79 +27,76 @@ ActiveRecord::Base.establish_connection(
27
27
 
28
28
  DatabaseCleaner.strategy = :truncation
29
29
 
30
- ActiveRecord::Base.connection.execute(%{CREATE TABLE users (id INTEGER PRIMARY KEY, destroyed_at DATETIME, type STRING);})
31
- ActiveRecord::Base.connection.execute(%{CREATE TABLE profiles (id INTEGER PRIMARY KEY, destroyed_at DATETIME, user_id INTEGER);})
32
- ActiveRecord::Base.connection.execute(%{CREATE TABLE cars (id INTEGER PRIMARY KEY, user_id INTEGER);})
33
- ActiveRecord::Base.connection.execute(%{CREATE TABLE dinners (id INTEGER PRIMARY KEY, destroyed_at DATETIME, user_id INTEGER);})
34
- ActiveRecord::Base.connection.execute(%{CREATE TABLE shows (id INTEGER PRIMARY KEY, destroyed_at DATETIME, user_id INTEGER);})
35
- ActiveRecord::Base.connection.execute(%{CREATE TABLE fleets (id INTEGER PRIMARY KEY, destroyed_at DATETIME, user_id INTEGER, car_id INTEGER);})
36
-
37
- class User < ActiveRecord::Base
38
- include DestroyedAt
39
- has_one :profile, :dependent => :destroy
40
- has_one :car, :dependent => :destroy
41
- has_many :dinners, :dependent => :destroy
42
- has_one :show
43
- has_many :fleets
44
- has_many :cars, :through => :fleets, :dependent => :destroy
45
- before_update :increment_callback_counter
46
- validate :increment_validation_counter
30
+ ActiveRecord::Base.connection.execute(%{CREATE TABLE authors (id INTEGER PRIMARY KEY);})
31
+ ActiveRecord::Base.connection.execute(%{CREATE TABLE categories (id INTEGER PRIMARY KEY);})
32
+ ActiveRecord::Base.connection.execute(%{CREATE TABLE categorizations (id INTEGER PRIMARY KEY, category_id INTEGER, post_id INTEGER);})
33
+ ActiveRecord::Base.connection.execute(%{CREATE TABLE comments (id INTEGER PRIMARY KEY, commenter_id INTEGER, post_id INTEGER, destroyed_at DATETIME);})
34
+ ActiveRecord::Base.connection.execute(%{CREATE TABLE commenters (id INTEGER PRIMARY KEY, destroyed_at DATETIME);})
35
+ ActiveRecord::Base.connection.execute(%{CREATE TABLE images (id INTEGER PRIMARY KEY, post_id INTEGER);})
36
+ ActiveRecord::Base.connection.execute(%{CREATE TABLE posts (id INTEGER PRIMARY KEY, author_id INTEGER, destroyed_at DATETIME);})
37
+
38
+ class Author < ActiveRecord::Base
39
+ has_many :posts
40
+ end
47
41
 
48
- attr_accessor :before_update_count, :validation_count
42
+ class Category < ActiveRecord::Base
43
+ has_many :categorizations
44
+ has_many :posts, through: :categorizations
45
+ end
49
46
 
50
- private
47
+ class Categorization < ActiveRecord::Base
48
+ belongs_to :category
49
+ belongs_to :post
50
+ end
51
51
 
52
- def increment_callback_counter
53
- self.before_update_count ||= 0
54
- self.before_update_count = self.before_update_count + 1
55
- end
52
+ class Comment < ActiveRecord::Base
53
+ include DestroyedAt
54
+ belongs_to :post
55
+ belongs_to :commenter
56
+ end
56
57
 
57
- def increment_validation_counter
58
- self.validation_count ||= 0
59
- self.validation_count = self.validation_count + 1
60
- end
58
+ class Commenter < ActiveRecord::Base
59
+ include DestroyedAt
60
+ has_many :comments, dependent: :destroy
61
+ has_many :posts, through: :comments
61
62
  end
62
63
 
63
- class Person < User
64
- before_destroy :set_before_flag
65
- after_destroy :set_after_flag
64
+ class Post < ActiveRecord::Base
65
+ include DestroyedAt
66
66
 
67
- before_restore :set_before_flag
68
- after_restore :set_after_flag
67
+ belongs_to :author
68
+ has_many :categories, through: :categorizations
69
+ has_many :categorizations, dependent: :destroy
70
+ has_many :comments, dependent: :destroy
71
+ has_many :commenters, through: :comments
69
72
 
70
- attr_accessor :before_flag, :after_flag
73
+ before_destroy :increment_destroy_callback_counter
74
+ before_restore :increment_restore_callback_counter
75
+ before_update :increment_update_callback_counter
71
76
 
72
- def set_before_flag
73
- self.before_flag = true
74
- end
77
+ validate :increment_validation_counter
75
78
 
76
- def set_after_flag
77
- self.after_flag = true
78
- end
79
- end
79
+ attr_accessor :destroy_callback_count, :restore_callback_count, :update_callback_count, :validation_count
80
80
 
81
- class Profile < ActiveRecord::Base
82
- include DestroyedAt
83
- belongs_to :user
84
- end
81
+ private
85
82
 
86
- class Car < ActiveRecord::Base
87
- belongs_to :user
88
- has_many :fleets
89
- end
83
+ def increment_restore_callback_counter
84
+ self.restore_callback_count ||= 0
85
+ self.restore_callback_count = self.restore_callback_count + 1
86
+ end
90
87
 
91
- class Dinner < ActiveRecord::Base
92
- include DestroyedAt
93
- belongs_to :user
94
- end
88
+ def increment_destroy_callback_counter
89
+ self.destroy_callback_count ||= 0
90
+ self.destroy_callback_count = self.destroy_callback_count + 1
91
+ end
95
92
 
96
- class Show < ActiveRecord::Base
97
- include DestroyedAt
98
- belongs_to :user, :dependent => :destroy
99
- end
93
+ def increment_update_callback_counter
94
+ self.update_callback_count ||= 0
95
+ self.update_callback_count = self.update_callback_count + 1
96
+ end
100
97
 
101
- class Fleet < ActiveRecord::Base
102
- include DestroyedAt
103
- belongs_to :user
104
- belongs_to :car
98
+ def increment_validation_counter
99
+ self.validation_count ||= 0
100
+ self.validation_count = self.validation_count + 1
101
+ end
105
102
  end
metadata CHANGED
@@ -1,57 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: destroyed_at
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Dupuis Jr.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-04 00:00:00.000000000 Z
11
+ date: 2013-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: bundler
14
+ name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: '1.3'
20
- type: :development
19
+ version: 4.0.0
20
+ type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: '1.3'
26
+ version: 4.0.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: actionpack
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ~>
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
33
+ version: 4.0.0
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ~>
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 4.0.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: activerecord
42
+ name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ~>
46
46
  - !ruby/object:Gem::Version
47
- version: 4.0.0
47
+ version: '1.3'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
- version: 4.0.0
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: minitest
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -145,6 +159,7 @@ extra_rdoc_files: []
145
159
  files:
146
160
  - .gitignore
147
161
  - .travis.yml
162
+ - CHANGELOG.md
148
163
  - CONTRIBUTING.md
149
164
  - Gemfile
150
165
  - README.md
@@ -155,8 +170,12 @@ files:
155
170
  - lib/destroyed_at/belongs_to_association.rb
156
171
  - lib/destroyed_at/has_many_association.rb
157
172
  - lib/destroyed_at/has_one_association.rb
173
+ - lib/destroyed_at/mapper.rb
174
+ - lib/destroyed_at/routes.rb
158
175
  - lib/destroyed_at/version.rb
159
176
  - test/destroyed_at_test.rb
177
+ - test/mapper_test.rb
178
+ - test/scope_test.rb
160
179
  - test/test_helper.rb
161
180
  homepage: https://github.com/dockyard/destroyed_at
162
181
  licenses:
@@ -178,10 +197,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
178
197
  version: '0'
179
198
  requirements: []
180
199
  rubyforge_project:
181
- rubygems_version: 2.1.0
200
+ rubygems_version: 2.0.3
182
201
  signing_key:
183
202
  specification_version: 4
184
203
  summary: Safe destroy for ActiveRecord.
185
204
  test_files:
186
205
  - test/destroyed_at_test.rb
206
+ - test/mapper_test.rb
207
+ - test/scope_test.rb
187
208
  - test/test_helper.rb
209
+ has_rdoc: