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