seed_dump 3.4.0 → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -3
- data/lib/seed_dump/dump_methods.rb +52 -0
- data/lib/seed_dump/environment.rb +9 -0
- metadata +121 -54
- data/.github/workflows/release.yml +0 -38
- data/.rspec +0 -2
- data/Appraisals +0 -47
- data/Gemfile +0 -36
- data/Rakefile +0 -32
- data/VERSION +0 -1
- data/find_ruby_compat.sh +0 -237
- data/gemfiles/rails_6.1.gemfile +0 -28
- data/gemfiles/rails_6.1.gemfile.lock +0 -150
- data/gemfiles/rails_7.0.gemfile +0 -28
- data/gemfiles/rails_7.0.gemfile.lock +0 -148
- data/gemfiles/rails_7.1.gemfile +0 -28
- data/gemfiles/rails_7.1.gemfile.lock +0 -161
- data/gemfiles/rails_7.2.gemfile +0 -28
- data/gemfiles/rails_7.2.gemfile.lock +0 -164
- data/gemfiles/rails_8.0.gemfile +0 -28
- data/gemfiles/rails_8.0.gemfile.lock +0 -163
- data/seed_dump.gemspec +0 -75
- data/spec/dump_methods_spec.rb +0 -1114
- data/spec/environment_spec.rb +0 -613
- data/spec/factories/another_samples.rb +0 -21
- data/spec/factories/authors.rb +0 -5
- data/spec/factories/base_users.rb +0 -16
- data/spec/factories/books.rb +0 -6
- data/spec/factories/bosses.rb +0 -5
- data/spec/factories/reviews.rb +0 -7
- data/spec/factories/samples.rb +0 -20
- data/spec/factories/yet_another_samples.rb +0 -21
- data/spec/helpers.rb +0 -252
- data/spec/spec_helper.rb +0 -59
data/spec/environment_spec.rb
DELETED
|
@@ -1,613 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe SeedDump do
|
|
4
|
-
describe '.dump_using_environment' do
|
|
5
|
-
# Schema creation and model loading are handled in spec_helper's before(:suite).
|
|
6
|
-
|
|
7
|
-
before(:each) do
|
|
8
|
-
# Clean DB and create a fresh sample before each example
|
|
9
|
-
DatabaseCleaner.start
|
|
10
|
-
FactoryBot.create(:sample)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
after(:each) do
|
|
14
|
-
# Clean DB after each example
|
|
15
|
-
DatabaseCleaner.clean
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
describe 'APPEND' do
|
|
20
|
-
it "should specify append as true if the APPEND env var is 'true'" do
|
|
21
|
-
expect(SeedDump).to receive(:dump).with(anything, include(append: true))
|
|
22
|
-
# Need to stub dump for other models if they exist in this context
|
|
23
|
-
allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
|
|
24
|
-
allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
|
|
25
|
-
SeedDump.dump_using_environment('APPEND' => 'true')
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
it "should specify append as true if the APPEND env var is 'TRUE'" do
|
|
29
|
-
expect(SeedDump).to receive(:dump).with(anything, include(append: true))
|
|
30
|
-
allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
|
|
31
|
-
allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
|
|
32
|
-
SeedDump.dump_using_environment('APPEND' => 'TRUE')
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it "should specify append as false the first time if the APPEND env var is not 'true' (and true after that)" do
|
|
36
|
-
FactoryBot.create(:another_sample)
|
|
37
|
-
expect(SeedDump).to receive(:dump).with(Sample, include(append: false)).ordered
|
|
38
|
-
expect(SeedDump).to receive(:dump).with(AnotherSample, include(append: true)).ordered
|
|
39
|
-
# Explicitly set MODELS to control order and prevent other models interfering
|
|
40
|
-
SeedDump.dump_using_environment('APPEND' => 'false', 'MODELS' => 'Sample,AnotherSample')
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
describe 'BATCH_SIZE' do
|
|
45
|
-
it 'should pass along the specified batch size' do
|
|
46
|
-
expect(SeedDump).to receive(:dump).with(anything, include(batch_size: 17))
|
|
47
|
-
allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
|
|
48
|
-
allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
|
|
49
|
-
SeedDump.dump_using_environment('BATCH_SIZE' => '17')
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
it 'should pass along a nil batch size if BATCH_SIZE is not specified' do
|
|
53
|
-
expect(SeedDump).to receive(:dump).with(anything, include(batch_size: nil))
|
|
54
|
-
allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
|
|
55
|
-
allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
|
|
56
|
-
SeedDump.dump_using_environment
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
describe 'EXCLUDE' do
|
|
61
|
-
it 'should pass along any attributes to be excluded' do
|
|
62
|
-
expect(SeedDump).to receive(:dump).with(anything, include(exclude: [:baggins, :saggins]))
|
|
63
|
-
allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
|
|
64
|
-
allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
|
|
65
|
-
SeedDump.dump_using_environment('EXCLUDE' => 'baggins,saggins')
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
it 'should pass an empty array when EXCLUDE is set to empty string (issue #147)' do
|
|
69
|
-
expect(SeedDump).to receive(:dump).with(anything, include(exclude: []))
|
|
70
|
-
allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
|
|
71
|
-
allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
|
|
72
|
-
SeedDump.dump_using_environment('EXCLUDE' => '')
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
it 'should pass nil when EXCLUDE is not set (to use default excludes)' do
|
|
76
|
-
expect(SeedDump).to receive(:dump).with(anything, include(exclude: nil))
|
|
77
|
-
allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
|
|
78
|
-
allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
|
|
79
|
-
SeedDump.dump_using_environment
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
it 'should pass an empty array when INCLUDE_ALL is true (issue #147)' do
|
|
83
|
-
expect(SeedDump).to receive(:dump).with(anything, include(exclude: []))
|
|
84
|
-
allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
|
|
85
|
-
allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
|
|
86
|
-
SeedDump.dump_using_environment('INCLUDE_ALL' => 'true')
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
it 'should let explicit EXCLUDE override INCLUDE_ALL' do
|
|
90
|
-
expect(SeedDump).to receive(:dump).with(anything, include(exclude: [:some_field]))
|
|
91
|
-
allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
|
|
92
|
-
allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
|
|
93
|
-
SeedDump.dump_using_environment('INCLUDE_ALL' => 'true', 'EXCLUDE' => 'some_field')
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
describe 'FILE' do
|
|
98
|
-
it 'should pass the FILE parameter to the dump method correctly' do
|
|
99
|
-
expect(SeedDump).to receive(:dump).with(anything, include(file: 'blargle'))
|
|
100
|
-
allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
|
|
101
|
-
allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
|
|
102
|
-
SeedDump.dump_using_environment('FILE' => 'blargle')
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
it 'should pass db/seeds.rb as the file parameter if no FILE is specified' do
|
|
106
|
-
expect(SeedDump).to receive(:dump).with(anything, include(file: 'db/seeds.rb'))
|
|
107
|
-
allow(SeedDump).to receive(:dump).with(AnotherSample, anything) if defined?(AnotherSample)
|
|
108
|
-
allow(SeedDump).to receive(:dump).with(YetAnotherSample, anything) if defined?(YetAnotherSample)
|
|
109
|
-
SeedDump.dump_using_environment
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
describe 'LIMIT' do
|
|
114
|
-
it 'should apply the specified limit to the records' do
|
|
115
|
-
relation_double = double('ActiveRecord relation double')
|
|
116
|
-
allow(Sample).to receive(:limit).with(5).and_return(relation_double)
|
|
117
|
-
expect(SeedDump).to receive(:dump).with(relation_double, anything)
|
|
118
|
-
# Allow other calls if necessary
|
|
119
|
-
allow(SeedDump).to receive(:dump).with(instance_of(Class), anything) unless relation_double.is_a?(Class)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
SeedDump.dump_using_environment('LIMIT' => '5')
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
describe 'MODEL_LIMITS (issue #142)' do
|
|
127
|
-
# MODEL_LIMITS allows per-model limit overrides to prevent LIMIT from breaking
|
|
128
|
-
# associations. For example, if Teacher has_many Students, you can set
|
|
129
|
-
# MODEL_LIMITS=Teacher:0 to dump all teachers while limiting other models.
|
|
130
|
-
|
|
131
|
-
it 'should apply per-model limit when specified' do
|
|
132
|
-
FactoryBot.create(:another_sample)
|
|
133
|
-
|
|
134
|
-
sample_relation = double('Sample relation')
|
|
135
|
-
another_sample_relation = double('AnotherSample relation')
|
|
136
|
-
|
|
137
|
-
allow(Sample).to receive(:limit).with(5).and_return(sample_relation)
|
|
138
|
-
allow(AnotherSample).to receive(:limit).with(20).and_return(another_sample_relation)
|
|
139
|
-
|
|
140
|
-
expect(SeedDump).to receive(:dump).with(sample_relation, anything)
|
|
141
|
-
expect(SeedDump).to receive(:dump).with(another_sample_relation, anything)
|
|
142
|
-
|
|
143
|
-
SeedDump.dump_using_environment(
|
|
144
|
-
'MODELS' => 'Sample,AnotherSample',
|
|
145
|
-
'MODEL_LIMITS' => 'Sample:5,AnotherSample:20'
|
|
146
|
-
)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
it 'should interpret 0 as unlimited (dump all records)' do
|
|
150
|
-
# When MODEL_LIMITS=Sample:0, Sample should not have limit applied
|
|
151
|
-
expect(Sample).not_to receive(:limit)
|
|
152
|
-
expect(SeedDump).to receive(:dump).with(Sample, anything)
|
|
153
|
-
|
|
154
|
-
SeedDump.dump_using_environment(
|
|
155
|
-
'MODELS' => 'Sample',
|
|
156
|
-
'MODEL_LIMITS' => 'Sample:0'
|
|
157
|
-
)
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
it 'should fall back to global LIMIT for models not in MODEL_LIMITS' do
|
|
161
|
-
FactoryBot.create(:another_sample)
|
|
162
|
-
|
|
163
|
-
# Sample has specific limit of 5, AnotherSample falls back to global LIMIT of 10
|
|
164
|
-
sample_relation = double('Sample relation')
|
|
165
|
-
another_sample_relation = double('AnotherSample relation')
|
|
166
|
-
|
|
167
|
-
allow(Sample).to receive(:limit).with(5).and_return(sample_relation)
|
|
168
|
-
allow(AnotherSample).to receive(:limit).with(10).and_return(another_sample_relation)
|
|
169
|
-
|
|
170
|
-
expect(SeedDump).to receive(:dump).with(sample_relation, anything)
|
|
171
|
-
expect(SeedDump).to receive(:dump).with(another_sample_relation, anything)
|
|
172
|
-
|
|
173
|
-
SeedDump.dump_using_environment(
|
|
174
|
-
'MODELS' => 'Sample,AnotherSample',
|
|
175
|
-
'LIMIT' => '10',
|
|
176
|
-
'MODEL_LIMITS' => 'Sample:5'
|
|
177
|
-
)
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
it 'should work with MODEL_LIMITS alone (no global LIMIT)' do
|
|
181
|
-
FactoryBot.create(:another_sample)
|
|
182
|
-
|
|
183
|
-
# Sample has limit of 5, AnotherSample has no limit (dumps all)
|
|
184
|
-
sample_relation = double('Sample relation')
|
|
185
|
-
|
|
186
|
-
allow(Sample).to receive(:limit).with(5).and_return(sample_relation)
|
|
187
|
-
expect(AnotherSample).not_to receive(:limit)
|
|
188
|
-
|
|
189
|
-
expect(SeedDump).to receive(:dump).with(sample_relation, anything)
|
|
190
|
-
expect(SeedDump).to receive(:dump).with(AnotherSample, anything)
|
|
191
|
-
|
|
192
|
-
SeedDump.dump_using_environment(
|
|
193
|
-
'MODELS' => 'Sample,AnotherSample',
|
|
194
|
-
'MODEL_LIMITS' => 'Sample:5'
|
|
195
|
-
)
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
it 'should handle whitespace in MODEL_LIMITS' do
|
|
199
|
-
sample_relation = double('Sample relation')
|
|
200
|
-
allow(Sample).to receive(:limit).with(5).and_return(sample_relation)
|
|
201
|
-
expect(SeedDump).to receive(:dump).with(sample_relation, anything)
|
|
202
|
-
|
|
203
|
-
SeedDump.dump_using_environment(
|
|
204
|
-
'MODELS' => 'Sample',
|
|
205
|
-
'MODEL_LIMITS' => ' Sample : 5 '
|
|
206
|
-
)
|
|
207
|
-
end
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
['', 'S'].each do |model_suffix|
|
|
211
|
-
model_env = 'MODEL' + model_suffix
|
|
212
|
-
|
|
213
|
-
describe model_env do
|
|
214
|
-
context "if #{model_env} is not specified" do
|
|
215
|
-
it "should dump all non-empty models" do
|
|
216
|
-
FactoryBot.create(:another_sample)
|
|
217
|
-
expect(SeedDump).to receive(:dump).with(Sample, anything)
|
|
218
|
-
expect(SeedDump).to receive(:dump).with(AnotherSample, anything)
|
|
219
|
-
SeedDump.dump_using_environment
|
|
220
|
-
end
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
context "if #{model_env} is specified" do
|
|
224
|
-
it "should dump only the specified model" do
|
|
225
|
-
FactoryBot.create(:another_sample)
|
|
226
|
-
expect(SeedDump).to receive(:dump).with(Sample, anything)
|
|
227
|
-
# Ensure the other model is NOT dumped
|
|
228
|
-
expect(SeedDump).not_to receive(:dump).with(AnotherSample, anything)
|
|
229
|
-
SeedDump.dump_using_environment(model_env => 'Sample')
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
it "should not dump empty models" do
|
|
233
|
-
expect(SeedDump).not_to receive(:dump).with(EmptyModel, anything)
|
|
234
|
-
# Ensure Sample is still dumped
|
|
235
|
-
expect(SeedDump).to receive(:dump).with(Sample, anything)
|
|
236
|
-
SeedDump.dump_using_environment(model_env => 'EmptyModel, Sample')
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
end
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
describe "MODELS_EXCLUDE" do
|
|
243
|
-
it "should dump all non-empty models except the specified models" do
|
|
244
|
-
FactoryBot.create(:another_sample)
|
|
245
|
-
expect(SeedDump).to receive(:dump).with(Sample, anything)
|
|
246
|
-
# Ensure the excluded model is NOT dumped
|
|
247
|
-
expect(SeedDump).not_to receive(:dump).with(AnotherSample, anything)
|
|
248
|
-
SeedDump.dump_using_environment('MODELS_EXCLUDE' => 'AnotherSample')
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
describe 'model names ending in s (issue #121)' do
|
|
253
|
-
# Model names like "Boss" are incorrectly singularized to "Bos" when
|
|
254
|
-
# processing MODELS=Boss, causing NameError: uninitialized constant Bos.
|
|
255
|
-
# The fix should use the exact model name if it resolves to a valid constant.
|
|
256
|
-
|
|
257
|
-
it 'should handle model name "Boss" without extra s' do
|
|
258
|
-
FactoryBot.create(:boss)
|
|
259
|
-
expect(SeedDump).to receive(:dump).with(Boss, anything)
|
|
260
|
-
SeedDump.dump_using_environment('MODELS' => 'Boss')
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
it 'should handle model name "boss" (lowercase) without extra s' do
|
|
264
|
-
FactoryBot.create(:boss)
|
|
265
|
-
expect(SeedDump).to receive(:dump).with(Boss, anything)
|
|
266
|
-
SeedDump.dump_using_environment('MODELS' => 'boss')
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
it 'should handle MODELS_EXCLUDE with model names ending in s' do
|
|
270
|
-
FactoryBot.create(:boss)
|
|
271
|
-
expect(SeedDump).to receive(:dump).with(Sample, anything)
|
|
272
|
-
expect(SeedDump).not_to receive(:dump).with(Boss, anything)
|
|
273
|
-
SeedDump.dump_using_environment('MODELS_EXCLUDE' => 'Boss')
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
it 'should still handle plural model names (e.g., "samples" -> Sample)' do
|
|
277
|
-
expect(SeedDump).to receive(:dump).with(Sample, anything)
|
|
278
|
-
SeedDump.dump_using_environment('MODELS' => 'samples')
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
it 'should still handle plural model names in MODELS_EXCLUDE' do
|
|
282
|
-
FactoryBot.create(:another_sample)
|
|
283
|
-
expect(SeedDump).to receive(:dump).with(AnotherSample, anything)
|
|
284
|
-
expect(SeedDump).not_to receive(:dump).with(Sample, anything)
|
|
285
|
-
SeedDump.dump_using_environment('MODELS_EXCLUDE' => 'samples')
|
|
286
|
-
end
|
|
287
|
-
end
|
|
288
|
-
|
|
289
|
-
describe 'INSERT_ALL (issue #153)' do
|
|
290
|
-
it "should specify insert_all as true if the INSERT_ALL env var is 'true'" do
|
|
291
|
-
expect(SeedDump).to receive(:dump).with(anything, include(insert_all: true))
|
|
292
|
-
SeedDump.dump_using_environment('INSERT_ALL' => 'true')
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
it "should specify insert_all as true if the INSERT_ALL env var is 'TRUE'" do
|
|
296
|
-
expect(SeedDump).to receive(:dump).with(anything, include(insert_all: true))
|
|
297
|
-
SeedDump.dump_using_environment('INSERT_ALL' => 'TRUE')
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
it "should specify insert_all as false if the INSERT_ALL env var is not 'true'" do
|
|
301
|
-
expect(SeedDump).to receive(:dump).with(anything, include(insert_all: false))
|
|
302
|
-
SeedDump.dump_using_environment('INSERT_ALL' => 'false')
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
it "should specify insert_all as false if the INSERT_ALL env var is not set" do
|
|
306
|
-
expect(SeedDump).to receive(:dump).with(anything, include(insert_all: false))
|
|
307
|
-
SeedDump.dump_using_environment
|
|
308
|
-
end
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
describe 'HEADER (issue #126 - comment header in seed file)' do
|
|
312
|
-
# HEADER adds a comment at the top of the seed file showing that seed_dump
|
|
313
|
-
# was used and what options were specified for traceability.
|
|
314
|
-
|
|
315
|
-
it "should specify header as true if the HEADER env var is 'true'" do
|
|
316
|
-
expect(SeedDump).to receive(:dump).with(anything, include(header: true))
|
|
317
|
-
SeedDump.dump_using_environment('HEADER' => 'true')
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
it "should specify header as true if the HEADER env var is 'TRUE'" do
|
|
321
|
-
expect(SeedDump).to receive(:dump).with(anything, include(header: true))
|
|
322
|
-
SeedDump.dump_using_environment('HEADER' => 'TRUE')
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
it "should specify header as false if the HEADER env var is not 'true'" do
|
|
326
|
-
expect(SeedDump).to receive(:dump).with(anything, include(header: false))
|
|
327
|
-
SeedDump.dump_using_environment('HEADER' => 'false')
|
|
328
|
-
end
|
|
329
|
-
|
|
330
|
-
it "should specify header as false if the HEADER env var is not set" do
|
|
331
|
-
expect(SeedDump).to receive(:dump).with(anything, include(header: false))
|
|
332
|
-
SeedDump.dump_using_environment
|
|
333
|
-
end
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
describe 'UPSERT_ALL (issue #104 - non-continuous IDs / foreign key preservation)' do
|
|
337
|
-
# UPSERT_ALL solves the problem where deleted rows cause foreign key
|
|
338
|
-
# references to become invalid after reimporting seeds. When IDs are
|
|
339
|
-
# preserved via upsert_all, foreign key references remain correct.
|
|
340
|
-
|
|
341
|
-
it "should specify upsert_all as true if the UPSERT_ALL env var is 'true'" do
|
|
342
|
-
expect(SeedDump).to receive(:dump).with(anything, include(upsert_all: true))
|
|
343
|
-
SeedDump.dump_using_environment('UPSERT_ALL' => 'true')
|
|
344
|
-
end
|
|
345
|
-
|
|
346
|
-
it "should specify upsert_all as true if the UPSERT_ALL env var is 'TRUE'" do
|
|
347
|
-
expect(SeedDump).to receive(:dump).with(anything, include(upsert_all: true))
|
|
348
|
-
SeedDump.dump_using_environment('UPSERT_ALL' => 'TRUE')
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
it "should specify upsert_all as false if the UPSERT_ALL env var is not 'true'" do
|
|
352
|
-
expect(SeedDump).to receive(:dump).with(anything, include(upsert_all: false))
|
|
353
|
-
SeedDump.dump_using_environment('UPSERT_ALL' => 'false')
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
it "should specify upsert_all as false if the UPSERT_ALL env var is not set" do
|
|
357
|
-
expect(SeedDump).to receive(:dump).with(anything, include(upsert_all: false))
|
|
358
|
-
SeedDump.dump_using_environment
|
|
359
|
-
end
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
it 'should handle non-model classes in ActiveRecord::Base.descendants (issue #112)' do
|
|
363
|
-
# Create a class that inherits from ActiveRecord::Base but doesn't respond to exists?
|
|
364
|
-
# This simulates edge cases like abstract classes or improperly configured models
|
|
365
|
-
non_model_class = Class.new(ActiveRecord::Base) do
|
|
366
|
-
def self.exists?
|
|
367
|
-
raise NoMethodError, "undefined method `exists?' for #{self}"
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
def self.table_exists?
|
|
371
|
-
raise NoMethodError, "undefined method `table_exists?' for #{self}"
|
|
372
|
-
end
|
|
373
|
-
end
|
|
374
|
-
Object.const_set('NonModelClass', non_model_class)
|
|
375
|
-
|
|
376
|
-
allow(SeedDump).to receive(:dump)
|
|
377
|
-
|
|
378
|
-
begin
|
|
379
|
-
expect { SeedDump.dump_using_environment }.not_to raise_error
|
|
380
|
-
ensure
|
|
381
|
-
Object.send(:remove_const, :NonModelClass)
|
|
382
|
-
end
|
|
383
|
-
end
|
|
384
|
-
|
|
385
|
-
it 'should run ok without ActiveRecord::SchemaMigration being set (needed for Rails Engines)' do
|
|
386
|
-
# Ensure Sample model exists before trying to remove SchemaMigration
|
|
387
|
-
expect(defined?(Sample)).to be_truthy
|
|
388
|
-
schema_migration_defined = defined?(ActiveRecord::SchemaMigration)
|
|
389
|
-
schema_migration = ActiveRecord::SchemaMigration if schema_migration_defined
|
|
390
|
-
|
|
391
|
-
# Stub the dump method before removing the constant
|
|
392
|
-
allow(SeedDump).to receive(:dump)
|
|
393
|
-
|
|
394
|
-
# Use remove_const carefully only if it's defined
|
|
395
|
-
ActiveRecord.send(:remove_const, :SchemaMigration) if schema_migration_defined
|
|
396
|
-
|
|
397
|
-
begin
|
|
398
|
-
expect { SeedDump.dump_using_environment }.not_to raise_error
|
|
399
|
-
ensure
|
|
400
|
-
# Ensure the constant is restored only if it was originally defined
|
|
401
|
-
ActiveRecord.const_set(:SchemaMigration, schema_migration) if schema_migration_defined && !defined?(ActiveRecord::SchemaMigration)
|
|
402
|
-
end
|
|
403
|
-
end
|
|
404
|
-
|
|
405
|
-
describe 'HABTM deduplication (issues #26, #114)' do
|
|
406
|
-
# When using has_and_belongs_to_many, Rails creates two auto-generated models
|
|
407
|
-
# that point to the same join table (e.g., User::HABTM_Roles and Role::HABTM_Users).
|
|
408
|
-
# We should only dump one of them to avoid duplicate seed data.
|
|
409
|
-
|
|
410
|
-
it 'should deduplicate HABTM models that share the same table' do
|
|
411
|
-
# Create mock HABTM classes that share the same table_name
|
|
412
|
-
habtm_class_1 = Class.new(ActiveRecord::Base) do
|
|
413
|
-
self.table_name = 'roles_users'
|
|
414
|
-
def self.name; 'User::HABTM_Roles'; end
|
|
415
|
-
def self.to_s; name; end
|
|
416
|
-
end
|
|
417
|
-
|
|
418
|
-
habtm_class_2 = Class.new(ActiveRecord::Base) do
|
|
419
|
-
self.table_name = 'roles_users'
|
|
420
|
-
def self.name; 'Role::HABTM_Users'; end
|
|
421
|
-
def self.to_s; name; end
|
|
422
|
-
end
|
|
423
|
-
|
|
424
|
-
# Temporarily add these to AR descendants by setting constants
|
|
425
|
-
User = Class.new unless defined?(User)
|
|
426
|
-
Role = Class.new unless defined?(Role)
|
|
427
|
-
User.const_set('HABTM_Roles', habtm_class_1)
|
|
428
|
-
Role.const_set('HABTM_Users', habtm_class_2)
|
|
429
|
-
|
|
430
|
-
begin
|
|
431
|
-
# Stub exists? and table_exists? to return true
|
|
432
|
-
allow(habtm_class_1).to receive(:table_exists?).and_return(true)
|
|
433
|
-
allow(habtm_class_1).to receive(:exists?).and_return(true)
|
|
434
|
-
allow(habtm_class_2).to receive(:table_exists?).and_return(true)
|
|
435
|
-
allow(habtm_class_2).to receive(:exists?).and_return(true)
|
|
436
|
-
|
|
437
|
-
# Track which models get dumped
|
|
438
|
-
dumped_models = []
|
|
439
|
-
allow(SeedDump).to receive(:dump) do |model, _options|
|
|
440
|
-
dumped_models << model.to_s
|
|
441
|
-
end
|
|
442
|
-
|
|
443
|
-
SeedDump.dump_using_environment
|
|
444
|
-
|
|
445
|
-
# Only one of the HABTM models should be dumped, not both
|
|
446
|
-
habtm_dumps = dumped_models.select { |m| m.include?('HABTM_') }
|
|
447
|
-
habtm_tables = habtm_dumps.map { |m| m.include?('HABTM_Roles') ? 'roles_users' : 'roles_users' }
|
|
448
|
-
|
|
449
|
-
expect(habtm_dumps.size).to eq(1), "Expected 1 HABTM model to be dumped, got #{habtm_dumps.size}: #{habtm_dumps}"
|
|
450
|
-
ensure
|
|
451
|
-
User.send(:remove_const, 'HABTM_Roles') if defined?(User::HABTM_Roles)
|
|
452
|
-
Role.send(:remove_const, 'HABTM_Users') if defined?(Role::HABTM_Users)
|
|
453
|
-
Object.send(:remove_const, 'User') if defined?(User) && User.is_a?(Class) && User.superclass == Object
|
|
454
|
-
Object.send(:remove_const, 'Role') if defined?(Role) && Role.is_a?(Class) && Role.superclass == Object
|
|
455
|
-
end
|
|
456
|
-
end
|
|
457
|
-
|
|
458
|
-
it 'should not affect non-HABTM models with different tables' do
|
|
459
|
-
# Sample and AnotherSample have different tables, so both should dump
|
|
460
|
-
allow(SeedDump).to receive(:dump)
|
|
461
|
-
|
|
462
|
-
FactoryBot.create(:another_sample)
|
|
463
|
-
expect(SeedDump).to receive(:dump).with(Sample, anything)
|
|
464
|
-
expect(SeedDump).to receive(:dump).with(AnotherSample, anything)
|
|
465
|
-
|
|
466
|
-
SeedDump.dump_using_environment
|
|
467
|
-
end
|
|
468
|
-
end
|
|
469
|
-
|
|
470
|
-
describe 'foreign key dependency ordering (issues #78, #83)' do
|
|
471
|
-
# Models with foreign key dependencies should be dumped in the correct order
|
|
472
|
-
# so that seeds can be imported without foreign key violations.
|
|
473
|
-
# For example: Author -> Book -> Review means Author should be dumped first,
|
|
474
|
-
# then Book, then Review.
|
|
475
|
-
|
|
476
|
-
it 'should order models by foreign key dependencies' do
|
|
477
|
-
# Create records with dependencies
|
|
478
|
-
author = FactoryBot.create(:author)
|
|
479
|
-
book = FactoryBot.create(:book, author: author)
|
|
480
|
-
FactoryBot.create(:review, book: book)
|
|
481
|
-
|
|
482
|
-
# Track which models get dumped and in what order
|
|
483
|
-
dumped_models = []
|
|
484
|
-
allow(SeedDump).to receive(:dump) do |model, _options|
|
|
485
|
-
dumped_models << model.to_s
|
|
486
|
-
end
|
|
487
|
-
|
|
488
|
-
SeedDump.dump_using_environment('MODELS' => 'Review,Book,Author')
|
|
489
|
-
|
|
490
|
-
# Verify the order: Author must come before Book, Book must come before Review
|
|
491
|
-
author_index = dumped_models.index('Author')
|
|
492
|
-
book_index = dumped_models.index('Book')
|
|
493
|
-
review_index = dumped_models.index('Review')
|
|
494
|
-
|
|
495
|
-
expect(author_index).not_to be_nil, "Author should be in the dump"
|
|
496
|
-
expect(book_index).not_to be_nil, "Book should be in the dump"
|
|
497
|
-
expect(review_index).not_to be_nil, "Review should be in the dump"
|
|
498
|
-
|
|
499
|
-
expect(author_index).to be < book_index,
|
|
500
|
-
"Author (index #{author_index}) should be dumped before Book (index #{book_index})"
|
|
501
|
-
expect(book_index).to be < review_index,
|
|
502
|
-
"Book (index #{book_index}) should be dumped before Review (index #{review_index})"
|
|
503
|
-
end
|
|
504
|
-
|
|
505
|
-
it 'should handle models without foreign key dependencies' do
|
|
506
|
-
# Sample has no foreign keys, should still be dumped normally
|
|
507
|
-
FactoryBot.create(:author)
|
|
508
|
-
|
|
509
|
-
dumped_models = []
|
|
510
|
-
allow(SeedDump).to receive(:dump) do |model, _options|
|
|
511
|
-
dumped_models << model.to_s
|
|
512
|
-
end
|
|
513
|
-
|
|
514
|
-
SeedDump.dump_using_environment('MODELS' => 'Sample,Author')
|
|
515
|
-
|
|
516
|
-
expect(dumped_models).to include('Sample')
|
|
517
|
-
expect(dumped_models).to include('Author')
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
it 'should handle circular dependencies gracefully' do
|
|
521
|
-
# Create models with circular dependency for testing
|
|
522
|
-
# PersonA belongs_to PersonB, PersonB belongs_to PersonA
|
|
523
|
-
person_a_class = Class.new(ActiveRecord::Base) do
|
|
524
|
-
self.table_name = 'person_as'
|
|
525
|
-
end
|
|
526
|
-
person_b_class = Class.new(ActiveRecord::Base) do
|
|
527
|
-
self.table_name = 'person_bs'
|
|
528
|
-
end
|
|
529
|
-
Object.const_set('PersonA', person_a_class)
|
|
530
|
-
Object.const_set('PersonB', person_b_class)
|
|
531
|
-
|
|
532
|
-
# Add circular associations after both classes exist
|
|
533
|
-
PersonA.belongs_to :person_b, optional: true
|
|
534
|
-
PersonB.belongs_to :person_a, optional: true
|
|
535
|
-
|
|
536
|
-
# Create tables
|
|
537
|
-
ActiveRecord::Schema.define do
|
|
538
|
-
create_table 'person_as', force: true do |t|
|
|
539
|
-
t.references :person_b
|
|
540
|
-
end
|
|
541
|
-
create_table 'person_bs', force: true do |t|
|
|
542
|
-
t.references :person_a
|
|
543
|
-
end
|
|
544
|
-
end
|
|
545
|
-
|
|
546
|
-
# Create records
|
|
547
|
-
PersonA.create!
|
|
548
|
-
PersonB.create!
|
|
549
|
-
|
|
550
|
-
begin
|
|
551
|
-
dumped_models = []
|
|
552
|
-
allow(SeedDump).to receive(:dump) do |model, _options|
|
|
553
|
-
dumped_models << model.to_s
|
|
554
|
-
end
|
|
555
|
-
|
|
556
|
-
# Should not raise an error despite circular dependency
|
|
557
|
-
expect {
|
|
558
|
-
SeedDump.dump_using_environment('MODELS' => 'PersonA,PersonB')
|
|
559
|
-
}.not_to raise_error
|
|
560
|
-
|
|
561
|
-
# Both models should be dumped
|
|
562
|
-
expect(dumped_models).to include('PersonA')
|
|
563
|
-
expect(dumped_models).to include('PersonB')
|
|
564
|
-
ensure
|
|
565
|
-
Object.send(:remove_const, :PersonA)
|
|
566
|
-
Object.send(:remove_const, :PersonB)
|
|
567
|
-
end
|
|
568
|
-
end
|
|
569
|
-
end
|
|
570
|
-
|
|
571
|
-
describe 'STI deduplication (issue #120)' do
|
|
572
|
-
# When using STI (Single Table Inheritance), multiple model classes share
|
|
573
|
-
# the same database table. For example, AdminUser < BaseUser and
|
|
574
|
-
# GuestUser < BaseUser all use the 'base_users' table.
|
|
575
|
-
# Without deduplication, each STI subclass would be dumped separately,
|
|
576
|
-
# creating duplicate records in the seeds file.
|
|
577
|
-
|
|
578
|
-
it 'should deduplicate STI models by keeping only the base class' do
|
|
579
|
-
# Create records of different STI types
|
|
580
|
-
FactoryBot.create(:admin_user)
|
|
581
|
-
FactoryBot.create(:guest_user)
|
|
582
|
-
|
|
583
|
-
# Track which models get dumped
|
|
584
|
-
dumped_models = []
|
|
585
|
-
allow(SeedDump).to receive(:dump) do |model, _options|
|
|
586
|
-
dumped_models << model.to_s
|
|
587
|
-
end
|
|
588
|
-
|
|
589
|
-
SeedDump.dump_using_environment
|
|
590
|
-
|
|
591
|
-
# Only BaseUser should be dumped, not AdminUser or GuestUser
|
|
592
|
-
expect(dumped_models).to include('BaseUser')
|
|
593
|
-
expect(dumped_models).not_to include('AdminUser')
|
|
594
|
-
expect(dumped_models).not_to include('GuestUser')
|
|
595
|
-
end
|
|
596
|
-
|
|
597
|
-
it 'should dump all records through the base class' do
|
|
598
|
-
# Create records of different STI types
|
|
599
|
-
FactoryBot.create(:admin_user)
|
|
600
|
-
FactoryBot.create(:guest_user)
|
|
601
|
-
FactoryBot.create(:base_user)
|
|
602
|
-
|
|
603
|
-
# The base class should have access to all records
|
|
604
|
-
result = SeedDump.dump(BaseUser)
|
|
605
|
-
|
|
606
|
-
# All three records should be in the output
|
|
607
|
-
expect(result).to include('AdminUser')
|
|
608
|
-
expect(result).to include('GuestUser')
|
|
609
|
-
expect(result).to include('BaseUser')
|
|
610
|
-
end
|
|
611
|
-
end
|
|
612
|
-
end
|
|
613
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
FactoryBot.define do
|
|
2
|
-
factory :another_sample do
|
|
3
|
-
# Use block syntax for attribute definitions
|
|
4
|
-
string { 'string' }
|
|
5
|
-
text { 'text' }
|
|
6
|
-
integer { 42 }
|
|
7
|
-
float { 3.14 }
|
|
8
|
-
# BigDecimal needs to be initialized within the block
|
|
9
|
-
decimal { BigDecimal('2.72') }
|
|
10
|
-
# Use standard Ruby DateTime/Time parse methods directly
|
|
11
|
-
datetime { DateTime.parse('July 4, 1776 7:14pm UTC') }
|
|
12
|
-
time { Time.parse('3:15am UTC') }
|
|
13
|
-
date { Date.parse('November 19, 1863') }
|
|
14
|
-
binary { 'binary' }
|
|
15
|
-
boolean { false }
|
|
16
|
-
# created_at and updated_at are typically handled by ActiveRecord timestamps
|
|
17
|
-
# but can be defined explicitly if needed for specific tests.
|
|
18
|
-
# created_at { Time.now }
|
|
19
|
-
# updated_at { Time.now }
|
|
20
|
-
end
|
|
21
|
-
end
|
data/spec/factories/authors.rb
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
FactoryBot.define do
|
|
2
|
-
factory :base_user do
|
|
3
|
-
name { 'Test User' }
|
|
4
|
-
email { 'user@example.com' }
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
factory :admin_user do
|
|
8
|
-
name { 'Admin User' }
|
|
9
|
-
email { 'admin@example.com' }
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
factory :guest_user do
|
|
13
|
-
name { 'Guest User' }
|
|
14
|
-
email { 'guest@example.com' }
|
|
15
|
-
end
|
|
16
|
-
end
|
data/spec/factories/books.rb
DELETED
data/spec/factories/bosses.rb
DELETED