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 +4 -4
- data/.travis.yml +8 -1
- data/CHANGELOG.md +8 -0
- data/README.md +36 -2
- data/destroyed_at.gemspec +3 -1
- data/lib/destroyed_at.rb +12 -0
- data/lib/destroyed_at/has_many_association.rb +7 -1
- data/lib/destroyed_at/mapper.rb +26 -0
- data/lib/destroyed_at/routes.rb +26 -0
- data/lib/destroyed_at/version.rb +1 -1
- data/test/destroyed_at_test.rb +149 -174
- data/test/mapper_test.rb +72 -0
- data/test/scope_test.rb +52 -0
- data/test/test_helper.rb +57 -60
- metadata +38 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cb825af31fcc38174f287636b93d18b81e14304e
|
4
|
+
data.tar.gz: b3ff1f1fea7d4b1a7c6d08637f891cfdf069b122
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d3261f0ff7ab631122308add947cdf6b6e3dccfcbb274453db7b8905b7888babbfda33b76bb412b5056ffbc4019d7b59d53f9423f930a8b14fa668279f55754
|
7
|
+
data.tar.gz: 7e3edf023b3691dd43033a61f3ee287b36e3f73ec3120f1f300dc3a0cb2681e16ee744e931a9aa9435a2d71c04ec5a2059dc762acba6948b86eb5f3715aec2cc
|
data/.travis.yml
CHANGED
@@ -1,2 +1,9 @@
|
|
1
1
|
rvm:
|
2
|
-
|
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 `
|
88
|
-
model. They work similarly to the `before_destroy` and `
|
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
|
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)
|
data/lib/destroyed_at/version.rb
CHANGED
data/test/destroyed_at_test.rb
CHANGED
@@ -1,200 +1,175 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
describe '
|
4
|
-
|
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 '
|
6
|
+
it 'sets the timestamp it was destroyed at' do
|
12
7
|
time = Time.now
|
13
8
|
Timecop.freeze(time) do
|
14
|
-
|
15
|
-
|
16
|
-
|
9
|
+
post = Post.create
|
10
|
+
post.destroy
|
11
|
+
post.destroyed_at.must_equal time
|
17
12
|
end
|
18
13
|
end
|
19
14
|
|
20
|
-
it '
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
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 '
|
127
|
-
|
128
|
-
|
129
|
-
|
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 '
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
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
|
-
|
145
|
-
|
146
|
-
user.persisted?.must_equal true
|
44
|
+
post.destroy
|
45
|
+
post.persisted?.must_equal true
|
147
46
|
end
|
148
47
|
|
149
|
-
it '
|
150
|
-
|
151
|
-
|
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
|
-
|
156
|
-
|
157
|
-
|
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
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
end
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
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
|
data/test/mapper_test.rb
ADDED
@@ -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
|
data/test/scope_test.rb
ADDED
@@ -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
|
31
|
-
ActiveRecord::Base.connection.execute(%{CREATE TABLE
|
32
|
-
ActiveRecord::Base.connection.execute(%{CREATE TABLE
|
33
|
-
ActiveRecord::Base.connection.execute(%{CREATE TABLE
|
34
|
-
ActiveRecord::Base.connection.execute(%{CREATE TABLE
|
35
|
-
ActiveRecord::Base.connection.execute(%{CREATE TABLE
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
42
|
+
class Category < ActiveRecord::Base
|
43
|
+
has_many :categorizations
|
44
|
+
has_many :posts, through: :categorizations
|
45
|
+
end
|
49
46
|
|
50
|
-
|
47
|
+
class Categorization < ActiveRecord::Base
|
48
|
+
belongs_to :category
|
49
|
+
belongs_to :post
|
50
|
+
end
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
class Comment < ActiveRecord::Base
|
53
|
+
include DestroyedAt
|
54
|
+
belongs_to :post
|
55
|
+
belongs_to :commenter
|
56
|
+
end
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
64
|
-
|
65
|
-
after_destroy :set_after_flag
|
64
|
+
class Post < ActiveRecord::Base
|
65
|
+
include DestroyedAt
|
66
66
|
|
67
|
-
|
68
|
-
|
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
|
-
|
73
|
+
before_destroy :increment_destroy_callback_counter
|
74
|
+
before_restore :increment_restore_callback_counter
|
75
|
+
before_update :increment_update_callback_counter
|
71
76
|
|
72
|
-
|
73
|
-
self.before_flag = true
|
74
|
-
end
|
77
|
+
validate :increment_validation_counter
|
75
78
|
|
76
|
-
|
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
|
-
|
82
|
-
include DestroyedAt
|
83
|
-
belongs_to :user
|
84
|
-
end
|
81
|
+
private
|
85
82
|
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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.
|
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-
|
11
|
+
date: 2013-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activerecord
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
20
|
-
type: :
|
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:
|
26
|
+
version: 4.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: actionpack
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
34
|
-
type: :
|
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:
|
40
|
+
version: 4.0.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ~>
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
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:
|
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.
|
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:
|