permanent_records 2.3.0 → 3.0.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.
- data/Gemfile +10 -3
- data/Rakefile +8 -23
- data/VERSION +1 -1
- data/lib/permanent_records.rb +127 -204
- data/permanent_records.gemspec +15 -15
- data/spec/permanent_records_spec.rb +212 -0
- data/spec/spec_helper.rb +48 -0
- data/{test → spec/support}/comment.rb +1 -1
- data/spec/support/database.yml +16 -0
- data/spec/support/difficulty.rb +5 -0
- data/{test → spec/support}/dirt.rb +0 -0
- data/{test → spec/support}/earthworm.rb +3 -3
- data/{test → spec/support}/hole.rb +3 -3
- data/{test → spec/support}/kitty.rb +0 -0
- data/{test → spec/support}/location.rb +1 -1
- data/{test → spec/support}/mole.rb +0 -0
- data/{test → spec/support}/muskrat.rb +0 -0
- data/{test → spec/support}/schema.rb +0 -0
- data/{test → spec/support}/unused_model.rb +0 -0
- metadata +17 -17
- data/test/database.yml +0 -18
- data/test/difficulty.rb +0 -11
- data/test/permanent_records_test.rb +0 -270
- data/test/test_helper.rb +0 -46
data/Gemfile
CHANGED
@@ -1,9 +1,16 @@
|
|
1
|
-
source '
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem 'activerecord'
|
3
|
+
gem 'activerecord', '> 3.0.0'
|
4
4
|
|
5
5
|
group :development do
|
6
|
-
gem 'jeweler' # Because Bundler doesn't fucking have version:bump tasks
|
7
6
|
gem 'rake'
|
7
|
+
gem 'pg'
|
8
|
+
gem 'activesupport'
|
8
9
|
gem 'sqlite3'
|
9
10
|
end
|
11
|
+
|
12
|
+
group :test do
|
13
|
+
gem 'awesome_print'
|
14
|
+
gem 'rspec'
|
15
|
+
gem 'database_cleaner'
|
16
|
+
end
|
data/Rakefile
CHANGED
@@ -1,28 +1,13 @@
|
|
1
|
-
require '
|
2
|
-
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
gem.name = "permanent_records"
|
8
|
-
gem.summary = %Q{Soft-delete your ActiveRecord records}
|
9
|
-
gem.description = %Q{Never Lose Data. Rather than deleting rows this sets Record#deleted_at and gives you all the scopes you need to work with your data.}
|
10
|
-
gem.email = "gems@6brand.com"
|
11
|
-
gem.homepage = "http://github.com/JackDanger/permanent_records"
|
12
|
-
gem.authors = ["Jack Danger Canty", "David Sulc"]
|
4
|
+
namespace :db do
|
5
|
+
task :create do
|
6
|
+
`createdb permanent_records`
|
13
7
|
end
|
14
|
-
rescue LoadError
|
15
|
-
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
16
8
|
end
|
17
9
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
test.ruby_opts << '-rubygems'
|
22
|
-
test.pattern = 'test/*_test.rb'
|
23
|
-
test.verbose = true
|
10
|
+
desc 'Run all tests'
|
11
|
+
task :spec => 'db:create' do
|
12
|
+
exec 'rspec'
|
24
13
|
end
|
25
|
-
task :spec => :test
|
26
|
-
|
27
|
-
task :default => :test
|
28
|
-
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
3.0.0
|
data/lib/permanent_records.rb
CHANGED
@@ -1,245 +1,168 @@
|
|
1
1
|
module PermanentRecords
|
2
|
+
|
2
3
|
def self.included(base)
|
3
4
|
|
4
|
-
base.
|
5
|
-
|
6
|
-
|
7
|
-
if ActiveRecord::VERSION::MAJOR >= 3
|
8
|
-
base.extend Scopes
|
9
|
-
base.instance_eval { define_model_callbacks :revive }
|
10
|
-
# Rails 2.x.x
|
11
|
-
elsif base.respond_to?(:named_scope)
|
12
|
-
base.named_scope :deleted, :conditions => 'deleted_at IS NOT NULL'
|
13
|
-
base.named_scope :not_deleted, :conditions => { :deleted_at => nil }
|
14
|
-
base.instance_eval { define_callbacks :before_revive, :after_revive }
|
15
|
-
base.send :alias_method_chain, :destroy, :permanent_records
|
16
|
-
# Early Rails code
|
17
|
-
else
|
18
|
-
base.extend EarlyRails
|
19
|
-
base.instance_eval { define_callbacks :before_revive, :after_revive }
|
20
|
-
end
|
5
|
+
base.extend Scopes
|
6
|
+
base.extend IsPermanent
|
7
|
+
|
21
8
|
base.instance_eval do
|
9
|
+
define_model_callbacks :revive
|
10
|
+
|
22
11
|
before_revive :revive_destroyed_dependent_records
|
23
|
-
def is_permanent?
|
24
|
-
columns.detect {|c| 'deleted_at' == c.name}
|
25
|
-
end
|
26
12
|
end
|
13
|
+
|
27
14
|
end
|
28
|
-
|
15
|
+
|
29
16
|
module Scopes
|
30
17
|
def deleted
|
31
|
-
where
|
18
|
+
where "#{table_name}.deleted_at IS NOT NULL"
|
32
19
|
end
|
20
|
+
|
33
21
|
def not_deleted
|
34
|
-
where
|
22
|
+
where "#{table_name}.deleted_at IS NULL"
|
35
23
|
end
|
36
24
|
end
|
37
|
-
|
38
|
-
module
|
39
|
-
def
|
40
|
-
|
41
|
-
yield
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def with_not_deleted
|
46
|
-
with_scope :find => {:conditions => "#{quoted_table_name}.deleted_at IS NULL"} do
|
47
|
-
yield
|
48
|
-
end
|
25
|
+
|
26
|
+
module IsPermanent
|
27
|
+
def is_permanent?
|
28
|
+
columns.detect {|c| 'deleted_at' == c.name}
|
49
29
|
end
|
50
|
-
|
51
|
-
# this next bit is basically stolen from the scope_out plugin
|
52
|
-
[:deleted, :not_deleted].each do |name|
|
53
|
-
define_method "find_#{name}" do |*args|
|
54
|
-
send("with_#{name}") { find(*args) }
|
55
|
-
end
|
30
|
+
end
|
56
31
|
|
57
|
-
|
58
|
-
|
59
|
-
|
32
|
+
def is_permanent?
|
33
|
+
respond_to?(:deleted_at)
|
34
|
+
end
|
60
35
|
|
61
|
-
|
62
|
-
|
63
|
-
|
36
|
+
def deleted?
|
37
|
+
deleted_at if is_permanent?
|
38
|
+
end
|
64
39
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
end
|
40
|
+
def revive
|
41
|
+
_run_revive_callbacks { set_deleted_at nil }
|
42
|
+
self
|
69
43
|
end
|
70
44
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
respond_to?(:deleted_at)
|
45
|
+
def destroy(force = nil)
|
46
|
+
unless is_permanent? && (:force != force)
|
47
|
+
return permanently_delete_records_after { super() }
|
75
48
|
end
|
76
|
-
|
77
|
-
|
78
|
-
|
49
|
+
destroy_with_permanent_records force
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
|
54
|
+
def set_deleted_at(value)
|
55
|
+
return self unless is_permanent?
|
56
|
+
record = self.class.unscoped.find(id)
|
57
|
+
record.deleted_at = value
|
58
|
+
begin
|
59
|
+
# we call save! instead of update_attribute so an ActiveRecord::RecordInvalid
|
60
|
+
# error will be raised if the record isn't valid. (This prevents reviving records that
|
61
|
+
# disregard validation constraints,)
|
62
|
+
record.save!
|
63
|
+
@attributes, @attributes_cache = record.attributes, record.attributes
|
64
|
+
rescue Exception => e
|
65
|
+
# trigger dependent record destruction (they were revived before this record,
|
66
|
+
# which cannot be revived due to validations)
|
67
|
+
record.destroy
|
68
|
+
raise e
|
79
69
|
end
|
70
|
+
end
|
80
71
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
set_deleted_at nil
|
85
|
-
end
|
86
|
-
else
|
87
|
-
run_callbacks :before_revive
|
88
|
-
attempt_notifying_observers(:before_revive)
|
89
|
-
set_deleted_at nil
|
90
|
-
run_callbacks :after_revive
|
91
|
-
attempt_notifying_observers(:after_revive)
|
92
|
-
end
|
93
|
-
self
|
72
|
+
def destroy_with_permanent_records(force = nil)
|
73
|
+
_run_destroy_callbacks do
|
74
|
+
deleted? || new_record? ? save : set_deleted_at(Time.now)
|
94
75
|
end
|
76
|
+
self
|
77
|
+
end
|
95
78
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
79
|
+
def revive_destroyed_dependent_records
|
80
|
+
self.class.reflections.select do |name, reflection|
|
81
|
+
'destroy' == reflection.options[:dependent].to_s && reflection.klass.is_permanent?
|
82
|
+
end.each do |name, reflection|
|
83
|
+
cardinality = reflection.macro.to_s.gsub('has_', '')
|
84
|
+
if cardinality == 'many'
|
85
|
+
records = send(name).unscoped.find(
|
86
|
+
:all,
|
87
|
+
:conditions => [
|
88
|
+
"#{reflection.quoted_table_name}.deleted_at > ?" +
|
89
|
+
" AND " +
|
90
|
+
"#{reflection.quoted_table_name}.deleted_at < ?",
|
91
|
+
deleted_at - 3.seconds,
|
92
|
+
deleted_at + 3.seconds
|
93
|
+
]
|
94
|
+
)
|
95
|
+
elsif cardinality == 'one' or cardinality == 'belongs_to'
|
96
|
+
self.class.unscoped do
|
97
|
+
records = [] << send(name)
|
100
98
|
end
|
101
99
|
end
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
private
|
106
|
-
def set_deleted_at(value)
|
107
|
-
return self unless is_permanent?
|
108
|
-
record = self.class
|
109
|
-
record = record.unscoped if active_record_3?
|
110
|
-
record = record.find(id)
|
111
|
-
record.deleted_at = value
|
112
|
-
begin
|
113
|
-
# we call save! instead of update_attribute so an ActiveRecord::RecordInvalid
|
114
|
-
# error will be raised if the record isn't valid. (This prevents reviving records that
|
115
|
-
# disregard validation constraints,)
|
116
|
-
record.save!
|
117
|
-
@attributes, @attributes_cache = record.attributes, record.attributes
|
118
|
-
rescue Exception => e
|
119
|
-
# trigger dependent record destruction (they were revived before this record,
|
120
|
-
# which cannot be revived due to validations)
|
121
|
-
record.destroy
|
122
|
-
raise e
|
100
|
+
[records].flatten.compact.each do |dependent|
|
101
|
+
dependent.revive
|
123
102
|
end
|
103
|
+
|
104
|
+
# and update the reflection cache
|
105
|
+
send(name, :reload)
|
124
106
|
end
|
107
|
+
end
|
125
108
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
end
|
132
|
-
if active_record_3?
|
133
|
-
_run_destroy_callbacks do
|
134
|
-
deleted? || new_record? ? save : set_deleted_at(Time.now)
|
135
|
-
end
|
136
|
-
else
|
137
|
-
run_callbacks :before_destroy
|
138
|
-
deleted? || new_record? ? save : set_deleted_at(Time.now)
|
139
|
-
run_callbacks :after_destroy
|
140
|
-
end
|
141
|
-
self
|
109
|
+
def attempt_notifying_observers(callback)
|
110
|
+
begin
|
111
|
+
notify_observers(callback)
|
112
|
+
rescue NoMethodError => e
|
113
|
+
# do nothing: this model isn't being observed
|
142
114
|
end
|
115
|
+
end
|
143
116
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
end.each do |name, reflection|
|
148
|
-
cardinality = reflection.macro.to_s.gsub('has_', '')
|
149
|
-
if cardinality == 'many'
|
150
|
-
records = send(name)
|
151
|
-
records = records.unscoped if active_record_3?
|
152
|
-
records = records.find(:all,
|
153
|
-
:conditions => [
|
154
|
-
"#{reflection.quoted_table_name}.deleted_at > ?" +
|
155
|
-
" AND " +
|
156
|
-
"#{reflection.quoted_table_name}.deleted_at < ?",
|
157
|
-
deleted_at - 3.seconds,
|
158
|
-
deleted_at + 3.seconds
|
159
|
-
]
|
160
|
-
)
|
161
|
-
elsif cardinality == 'one' or cardinality == 'belongs_to'
|
162
|
-
if active_record_3?
|
163
|
-
self.class.unscoped do
|
164
|
-
records = [] << send(name)
|
165
|
-
end
|
166
|
-
else
|
167
|
-
records = [] << send(name)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
[records].flatten.compact.each do |dependent|
|
171
|
-
dependent.revive
|
172
|
-
end
|
117
|
+
# return the records corresponding to an association with the `:dependent => :destroy` option
|
118
|
+
def get_dependent_records
|
119
|
+
dependent_records = {}
|
173
120
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
# do nothing: this model isn't being observed
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
# return the records corresponding to an association with the `:dependent => :destroy` option
|
188
|
-
def get_dependent_records
|
189
|
-
dependent_records = {}
|
190
|
-
|
191
|
-
# check which dependent records are to be destroyed
|
192
|
-
klass = self.class
|
193
|
-
klass.reflections.each do |key, reflection|
|
194
|
-
if reflection.options[:dependent] == :destroy
|
195
|
-
next unless records = self.send(key) # skip if there are no dependent record instances
|
196
|
-
if records.respond_to? :size
|
197
|
-
next unless records.size > 0 # skip if there are no dependent record instances
|
198
|
-
else
|
199
|
-
records = [] << records
|
200
|
-
end
|
201
|
-
dependent_record = records.first
|
202
|
-
next if dependent_record.nil?
|
203
|
-
dependent_records[dependent_record.class] = records.map(&:id)
|
121
|
+
# check which dependent records are to be destroyed
|
122
|
+
klass = self.class
|
123
|
+
klass.reflections.each do |key, reflection|
|
124
|
+
if reflection.options[:dependent] == :destroy
|
125
|
+
next unless records = self.send(key) # skip if there are no dependent record instances
|
126
|
+
if records.respond_to? :size
|
127
|
+
next unless records.size > 0 # skip if there are no dependent record instances
|
128
|
+
else
|
129
|
+
records = [] << records
|
204
130
|
end
|
131
|
+
dependent_record = records.first
|
132
|
+
next if dependent_record.nil?
|
133
|
+
dependent_records[dependent_record.class] = records.map(&:id)
|
205
134
|
end
|
206
|
-
dependent_records
|
207
|
-
end
|
208
|
-
|
209
|
-
# If we force the destruction of the record, we will need to force the destruction of dependent records if the
|
210
|
-
# user specified `:dependent => :destroy` in the model.
|
211
|
-
# By default, the call to super/destroy_with_permanent_records (i.e. the &block param) will only soft delete
|
212
|
-
# the dependent records; we keep track of the dependent records
|
213
|
-
# that have `:dependent => :destroy` and call destroy(force) on them after the call to super
|
214
|
-
def permanently_delete_records_after(&block)
|
215
|
-
dependent_records = get_dependent_records
|
216
|
-
result = block.call
|
217
|
-
if result
|
218
|
-
permanently_delete_records(dependent_records)
|
219
|
-
end
|
220
|
-
result
|
221
135
|
end
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
136
|
+
dependent_records
|
137
|
+
end
|
138
|
+
|
139
|
+
# If we force the destruction of the record, we will need to force the destruction of dependent records if the
|
140
|
+
# user specified `:dependent => :destroy` in the model.
|
141
|
+
# By default, the call to super/destroy_with_permanent_records (i.e. the &block param) will only soft delete
|
142
|
+
# the dependent records; we keep track of the dependent records
|
143
|
+
# that have `:dependent => :destroy` and call destroy(force) on them after the call to super
|
144
|
+
def permanently_delete_records_after(&block)
|
145
|
+
dependent_records = get_dependent_records
|
146
|
+
result = block.call
|
147
|
+
if result
|
148
|
+
permanently_delete_records(dependent_records)
|
149
|
+
end
|
150
|
+
result
|
151
|
+
end
|
152
|
+
|
153
|
+
# permanently delete the records (i.e. remove from database)
|
154
|
+
def permanently_delete_records(dependent_records)
|
155
|
+
dependent_records.each do |klass, ids|
|
156
|
+
ids.each do |id|
|
157
|
+
record = begin
|
158
|
+
klass.unscoped.find id
|
159
|
+
rescue ActiveRecord::RecordNotFound
|
160
|
+
next # the record has already been deleted, possibly due to another association with `:dependent => :destroy`
|
236
161
|
end
|
162
|
+
record.deleted_at = nil
|
163
|
+
record.destroy(:force)
|
237
164
|
end
|
238
165
|
end
|
239
|
-
|
240
|
-
def active_record_3?
|
241
|
-
ActiveRecord::VERSION::MAJOR >= 3
|
242
|
-
end
|
243
166
|
end
|
244
167
|
end
|
245
168
|
|
data/permanent_records.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "permanent_records"
|
8
|
-
s.version =
|
8
|
+
s.version = File.read('VERSION')
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Jack Danger Canty", "David Sulc"]
|
@@ -26,20 +26,20 @@ Gem::Specification.new do |s|
|
|
26
26
|
"VERSION",
|
27
27
|
"lib/permanent_records.rb",
|
28
28
|
"permanent_records.gemspec",
|
29
|
-
"
|
30
|
-
"
|
31
|
-
"
|
32
|
-
"
|
33
|
-
"
|
34
|
-
"
|
35
|
-
"
|
36
|
-
"
|
37
|
-
"
|
38
|
-
"
|
39
|
-
"
|
40
|
-
"
|
41
|
-
"
|
42
|
-
"
|
29
|
+
"spec/spec_helper.rb",
|
30
|
+
"spec/permanent_records_spec.rb",
|
31
|
+
"spec/support/comment.rb",
|
32
|
+
"spec/support/database.yml",
|
33
|
+
"spec/support/difficulty.rb",
|
34
|
+
"spec/support/dirt.rb",
|
35
|
+
"spec/support/earthworm.rb",
|
36
|
+
"spec/support/hole.rb",
|
37
|
+
"spec/support/kitty.rb",
|
38
|
+
"spec/support/location.rb",
|
39
|
+
"spec/support/mole.rb",
|
40
|
+
"spec/support/muskrat.rb",
|
41
|
+
"spec/support/schema.rb",
|
42
|
+
"spec/support/unused_model.rb"
|
43
43
|
]
|
44
44
|
s.homepage = "http://github.com/JackDanger/permanent_records"
|
45
45
|
s.require_paths = ["lib"]
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PermanentRecords do
|
4
|
+
|
5
|
+
let!(:frozen_moment) { Time.now }
|
6
|
+
let!(:dirt) { Dirt.create! }
|
7
|
+
let!(:earthworm) { dirt.create_earthworm }
|
8
|
+
let!(:hole) { dirt.create_hole }
|
9
|
+
let!(:muskrat) { hole.muskrats.create! }
|
10
|
+
let!(:mole) { hole.moles.create! }
|
11
|
+
let!(:location) { hole.create_location }
|
12
|
+
let!(:difficulty) { hole.create_difficulty }
|
13
|
+
let!(:comments) { 2.times.map {hole.comments.create!} }
|
14
|
+
let!(:kitty) { Kitty.create! }
|
15
|
+
|
16
|
+
|
17
|
+
describe '#destroy' do
|
18
|
+
|
19
|
+
let(:record) { hole }
|
20
|
+
let(:should_force) { false }
|
21
|
+
|
22
|
+
subject { record.destroy should_force }
|
23
|
+
|
24
|
+
it 'returns the record' do
|
25
|
+
subject.should == record
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'makes deleted? return true' do
|
29
|
+
subject.should be_deleted
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'sets the deleted_at attribute' do
|
33
|
+
subject.deleted_at.should be_within(0.1).of(Time.now)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'does not really remove the record' do
|
37
|
+
expect { subject }.to_not change { record.class.count }
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'with force argument set to truthy' do
|
41
|
+
let(:should_force) { :force }
|
42
|
+
|
43
|
+
it 'does really remove the record' do
|
44
|
+
expect { subject }.to change { record.class.count }.by(-1)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'when model has no deleted_at column' do
|
49
|
+
let(:record) { kitty }
|
50
|
+
|
51
|
+
it 'really removes the record' do
|
52
|
+
expect { subject }.to change { record.class.count }.by(-1)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'with dependent records' do
|
57
|
+
context 'that are permanent' do
|
58
|
+
it '' do
|
59
|
+
expect { subject }.to_not change { Muskrat.count }
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'with has_many cardinality' do
|
63
|
+
it 'marks records as deleted' do
|
64
|
+
subject.muskrats.each {|m| m.should be_deleted }
|
65
|
+
end
|
66
|
+
context 'with force delete' do
|
67
|
+
let(:should_force) { :force }
|
68
|
+
it('') { expect { subject }.to change { Muskrat.count }.by(-1) }
|
69
|
+
it('') { expect { subject }.to change { Comment.count }.by(-2) }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'with has_one cardinality' do
|
74
|
+
it 'marks records as deleted' do
|
75
|
+
subject.location.should be_deleted
|
76
|
+
end
|
77
|
+
context 'with force delete' do
|
78
|
+
let(:should_force) { :force }
|
79
|
+
it('') { expect { subject }.to change { Muskrat.count }.by(-1) }
|
80
|
+
it('') { expect { subject }.to change { Location.count }.by(-1) }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
context 'that are non-permanent' do
|
85
|
+
it 'removes them' do
|
86
|
+
expect { subject }.to change { Mole.count }.by(-1)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
context 'as default scope' do
|
90
|
+
context 'with :has_many cardinality' do
|
91
|
+
its('comments.size') { should == 2 }
|
92
|
+
it 'deletes them' do
|
93
|
+
subject.comments.each {|c| c.should be_deleted }
|
94
|
+
subject.comments.each {|c| Comment.find_by_id(c.id).should be_nil }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
context 'with :has_one cardinality' do
|
98
|
+
it 'deletes them' do
|
99
|
+
subject.difficulty.should be_deleted
|
100
|
+
Difficulty.find_by_id(subject.difficulty.id).should be_nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#revive' do
|
108
|
+
|
109
|
+
let!(:record) { hole.destroy }
|
110
|
+
|
111
|
+
subject { record.revive }
|
112
|
+
|
113
|
+
it 'returns the record' do
|
114
|
+
subject.should == record
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'unsets deleted_at' do
|
118
|
+
expect { subject }.to change {
|
119
|
+
record.deleted_at
|
120
|
+
}.to(nil)
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'makes deleted? return false' do
|
124
|
+
subject.should_not be_deleted
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'when validations fail' do
|
128
|
+
before {
|
129
|
+
Hole.any_instance.stub(:valid?).and_return(false)
|
130
|
+
}
|
131
|
+
it 'raises' do
|
132
|
+
expect { subject }.to raise_error(ActiveRecord::RecordInvalid)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'with dependent records' do
|
137
|
+
context 'that are permanent' do
|
138
|
+
it '' do
|
139
|
+
expect { subject }.to_not change { Muskrat.count }
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'that were deleted previously' do
|
143
|
+
before { muskrat.update_attributes! :deleted_at => 2.minutes.ago }
|
144
|
+
it 'does not restore' do
|
145
|
+
expect { subject }.to_not change { muskrat.deleted? }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'with has_many cardinality' do
|
150
|
+
it 'revives them' do
|
151
|
+
subject.muskrats.each {|m| m.should_not be_deleted }
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'with has_one cardinality' do
|
156
|
+
it 'revives them' do
|
157
|
+
subject.location.should_not be_deleted
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
context 'that are non-permanent' do
|
162
|
+
it 'cannot revive them' do
|
163
|
+
expect { subject }.to_not change { Mole.count }
|
164
|
+
end
|
165
|
+
end
|
166
|
+
context 'as default scope' do
|
167
|
+
context 'with :has_many cardinality' do
|
168
|
+
its('comments.size') { should == 2 }
|
169
|
+
it 'revives them' do
|
170
|
+
subject.comments.each {|c| c.should_not be_deleted }
|
171
|
+
subject.comments.each {|c| Comment.find_by_id(c.id).should == c }
|
172
|
+
end
|
173
|
+
end
|
174
|
+
context 'with :has_one cardinality' do
|
175
|
+
it 'revives them' do
|
176
|
+
subject.difficulty.should_not be_deleted
|
177
|
+
Difficulty.find_by_id(subject.difficulty.id).should == difficulty
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe 'scopes' do
|
185
|
+
|
186
|
+
before {
|
187
|
+
3.times { Muskrat.create! }
|
188
|
+
6.times { Muskrat.create!.destroy }
|
189
|
+
}
|
190
|
+
|
191
|
+
context '.not_deleted' do
|
192
|
+
|
193
|
+
it 'counts' do
|
194
|
+
Muskrat.not_deleted.count.should == Muskrat.all.reject(&:deleted?).size
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'has no deleted records' do
|
198
|
+
Muskrat.not_deleted.each {|m| m.should_not be_deleted }
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
context '.deleted' do
|
203
|
+
it 'counts' do
|
204
|
+
Muskrat.deleted.count.should == Muskrat.all.select(&:deleted?).size
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'has no non-deleted records' do
|
208
|
+
Muskrat.deleted.each {|m| m.should be_deleted }
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
|
2
|
+
# Include this file in your test by copying the following line to your test:
|
3
|
+
# require File.expand_path(File.dirname(__FILE__) + "/test_helper")
|
4
|
+
|
5
|
+
lib = Pathname.new File.expand_path('../../lib', File.dirname(__FILE__))
|
6
|
+
support = Pathname.new File.expand_path('../spec/support', File.dirname(__FILE__))
|
7
|
+
$:.unshift lib
|
8
|
+
$:.unshift support
|
9
|
+
RAILS_ROOT = File.dirname(__FILE__)
|
10
|
+
|
11
|
+
require 'active_record'
|
12
|
+
require 'active_support'
|
13
|
+
require 'permanent_records'
|
14
|
+
require 'awesome_print'
|
15
|
+
|
16
|
+
module Rails
|
17
|
+
def self.env; 'test'end
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'logger'
|
21
|
+
ActiveRecord::Base.logger = Logger.new support.join("debug.log")
|
22
|
+
ActiveRecord::Base.configurations = YAML::load_file support.join('database.yml')
|
23
|
+
ActiveRecord::Base.establish_connection
|
24
|
+
|
25
|
+
load 'schema.rb' if File.exist?(support.join('schema.rb'))
|
26
|
+
|
27
|
+
Dir.glob(support.join('*.rb')).each do |file|
|
28
|
+
autoload File.basename(file).chomp('.rb').camelcase.intern, file
|
29
|
+
end.each do |file|
|
30
|
+
require file
|
31
|
+
end
|
32
|
+
|
33
|
+
require 'database_cleaner'
|
34
|
+
|
35
|
+
RSpec.configure do |config|
|
36
|
+
config.before(:suite) do
|
37
|
+
DatabaseCleaner.strategy = :transaction
|
38
|
+
DatabaseCleaner.clean_with(:truncation)
|
39
|
+
end
|
40
|
+
|
41
|
+
config.before(:each) do
|
42
|
+
DatabaseCleaner.start
|
43
|
+
end
|
44
|
+
|
45
|
+
config.after(:each) do
|
46
|
+
DatabaseCleaner.clean
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
test:
|
2
|
+
:adapter: postgresql
|
3
|
+
:database: permanent_records
|
4
|
+
:min_messages: ERROR
|
5
|
+
# sqlite:
|
6
|
+
# :adapter: sqlite
|
7
|
+
# :database: plugin.sqlite.db
|
8
|
+
# sqlite3:
|
9
|
+
# :adapter: sqlite3
|
10
|
+
# :database: ":memory:"
|
11
|
+
# mysql:
|
12
|
+
# :adapter: mysql
|
13
|
+
# :host: localhost
|
14
|
+
# :username: rails
|
15
|
+
# :password:
|
16
|
+
# :database: plugin_test
|
File without changes
|
@@ -1,11 +1,11 @@
|
|
1
1
|
class Earthworm < ActiveRecord::Base
|
2
2
|
belongs_to :dirt
|
3
|
-
|
3
|
+
|
4
4
|
# Earthworms have been known to complain if they're left on their deathbeds without any dirt
|
5
5
|
before_destroy :complain!
|
6
|
-
|
6
|
+
|
7
7
|
def complain!
|
8
8
|
raise "Where's my dirt?!" if Dirt.not_deleted.find(self.dirt_id).nil?
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
class Hole < ActiveRecord::Base
|
2
2
|
# Because when we're destroying a mole hole we're obviously using high explosives.
|
3
3
|
belongs_to :dirt, :dependent => :destroy
|
4
|
-
|
4
|
+
|
5
5
|
# muskrats are permanent
|
6
6
|
has_many :muskrats, :dependent => :destroy
|
7
7
|
# moles are not permanent
|
8
8
|
has_many :moles, :dependent => :destroy
|
9
|
-
|
9
|
+
|
10
10
|
has_one :location, :dependent => :destroy
|
11
11
|
has_one :unused_model, :dependent => :destroy
|
12
12
|
has_one :difficulty, :dependent => :destroy
|
13
13
|
has_many :comments, :dependent => :destroy
|
14
|
-
end
|
14
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: permanent_records
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-02-28 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -94,20 +94,20 @@ files:
|
|
94
94
|
- VERSION
|
95
95
|
- lib/permanent_records.rb
|
96
96
|
- permanent_records.gemspec
|
97
|
-
-
|
98
|
-
-
|
99
|
-
-
|
100
|
-
-
|
101
|
-
-
|
102
|
-
-
|
103
|
-
-
|
104
|
-
-
|
105
|
-
-
|
106
|
-
-
|
107
|
-
-
|
108
|
-
-
|
109
|
-
-
|
110
|
-
-
|
97
|
+
- spec/spec_helper.rb
|
98
|
+
- spec/permanent_records_spec.rb
|
99
|
+
- spec/support/comment.rb
|
100
|
+
- spec/support/database.yml
|
101
|
+
- spec/support/difficulty.rb
|
102
|
+
- spec/support/dirt.rb
|
103
|
+
- spec/support/earthworm.rb
|
104
|
+
- spec/support/hole.rb
|
105
|
+
- spec/support/kitty.rb
|
106
|
+
- spec/support/location.rb
|
107
|
+
- spec/support/mole.rb
|
108
|
+
- spec/support/muskrat.rb
|
109
|
+
- spec/support/schema.rb
|
110
|
+
- spec/support/unused_model.rb
|
111
111
|
homepage: http://github.com/JackDanger/permanent_records
|
112
112
|
licenses: []
|
113
113
|
post_install_message:
|
@@ -122,7 +122,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
122
122
|
version: '0'
|
123
123
|
segments:
|
124
124
|
- 0
|
125
|
-
hash:
|
125
|
+
hash: 1142090409982771536
|
126
126
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
127
|
none: false
|
128
128
|
requirements:
|
data/test/database.yml
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
sqlite:
|
2
|
-
:adapter: sqlite
|
3
|
-
:database: plugin.sqlite.db
|
4
|
-
sqlite3:
|
5
|
-
:adapter: sqlite3
|
6
|
-
:database: ":memory:"
|
7
|
-
postgresql:
|
8
|
-
:adapter: postgresql
|
9
|
-
:username: postgres
|
10
|
-
:password: postgres
|
11
|
-
:database: plugin_test
|
12
|
-
:min_messages: ERROR
|
13
|
-
mysql:
|
14
|
-
:adapter: mysql
|
15
|
-
:host: localhost
|
16
|
-
:username: rails
|
17
|
-
:password:
|
18
|
-
:database: plugin_test
|
data/test/difficulty.rb
DELETED
@@ -1,270 +0,0 @@
|
|
1
|
-
# note: functionality to deal with default scopes (and the `unscoped` scope) has been added (and will be tested) for Rails 3
|
2
|
-
# this default scope functionality is skipped for Rails 2, because Rails 2 provides no functional means of altering the default scope
|
3
|
-
# in particular, setting the default scope in Rails 3 to ignore soft deleted records will work properly, whereas in Rails 2, it will
|
4
|
-
# lead to a world of pain
|
5
|
-
|
6
|
-
require File.expand_path(File.dirname(__FILE__) + "/test_helper")
|
7
|
-
|
8
|
-
%w(earthworm dirt hole mole muskrat kitty location comment difficulty unused_model).each do |a|
|
9
|
-
require File.expand_path(File.dirname(__FILE__) + "/" + a)
|
10
|
-
end
|
11
|
-
|
12
|
-
class PermanentRecordsTest < ActiveSupport::TestCase
|
13
|
-
|
14
|
-
def setup
|
15
|
-
super
|
16
|
-
Muskrat.delete_all
|
17
|
-
@active = Muskrat.create!(:name => 'Wakko')
|
18
|
-
@deleted = Muskrat.create!(:name => 'Yakko', :deleted_at => 4.days.ago)
|
19
|
-
@the_girl = Muskrat.create!(:name => 'Dot')
|
20
|
-
Kitty.delete_all
|
21
|
-
@kitty = Kitty.create!(:name => 'Meow Meow')
|
22
|
-
@dirt = Dirt.create!(:color => 'Brown')
|
23
|
-
@hole = Hole.create(:number => 14, :dirt => @dirt)
|
24
|
-
@hole.muskrats.create(:name => "Active Muskrat")
|
25
|
-
@hole.muskrats.create(:name => "Deleted Muskrat", :deleted_at => 5.days.ago)
|
26
|
-
Location.delete_all
|
27
|
-
@location = Location.create(:name => "South wall")
|
28
|
-
@hole.location = @location
|
29
|
-
@hole.save!
|
30
|
-
@mole = @hole.moles.create(:name => "Grabowski")
|
31
|
-
|
32
|
-
if ActiveRecord::VERSION::MAJOR >= 3
|
33
|
-
Difficulty.unscoped.delete_all
|
34
|
-
Comment.unscoped.delete_all
|
35
|
-
else
|
36
|
-
Difficulty.delete_all
|
37
|
-
Comment.delete_all
|
38
|
-
end
|
39
|
-
# test has_one cardinality with model having a default scope
|
40
|
-
@hole_with_difficulty = Hole.create(:number => 16)
|
41
|
-
@hole_with_difficulty.difficulty = Difficulty.create!(:name => 'Hard')
|
42
|
-
@hole_with_difficulty.save!
|
43
|
-
|
44
|
-
# test has_many cardinality with model having a default scope
|
45
|
-
@hole_with_comments = Hole.create(:number => 16)
|
46
|
-
@hole_with_comments.comments << Comment.create!(:text => "Beware of the pond.")
|
47
|
-
@hole_with_comments.comments << Comment.create!(:text => "Muskrats live here.")
|
48
|
-
end
|
49
|
-
|
50
|
-
def teardown
|
51
|
-
setup
|
52
|
-
end
|
53
|
-
|
54
|
-
def test_destroy_should_return_the_record
|
55
|
-
muskrat = @active
|
56
|
-
assert_equal muskrat, muskrat.destroy
|
57
|
-
end
|
58
|
-
|
59
|
-
def test_revive_should_return_the_record
|
60
|
-
muskrat = @deleted
|
61
|
-
assert_equal muskrat, muskrat.revive
|
62
|
-
end
|
63
|
-
|
64
|
-
def test_destroy_should_set_deleted_at_attribute
|
65
|
-
assert @active.destroy.deleted_at
|
66
|
-
end
|
67
|
-
|
68
|
-
def test_destroy_should_save_deleted_at_attribute
|
69
|
-
assert Muskrat.find(@active.destroy.id).deleted_at
|
70
|
-
end
|
71
|
-
|
72
|
-
def test_destroy_should_not_really_remove_the_record
|
73
|
-
assert Muskrat.find(@active.destroy.id)
|
74
|
-
end
|
75
|
-
|
76
|
-
def test_destroy_should_recognize_a_force_parameter
|
77
|
-
assert_raises(ActiveRecord::RecordNotFound) { @active.destroy(:force).reload }
|
78
|
-
end
|
79
|
-
|
80
|
-
def test_destroy_should_ignore_other_parameters
|
81
|
-
assert Muskrat.find(@active.destroy(:hula_dancer).id)
|
82
|
-
end
|
83
|
-
|
84
|
-
def test_revive_should_unfreeze_record
|
85
|
-
assert !@deleted.revive.frozen?
|
86
|
-
end
|
87
|
-
|
88
|
-
def test_revive_should_unset_deleted_at
|
89
|
-
assert !@deleted.revive.deleted_at
|
90
|
-
end
|
91
|
-
|
92
|
-
def test_revive_should_make_deleted_return_false
|
93
|
-
assert !@deleted.revive.deleted?
|
94
|
-
end
|
95
|
-
|
96
|
-
def test_deleted_returns_true_for_deleted_records
|
97
|
-
assert @deleted.deleted?
|
98
|
-
end
|
99
|
-
|
100
|
-
def test_destroy_returns_record_with_modified_attributes
|
101
|
-
assert @active.destroy.deleted?
|
102
|
-
end
|
103
|
-
|
104
|
-
def test_revive_and_destroy_should_be_chainable
|
105
|
-
assert @active.destroy.revive.destroy.destroy.revive.revive.destroy.deleted?
|
106
|
-
assert !@deleted.destroy.revive.revive.destroy.destroy.revive.deleted?
|
107
|
-
end
|
108
|
-
|
109
|
-
def test_with_counting_on_deleted_limits_scope_to_count_deleted_records
|
110
|
-
assert_equal Muskrat.deleted.length,
|
111
|
-
Muskrat.deleted.count
|
112
|
-
end
|
113
|
-
|
114
|
-
def test_with_counting_on_not_deleted_limits_scope_to_count_not_deleted_records
|
115
|
-
assert_equal Muskrat.not_deleted.length,
|
116
|
-
Muskrat.not_deleted.count
|
117
|
-
end
|
118
|
-
|
119
|
-
def test_with_deleted_limits_scope_to_deleted_records
|
120
|
-
assert Muskrat.deleted.all?(&:deleted?)
|
121
|
-
end
|
122
|
-
|
123
|
-
def test_with_not_deleted_limits_scope_to_not_deleted_records
|
124
|
-
assert !Muskrat.not_deleted.any?(&:deleted?)
|
125
|
-
end
|
126
|
-
|
127
|
-
def test_models_without_a_deleted_at_column_should_destroy_as_normal
|
128
|
-
assert_raises(ActiveRecord::RecordNotFound) {@kitty.destroy.reload}
|
129
|
-
end
|
130
|
-
|
131
|
-
def test_dependent_non_permanent_records_should_be_destroyed
|
132
|
-
assert @hole.is_permanent?
|
133
|
-
assert !@hole.moles.first.is_permanent?
|
134
|
-
assert_difference "Mole.count", -1 do
|
135
|
-
@hole.destroy
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def test_dependent_permanent_records_with_has_many_cardinality_should_be_marked_as_deleted
|
140
|
-
assert @hole.is_permanent?
|
141
|
-
assert @hole.muskrats.first.is_permanent?
|
142
|
-
assert_no_difference "Muskrat.count" do
|
143
|
-
@hole.destroy
|
144
|
-
end
|
145
|
-
|
146
|
-
@hole.reload
|
147
|
-
assert @hole.muskrats.first.deleted?
|
148
|
-
end
|
149
|
-
|
150
|
-
def test_dependent_permanent_records_with_has_one_cardinality_should_be_marked_as_deleted
|
151
|
-
assert @hole.is_permanent?
|
152
|
-
assert @hole.location.is_permanent?
|
153
|
-
assert_no_difference "Location.count" do
|
154
|
-
@hole.destroy
|
155
|
-
end
|
156
|
-
assert @hole.location.deleted?
|
157
|
-
assert Location.find_by_name("South wall").deleted?
|
158
|
-
end
|
159
|
-
|
160
|
-
def test_dependent_permanent_records_with_has_many_cardinality_should_be_revived_when_parent_is_revived
|
161
|
-
assert @hole.is_permanent?
|
162
|
-
@hole.destroy
|
163
|
-
assert @dirt.deleted?
|
164
|
-
assert @hole.muskrats.find_by_name("Active Muskrat").deleted?
|
165
|
-
@hole.revive
|
166
|
-
assert !@hole.muskrats.find_by_name("Active Muskrat").deleted?
|
167
|
-
assert !@dirt.deleted?
|
168
|
-
end
|
169
|
-
|
170
|
-
def test_dependent_permanent_records_with_has_one_cardinality_should_be_revived_when_parent_is_revived
|
171
|
-
assert @hole.is_permanent?
|
172
|
-
@hole.destroy
|
173
|
-
assert Location.find_by_name("South wall").deleted?
|
174
|
-
@hole.revive
|
175
|
-
assert !Location.find_by_name("South wall").deleted?
|
176
|
-
end
|
177
|
-
|
178
|
-
def test_before_callbacks_for_dependent_records_fire_before_destroy_occurs
|
179
|
-
@earthworm = Earthworm.create(:dirt => @dirt)
|
180
|
-
|
181
|
-
assert_nothing_raised do
|
182
|
-
@hole.destroy
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
# see comment at top of file for reasoning behind conditional testing of default scope
|
187
|
-
if ActiveRecord::VERSION::MAJOR >= 3
|
188
|
-
def test_dependent_permanent_records_with_has_one_cardinality_and_default_scope_should_be_revived_when_parent_is_revived
|
189
|
-
assert @hole_with_difficulty.is_permanent?
|
190
|
-
assert_difference("Difficulty.count", -1) do
|
191
|
-
@hole_with_difficulty.destroy
|
192
|
-
end
|
193
|
-
assert_nil Difficulty.find_by_name("Hard")
|
194
|
-
assert Difficulty.unscoped.find_by_name("Hard").deleted?
|
195
|
-
@hole_with_difficulty.revive
|
196
|
-
assert_not_nil Difficulty.find_by_name("Hard")
|
197
|
-
assert !Difficulty.unscoped.find_by_name("Hard").deleted?
|
198
|
-
end
|
199
|
-
|
200
|
-
def test_dependent_permanent_records_with_has_many_cardinality_and_default_scope_should_be_revived_when_parent_is_revived
|
201
|
-
assert @hole_with_comments.is_permanent?
|
202
|
-
assert_difference("Comment.count", -2) do
|
203
|
-
@hole_with_comments.destroy
|
204
|
-
end
|
205
|
-
assert_nil Comment.find_by_text("Beware of the pond.")
|
206
|
-
assert Comment.unscoped.find_by_text("Beware of the pond.").deleted?
|
207
|
-
@hole_with_comments.revive
|
208
|
-
assert_not_nil Comment.find_by_text("Beware of the pond.")
|
209
|
-
assert !Comment.unscoped.find_by_text("Beware of the pond.").deleted?
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
def test_inexistent_dependent_models_should_not_cause_errors
|
214
|
-
hole_with_unused_model = Hole.create!(:number => 1)
|
215
|
-
hole_with_unused_model.destroy
|
216
|
-
assert_nothing_raised do
|
217
|
-
hole_with_unused_model.revive
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
def test_old_dependent_permanent_records_should_not_be_revived
|
222
|
-
assert @hole.is_permanent?
|
223
|
-
@hole.destroy
|
224
|
-
assert @hole.muskrats.find_by_name("Deleted Muskrat").deleted?
|
225
|
-
@hole.revive
|
226
|
-
assert @hole.muskrats.find_by_name("Deleted Muskrat").deleted?
|
227
|
-
end
|
228
|
-
|
229
|
-
def test_validate_records_before_revival
|
230
|
-
duplicate_location = Location.new(@location.attributes)
|
231
|
-
@location.destroy
|
232
|
-
@location.reload
|
233
|
-
duplicate_location.save!
|
234
|
-
assert_equal duplicate_location.name, @location.name
|
235
|
-
assert_no_difference('Location.not_deleted.count') do
|
236
|
-
assert_raise (ActiveRecord::RecordInvalid) do
|
237
|
-
@location.revive
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
def test_force_deleting_a_record_with_has_one_force_deletes_dependent_records
|
243
|
-
hole = Hole.create(:number => 1)
|
244
|
-
location = Location.create(:name => "Near the clubhouse")
|
245
|
-
hole.location = location
|
246
|
-
hole.save!
|
247
|
-
|
248
|
-
assert_difference(monitor_for('Hole'), -1) do
|
249
|
-
assert_difference(monitor_for('Location'), -1) do
|
250
|
-
hole.destroy(:force)
|
251
|
-
end
|
252
|
-
end
|
253
|
-
end
|
254
|
-
|
255
|
-
def test_force_deleting_a_record_with_has_many_force_deletes_dependent_records
|
256
|
-
assert_difference(monitor_for('Hole'), -1) do
|
257
|
-
assert_difference(monitor_for('Comment'), -2) do
|
258
|
-
@hole_with_comments.destroy(:force)
|
259
|
-
end
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
263
|
-
def test_force_deletign_with_multiple_associations
|
264
|
-
assert_difference(monitor_for('Muskrat'), -2) do
|
265
|
-
assert_difference(monitor_for('Mole'), -1) do
|
266
|
-
@hole.destroy(:force)
|
267
|
-
end
|
268
|
-
end
|
269
|
-
end
|
270
|
-
end
|
data/test/test_helper.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
# Include this file in your test by copying the following line to your test:
|
2
|
-
# require File.expand_path(File.dirname(__FILE__) + "/test_helper")
|
3
|
-
|
4
|
-
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
5
|
-
RAILS_ROOT = File.dirname(__FILE__)
|
6
|
-
|
7
|
-
require 'rubygems'
|
8
|
-
require 'test/unit'
|
9
|
-
require 'active_record'
|
10
|
-
require 'active_record/fixtures'
|
11
|
-
require File.expand_path(File.dirname(__FILE__) + '/../lib/permanent_records')
|
12
|
-
|
13
|
-
require File.expand_path(File.dirname(__FILE__) + "/muskrat")
|
14
|
-
|
15
|
-
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
16
|
-
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
17
|
-
ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite3'])
|
18
|
-
|
19
|
-
load(File.dirname(__FILE__) + "/schema.rb") if File.exist?(File.dirname(__FILE__) + "/schema.rb")
|
20
|
-
|
21
|
-
class ActiveSupport::TestCase #:nodoc:
|
22
|
-
include ActiveRecord::TestFixtures
|
23
|
-
# def create_fixtures(*table_names)
|
24
|
-
# if block_given?
|
25
|
-
# Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
|
26
|
-
# else
|
27
|
-
# Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
|
28
|
-
# end
|
29
|
-
# end
|
30
|
-
|
31
|
-
self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
|
32
|
-
$LOAD_PATH.unshift(fixture_path)
|
33
|
-
|
34
|
-
# Turn off transactional fixtures if you're working with MyISAM tables in MySQL
|
35
|
-
self.use_transactional_fixtures = true
|
36
|
-
|
37
|
-
# Instantiated fixtures are slow, but give you @david where you otherwise would need people(:david)
|
38
|
-
self.use_instantiated_fixtures = false
|
39
|
-
|
40
|
-
# Add more helper methods to be used by all tests here...
|
41
|
-
def monitor_for(class_name)
|
42
|
-
result = class_name
|
43
|
-
result += '.unscoped' if ActiveRecord::VERSION::MAJOR >= 3
|
44
|
-
result += '.count'
|
45
|
-
end
|
46
|
-
end
|