destroyed_at 0.2.0 → 0.3.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.
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: