carrierwave-mongoid 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +45 -0
- data/README.md +2 -3
- data/carrierwave-mongoid.gemspec +3 -3
- data/gemfiles/carrierwave-1.1.gemfile +1 -0
- data/gemfiles/carrierwave-1.2.gemfile +1 -0
- data/gemfiles/carrierwave-1.3.gemfile +1 -0
- data/gemfiles/carrierwave-2.0.gemfile +2 -1
- data/gemfiles/carrierwave-2.1.gemfile +6 -0
- data/gemfiles/carrierwave-2.2.gemfile +6 -0
- data/gemfiles/mongoid-7.gemfile +1 -1
- data/gemfiles/mongoid-8.gemfile +6 -0
- data/lib/carrierwave/mongoid/version.rb +1 -1
- data/lib/carrierwave/mongoid.rb +104 -5
- data/spec/carrierwave/mongoid/mount_uploaders_spec.rb +1037 -0
- data/spec/mongoid_spec.rb +66 -55
- data/spec/spec_helper.rb +7 -0
- metadata +20 -15
- data/.travis.yml +0 -30
@@ -0,0 +1,1037 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe CarrierWave::Mongoid do
|
6
|
+
if Gem::Version.new(CarrierWave::VERSION) >= Gem::Version.new('1.0.beta')
|
7
|
+
describe '.mount_uploaders' do
|
8
|
+
let(:uploader_class) do
|
9
|
+
Class.new(CarrierWave::Uploader::Base)
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:model_class) do
|
13
|
+
uploader = uploader_class
|
14
|
+
|
15
|
+
Class.new do
|
16
|
+
include Mongoid::Document
|
17
|
+
|
18
|
+
store_in collection: :token_models
|
19
|
+
|
20
|
+
field :name
|
21
|
+
mount_uploaders :images, uploader
|
22
|
+
|
23
|
+
def self.model_name
|
24
|
+
ActiveModel::Name.new(self, nil, 'TokenModel')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
let!(:model) { model_class.new }
|
30
|
+
let(:record) { model_class.first }
|
31
|
+
|
32
|
+
after do
|
33
|
+
model_class.collection.drop
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'model#destroy' do
|
37
|
+
let(:identifiers) { ['portrait.jpg', 'test.jpeg'] }
|
38
|
+
let(:files) { identifiers.map { |i| stub_file(i) } }
|
39
|
+
let(:current_paths) { identifiers.map { |i| public_path("uploads/#{i}") } }
|
40
|
+
let(:current_pathnames) { current_paths.map { |p| Pathname.new(p) } }
|
41
|
+
|
42
|
+
describe 'when file assigned' do
|
43
|
+
it 'removes the file from the filesystem' do
|
44
|
+
model.images = files
|
45
|
+
expect(model.save).to be_truthy
|
46
|
+
|
47
|
+
expect(model.images.count).to eq files.count
|
48
|
+
expect(model.images).to all(be_an_instance_of(uploader_class))
|
49
|
+
expect(model.images.map(&:current_path)).to match_array(current_paths)
|
50
|
+
expect(current_pathnames).to all(be_file)
|
51
|
+
|
52
|
+
model.destroy!
|
53
|
+
|
54
|
+
expect(current_pathnames.map(&:exist?)).to all(be_falsey)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'when file is not assigned' do
|
59
|
+
before do
|
60
|
+
model.save!
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'deletes the instance of model_class after save' do
|
64
|
+
expect { model.destroy }.to change(model_class, :count).from(1).to(0)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'deletes the instance of model_class after save and then re-looking up the instance' do
|
68
|
+
expect { record.destroy }.to change(model_class, :count).from(1).to(0)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'model#save' do
|
74
|
+
let(:identifiers) { ['portrait.jpg', 'test.jpeg'] }
|
75
|
+
let(:files) { identifiers.map { |i| stub_file(i) } }
|
76
|
+
let(:current_paths) { identifiers.map { |i| public_path("uploads/#{i}") } }
|
77
|
+
|
78
|
+
it 'after it was initialized with params' do
|
79
|
+
model = model_class.new(images: files)
|
80
|
+
|
81
|
+
expect(model.save).to be_truthy
|
82
|
+
expect(model.images.count).to eq files.count
|
83
|
+
expect(model.images).to all(be_an_instance_of(uploader_class))
|
84
|
+
expect(model.images.map(&:current_path)).to match_array(current_paths)
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'when no file is assigned' do
|
88
|
+
it 'image is blank' do
|
89
|
+
expect(model.save).to be_truthy
|
90
|
+
expect(model.images).to be_blank
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'when a file is assigned' do
|
95
|
+
it 'copies the file to the upload directory' do
|
96
|
+
model.images = files
|
97
|
+
|
98
|
+
expect(model.save).to be_truthy
|
99
|
+
expect(model.images.count).to eq files.count
|
100
|
+
expect(model.images).to all(be_an_instance_of(uploader_class))
|
101
|
+
expect(model.images.map(&:current_path)).to match_array(current_paths)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'saves the filename in the database' do
|
105
|
+
model.images = files
|
106
|
+
|
107
|
+
expect(model.save).to be_truthy
|
108
|
+
expect(model[:images]).to match_array(identifiers)
|
109
|
+
expect(model.images_identifiers).to match_array(identifiers)
|
110
|
+
end
|
111
|
+
|
112
|
+
context 'when remove_images? is true' do
|
113
|
+
it 'removes the image' do
|
114
|
+
model.images = stub_file('test.jpeg')
|
115
|
+
model.save
|
116
|
+
model.remove_images = true
|
117
|
+
expect(model.save).to be_truthy
|
118
|
+
model.reload
|
119
|
+
expect(model.images).to be_blank
|
120
|
+
expect(model.images_identifiers).to be_blank
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'marks images as changed when saving a new image' do
|
125
|
+
model.save
|
126
|
+
expect(model.images_changed?).to be false
|
127
|
+
|
128
|
+
model.images = files
|
129
|
+
expect(model.images_changed?).to be true
|
130
|
+
|
131
|
+
model.save
|
132
|
+
model.reload
|
133
|
+
expect(model.images_changed?).to be false
|
134
|
+
|
135
|
+
model.images = files
|
136
|
+
expect(model.images_changed?).to be true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
describe 'model#remove_uploaders=' do
|
142
|
+
before do
|
143
|
+
model.save
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'treats true argument such that attribute is marked as changed' do
|
147
|
+
model.remove_images = true
|
148
|
+
|
149
|
+
expect(model.images_changed?).to be true
|
150
|
+
end
|
151
|
+
|
152
|
+
it "treats '1' argument such that attribute is marked as changed" do
|
153
|
+
model.remove_images = '1'
|
154
|
+
|
155
|
+
expect(model.images_changed?).to be true
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'treats false argument such that attribute is not marked as changed' do
|
159
|
+
model.remove_images = false
|
160
|
+
|
161
|
+
expect(model.images_changed?).to be false
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'treats nil argument such that attribute is not marked as changed' do
|
165
|
+
model.remove_images = nil
|
166
|
+
|
167
|
+
expect(model.images_changed?).to be false
|
168
|
+
end
|
169
|
+
|
170
|
+
it "treats '0' argument such that attribute is not marked as changed" do
|
171
|
+
model.remove_images = '0'
|
172
|
+
|
173
|
+
expect(model.images_changed?).to be false
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe 'model#uploaders' do
|
178
|
+
context 'when nothing was assigned yet' do
|
179
|
+
it 'returns an empty array' do
|
180
|
+
expect(model.images).to match_array([])
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
context 'when assigning an empty array' do
|
185
|
+
before do
|
186
|
+
model.images = []
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'returns an empty array' do
|
190
|
+
expect(model.images).to match_array([])
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'when saving and reloading' do
|
194
|
+
before do
|
195
|
+
model.save
|
196
|
+
model.reload
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'returns an empty array' do
|
200
|
+
expect(model.images).to match_array([])
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context 'when assigning values' do
|
206
|
+
context 'without using the model, i.e. writing filenames directly to the database record' do
|
207
|
+
let(:identifiers) { ['test1.jpg', 'test2.jpg'] }
|
208
|
+
|
209
|
+
before do
|
210
|
+
model.save!
|
211
|
+
model.collection.update_one({ _id: model.id }, { images: identifiers })
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'returns an array of uploaders' do
|
215
|
+
expect(model_class.first.images).to all(be_an_instance_of(uploader_class))
|
216
|
+
end
|
217
|
+
|
218
|
+
describe 'the returned uploaders' do
|
219
|
+
it 'have the matching identifiers', if: Gem::Version.new(CarrierWave::VERSION) >= Gem::Version.new('2') do
|
220
|
+
expect(model_class.first.images.map(&:identifier)).to match_array(identifiers)
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'have their paths set to the store directory' do
|
224
|
+
expect(model_class.first.images.map(&:current_path)).to match_array(identifiers.map { |i| public_path("uploads/#{i}") })
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
context 'when using the methods on the model' do
|
230
|
+
context 'when there are no uploaders assigned yet' do
|
231
|
+
let(:identifiers) { ['test.jpeg'] }
|
232
|
+
|
233
|
+
before do
|
234
|
+
model.images = identifiers.map { |f| stub_file(f) }
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'caches a file' do
|
238
|
+
expect(model.images).to all(be_an_instance_of(uploader_class))
|
239
|
+
expect(model.images.map(&:identifier)).to match_array(identifiers)
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'does not write anything to the database, in order to prevent overridden filenames to fail because of unassigned attributes' do
|
243
|
+
expect(model[:images]).to match_array([])
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'copies a file into into the cache directory' do
|
247
|
+
expect(model.images.first.current_path).to match(/^#{Regexp.escape(public_path('uploads/tmp'))}/)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
context 'when there are already uploaders assigned' do
|
252
|
+
let!(:model) { model_class.create(images: [stub_file('portrait.jpg')]) }
|
253
|
+
|
254
|
+
before do
|
255
|
+
model.images = model.images.push(stub_file('test.jpeg'))
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'caches the file' do
|
259
|
+
expect(model.images).to all(be_an_instance_of(uploader_class))
|
260
|
+
expect(model.images.map(&:identifier)).to match_array(['portrait.jpg', 'test.jpeg'])
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'does not write anything to the database, in order to prevent overridden filenames to fail because of unassigned attributes' do
|
264
|
+
expect(model[:images]).to match_array(['portrait.jpg'])
|
265
|
+
end
|
266
|
+
|
267
|
+
it 'copies a file into into the cache directory' do
|
268
|
+
expect(model.images.map(&:current_path)).to all(match(/^#{Regexp.escape(public_path('uploads/tmp'))}/))
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
describe 'model#uploaders=' do
|
276
|
+
context 'when nil is assigned' do
|
277
|
+
it 'does not set the value' do
|
278
|
+
model.images = nil
|
279
|
+
|
280
|
+
expect(model.images).to be_blank
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
context 'when an empty string is assigned' do
|
285
|
+
it 'does not set the value' do
|
286
|
+
model.images = ''
|
287
|
+
|
288
|
+
expect(model.images).to be_blank
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
context 'when assigning files' do
|
293
|
+
let(:files) { [stub_file('portrait.jpg'), stub_file('test.jpeg')] }
|
294
|
+
|
295
|
+
before do
|
296
|
+
model.images = files
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'caches the files' do
|
300
|
+
expect(model.images.count).to be files.count
|
301
|
+
expect(model.images).to all(be_an_instance_of(uploader_class))
|
302
|
+
end
|
303
|
+
|
304
|
+
it 'does not write to the database' do
|
305
|
+
expect(model[:images]).to be_empty
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'copies a file into into the cache directory' do
|
309
|
+
expect(model.images.map(&:current_path)).to all(start_with(public_path('uploads/tmp')))
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
context 'when validating integrity' do
|
314
|
+
let(:uploader_class) do
|
315
|
+
Class.new(CarrierWave::Uploader::Base) do
|
316
|
+
process :munge
|
317
|
+
|
318
|
+
def munge
|
319
|
+
raise CarrierWave::IntegrityError
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
let(:files) { [stub_file('portrait.jpg')] }
|
325
|
+
|
326
|
+
before do
|
327
|
+
model.images = files
|
328
|
+
end
|
329
|
+
|
330
|
+
it 'makes the document invalid when an integrity error occurs' do
|
331
|
+
expect(model).to be_invalid
|
332
|
+
end
|
333
|
+
|
334
|
+
it 'uses I18n for integrity error messages' do
|
335
|
+
translations = { mongoid: { errors: { messages: { carrierwave_integrity_error: 'is not of an allowed file type' } } } }
|
336
|
+
change_locale_and_store_translations(:en, translations) do
|
337
|
+
model.valid?
|
338
|
+
|
339
|
+
expect(model.errors[:images]).to eq ['is not of an allowed file type']
|
340
|
+
end
|
341
|
+
|
342
|
+
translations = { mongoid: { errors: { messages: { carrierwave_integrity_error: 'tipo de imagem não permitido.' } } } }
|
343
|
+
change_locale_and_store_translations(:pt, translations) do
|
344
|
+
model.valid?
|
345
|
+
|
346
|
+
expect(model.errors[:images]).to eq ['tipo de imagem não permitido.']
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
context 'when validating processing' do
|
352
|
+
let(:uploader_class) do
|
353
|
+
Class.new(CarrierWave::Uploader::Base) do
|
354
|
+
process :munge
|
355
|
+
|
356
|
+
def munge
|
357
|
+
raise CarrierWave::ProcessingError
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
let(:files) { [stub_file('portrait.jpg')] }
|
363
|
+
|
364
|
+
before do
|
365
|
+
model.images = files
|
366
|
+
end
|
367
|
+
|
368
|
+
it 'makes the document invalid when a processing error occurs' do
|
369
|
+
expect(model).not_to be_valid
|
370
|
+
end
|
371
|
+
|
372
|
+
it 'uses I18n for processing error messages' do
|
373
|
+
translations = { mongoid: { errors: { messages: { carrierwave_processing_error: 'failed to be processed' } } } }
|
374
|
+
change_locale_and_store_translations(:en, translations) do
|
375
|
+
model.valid?
|
376
|
+
expect(model.errors[:images]).to eq ['failed to be processed']
|
377
|
+
end
|
378
|
+
|
379
|
+
translations = { mongoid: { errors: { messages: { carrierwave_processing_error: 'falha ao processar imagem.' } } } }
|
380
|
+
change_locale_and_store_translations(:pt, translations) do
|
381
|
+
model.valid?
|
382
|
+
expect(model.errors[:images]).to eq ['falha ao processar imagem.']
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
describe 'model#update' do
|
389
|
+
let(:identifiers) { ['portrait.jpg', 'test.jpeg'] }
|
390
|
+
let(:files) { identifiers.map { |i| stub_file(i) } }
|
391
|
+
let(:current_paths) { identifiers.map { |i| public_path("uploads/#{i}") } }
|
392
|
+
let(:current_pathnames) { current_paths.map { |p| Pathname.new(p) } }
|
393
|
+
|
394
|
+
before do
|
395
|
+
model_class.create!(images: files)
|
396
|
+
end
|
397
|
+
|
398
|
+
it 'replaced it by a file with the same name' do
|
399
|
+
record.update!(images: [stub_file('test.jpeg')])
|
400
|
+
|
401
|
+
record.reload
|
402
|
+
|
403
|
+
expect(record[:images]).to match_array(['test.jpeg'])
|
404
|
+
expect(record.images_identifiers).to match_array(['test.jpeg'])
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
describe 'model#to_json' do
|
409
|
+
let(:json) { JSON.parse(record.to_json) }
|
410
|
+
|
411
|
+
context 'when assigning values' do
|
412
|
+
context 'without using the model, i.e. writing filenames directly to the database record' do
|
413
|
+
before do
|
414
|
+
model[:images] = identifiers
|
415
|
+
model.save!
|
416
|
+
end
|
417
|
+
|
418
|
+
context 'when the identifiers are blank' do
|
419
|
+
let(:identifiers) { nil }
|
420
|
+
|
421
|
+
it 'returns valid JSON' do
|
422
|
+
expect(json['images']).to match_array([])
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
context 'when the identifiers are present' do
|
427
|
+
let(:identifiers) { ['portrait.jpg', 'test.jpeg'] }
|
428
|
+
|
429
|
+
it 'returns valid JSON' do
|
430
|
+
expected = identifiers.map do |i|
|
431
|
+
{ 'url' => "/uploads/#{i}" }
|
432
|
+
end
|
433
|
+
|
434
|
+
expect(json['images']).to match_array(expected)
|
435
|
+
end
|
436
|
+
|
437
|
+
it 'returns valid JSON when called on a collection containing uploaders from the model' do
|
438
|
+
plaintext = { data: record.images }.to_json
|
439
|
+
|
440
|
+
expected = identifiers.map do |i|
|
441
|
+
{ 'url' => "/uploads/#{i}" }
|
442
|
+
end
|
443
|
+
|
444
|
+
expect(JSON.parse(plaintext)).to eq('data' => expected)
|
445
|
+
end
|
446
|
+
|
447
|
+
it 'returns valid JSON when using :only' do
|
448
|
+
plaintext = record.to_json(only: [:_id])
|
449
|
+
|
450
|
+
expect(JSON.parse(plaintext)).to eq('_id' => record.id.as_json)
|
451
|
+
end
|
452
|
+
|
453
|
+
it 'returns valid JSON when using :except' do
|
454
|
+
plaintext = record.to_json(except: %i[_id images])
|
455
|
+
|
456
|
+
expect(JSON.parse(plaintext)).to eq('name' => nil)
|
457
|
+
end
|
458
|
+
end
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
describe 'removing old files' do
|
464
|
+
let(:identifiers) { ['old.jpeg'] }
|
465
|
+
let(:files) { identifiers.map { |i| stub_file(i) } }
|
466
|
+
let(:current_paths) { identifiers.map { |i| public_path("uploads/#{i}") } }
|
467
|
+
let(:current_pathnames) { current_paths.map { |p| Pathname.new(p) } }
|
468
|
+
|
469
|
+
let!(:model) { model_class.create!(images: files) }
|
470
|
+
|
471
|
+
after do
|
472
|
+
FileUtils.rm_rf(public_path('uploads'))
|
473
|
+
end
|
474
|
+
|
475
|
+
describe 'before removing' do
|
476
|
+
it 'all files exist' do
|
477
|
+
expect(current_pathnames).to all(be_file)
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
describe 'normally' do
|
482
|
+
it 'removes old file if old file had a different path' do
|
483
|
+
model.images = [stub_file('new.jpeg')]
|
484
|
+
expect(model.save).to be_truthy
|
485
|
+
expect(File).to exist(public_path('uploads/new.jpeg'))
|
486
|
+
expect(File).not_to exist(public_path('uploads/old.jpeg'))
|
487
|
+
end
|
488
|
+
|
489
|
+
it 'does not remove old file if old file had a different path but config is false' do
|
490
|
+
uploader_class.remove_previously_stored_files_after_update = false
|
491
|
+
model.images = [stub_file('new.jpeg')]
|
492
|
+
expect(model.save).to be_truthy
|
493
|
+
expect(File).to exist(public_path('uploads/new.jpeg'))
|
494
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
495
|
+
end
|
496
|
+
|
497
|
+
it 'does not remove file if old file had the same path' do
|
498
|
+
model.images = [stub_file('old.jpeg')]
|
499
|
+
expect(model.save).to be_truthy
|
500
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
501
|
+
end
|
502
|
+
|
503
|
+
it 'does not remove file if validations fail on save' do
|
504
|
+
model_class.validate { |r| r.errors.add :textfile, 'FAIL!' }
|
505
|
+
model.images = [stub_file('new.jpeg')]
|
506
|
+
expect(model.save).to be_falsey
|
507
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
describe 'with an overridden filename' do
|
512
|
+
let(:uploader_class) do
|
513
|
+
Class.new(CarrierWave::Uploader::Base) do
|
514
|
+
def filename
|
515
|
+
model.name + File.extname(super)
|
516
|
+
end
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
let!(:model) { model_class.create!(name: 'Mike', images: [stub_file('old.jpeg')]) }
|
521
|
+
|
522
|
+
it 'does not remove file if old file had the same dynamic path' do
|
523
|
+
expect(File).to exist(public_path('uploads/Mike.jpeg'))
|
524
|
+
expect(model.images.first.read).to eq 'this is stuff'
|
525
|
+
|
526
|
+
model.update!(images: [stub_file('test.jpeg')])
|
527
|
+
|
528
|
+
expect(File).to exist(public_path('uploads/Mike.jpeg'))
|
529
|
+
end
|
530
|
+
|
531
|
+
it 'removes old file if old file had a different dynamic path' do
|
532
|
+
expect(File).to exist(public_path('uploads/Mike.jpeg'))
|
533
|
+
expect(model.images.first.read).to eq 'this is stuff'
|
534
|
+
|
535
|
+
model.update!(name: 'Frank', images: [stub_file('test.jpeg')])
|
536
|
+
|
537
|
+
expect(File).to exist(public_path('uploads/Frank.jpeg'))
|
538
|
+
expect(File).not_to exist(public_path('uploads/test.jpeg'))
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
shared_examples 'embedded documents' do
|
543
|
+
it 'removes old file if old file had a different path' do
|
544
|
+
embedded_model.images = [stub_file('new.jpeg')]
|
545
|
+
expect(embedded_model.save).to be_truthy
|
546
|
+
expect(File).to exist(public_path('uploads/new.jpeg'))
|
547
|
+
expect(File).not_to exist(public_path('uploads/old.jpeg'))
|
548
|
+
end
|
549
|
+
|
550
|
+
it 'does not remove old file if old file had a different path but config is false' do
|
551
|
+
uploader_class.remove_previously_stored_files_after_update = false
|
552
|
+
embedded_model.images = [stub_file('new.jpeg')]
|
553
|
+
expect(embedded_model.save).to be_truthy
|
554
|
+
expect(File).to exist(public_path('uploads/new.jpeg'))
|
555
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
556
|
+
end
|
557
|
+
|
558
|
+
it 'does not remove file if old file had the same path' do
|
559
|
+
embedded_model.images = [stub_file('old.jpeg')]
|
560
|
+
expect(embedded_model.save).to be_truthy
|
561
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
562
|
+
end
|
563
|
+
|
564
|
+
it 'does not remove file if validations fail on save' do
|
565
|
+
embedded_model.class.validate { |r| r.errors.add :textfile, 'FAIL!' }
|
566
|
+
embedded_model.images = [stub_file('new.jpeg')]
|
567
|
+
expect(embedded_model.save).to be_falsey
|
568
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
569
|
+
end
|
570
|
+
|
571
|
+
it "does not touch parent's dirty attributes" do
|
572
|
+
model.name = 'Kirk'
|
573
|
+
embedded_model.images = [stub_file('new.jpeg')]
|
574
|
+
embedded_model.save!
|
575
|
+
|
576
|
+
expect(embedded_model.save).to be_truthy
|
577
|
+
expect(model.name).to eq 'Kirk'
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
shared_examples 'double embedded documents' do
|
582
|
+
it 'removes old file if old file had a different path' do
|
583
|
+
double_embedded_model.images = [stub_file('new.jpeg')]
|
584
|
+
expect(double_embedded_model.save).to be_truthy
|
585
|
+
expect(File).to exist(public_path('uploads/new.jpeg'))
|
586
|
+
expect(File).not_to exist(public_path('uploads/old.jpeg'))
|
587
|
+
end
|
588
|
+
|
589
|
+
it 'does not remove old file if old file had a different path but config is false' do
|
590
|
+
uploader_class.remove_previously_stored_files_after_update = false
|
591
|
+
double_embedded_model.images = [stub_file('new.jpeg')]
|
592
|
+
expect(double_embedded_model.save).to be_truthy
|
593
|
+
expect(File).to exist(public_path('uploads/new.jpeg'))
|
594
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
595
|
+
end
|
596
|
+
|
597
|
+
it 'does not remove file if old file had the same path' do
|
598
|
+
double_embedded_model.images = [stub_file('old.jpeg')]
|
599
|
+
expect(double_embedded_model.save).to be_truthy
|
600
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
601
|
+
end
|
602
|
+
|
603
|
+
it 'does not remove file if validations fail on save' do
|
604
|
+
double_embedded_model_class.validate { |r| r.errors.add :textfile, 'FAIL!' }
|
605
|
+
double_embedded_model.images = [stub_file('new.jpeg')]
|
606
|
+
expect(double_embedded_model.save).to be_falsey
|
607
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
describe 'with document embedded as embeds_one' do
|
612
|
+
let!(:model_class) do
|
613
|
+
define_mongo_class('TokenModel') do
|
614
|
+
include Mongoid::Document
|
615
|
+
|
616
|
+
store_in collection: :token_models
|
617
|
+
|
618
|
+
field :name
|
619
|
+
|
620
|
+
embeds_one :token_embedded_model
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
let!(:embedded_model_class) do
|
625
|
+
uploader = uploader_class
|
626
|
+
|
627
|
+
define_mongo_class('TokenEmbeddedModel') do
|
628
|
+
include Mongoid::Document
|
629
|
+
|
630
|
+
embedded_in :token_model
|
631
|
+
|
632
|
+
field :title
|
633
|
+
mount_uploaders :images, uploader
|
634
|
+
end
|
635
|
+
end
|
636
|
+
|
637
|
+
let(:model) { model_class.new }
|
638
|
+
let!(:embedded_model) { model.create_token_embedded_model(images: [stub_file('old.jpeg')]) }
|
639
|
+
|
640
|
+
include_examples 'embedded documents'
|
641
|
+
end
|
642
|
+
|
643
|
+
describe 'with document embedded as embeds_one and parent document not matched the default scope' do
|
644
|
+
let!(:model_class) do
|
645
|
+
define_mongo_class('TokenModel') do
|
646
|
+
include Mongoid::Document
|
647
|
+
|
648
|
+
store_in collection: :token_models
|
649
|
+
|
650
|
+
field :name
|
651
|
+
|
652
|
+
embeds_one :token_embedded_model
|
653
|
+
|
654
|
+
default_scope -> { where(always_false: false) }
|
655
|
+
end
|
656
|
+
end
|
657
|
+
|
658
|
+
let!(:embedded_model_class) do
|
659
|
+
uploader = uploader_class
|
660
|
+
|
661
|
+
define_mongo_class('TokenEmbeddedModel') do
|
662
|
+
include Mongoid::Document
|
663
|
+
|
664
|
+
embedded_in :token_model
|
665
|
+
|
666
|
+
field :title
|
667
|
+
mount_uploaders :images, uploader
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
let(:model) { model_class.new }
|
672
|
+
let!(:embedded_model) { model.create_token_embedded_model(images: [stub_file('old.jpeg')]) }
|
673
|
+
|
674
|
+
include_examples 'embedded documents'
|
675
|
+
end
|
676
|
+
|
677
|
+
describe 'with embedded documents' do
|
678
|
+
let(:model_class) do
|
679
|
+
embedded_model_class # Invoke class definition
|
680
|
+
|
681
|
+
define_mongo_class('TokenModel') do
|
682
|
+
include Mongoid::Document
|
683
|
+
|
684
|
+
store_in collection: :token_models
|
685
|
+
|
686
|
+
field :name
|
687
|
+
|
688
|
+
embeds_many :token_embedded_models, cascade_callbacks: true
|
689
|
+
accepts_nested_attributes_for :token_embedded_models
|
690
|
+
end
|
691
|
+
end
|
692
|
+
|
693
|
+
let(:embedded_model_class) do
|
694
|
+
double_embedded_model_class # Invoke class definition
|
695
|
+
uploader = uploader_class
|
696
|
+
|
697
|
+
define_mongo_class('TokenEmbeddedModel') do
|
698
|
+
include Mongoid::Document
|
699
|
+
|
700
|
+
embedded_in :token_model
|
701
|
+
embeds_many :token_double_embedded_models
|
702
|
+
|
703
|
+
field :title
|
704
|
+
mount_uploaders :images, uploader
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
let(:double_embedded_model_class) do
|
709
|
+
uploader = uploader_class
|
710
|
+
|
711
|
+
define_mongo_class('TokenDoubleEmbeddedModel') do
|
712
|
+
include Mongoid::Document
|
713
|
+
|
714
|
+
embedded_in :token_embedded_model
|
715
|
+
|
716
|
+
mount_uploaders :images, uploader
|
717
|
+
end
|
718
|
+
end
|
719
|
+
|
720
|
+
let(:model) { model_class.create! }
|
721
|
+
let!(:embedded_model) { model.token_embedded_models.create!(images: [stub_file('old.jpeg')]) }
|
722
|
+
|
723
|
+
include_examples 'embedded documents'
|
724
|
+
|
725
|
+
it 'attaches a new file to an existing document that had no file at first' do
|
726
|
+
model.save!
|
727
|
+
model.reload
|
728
|
+
|
729
|
+
model.token_embedded_models.first.update!(images: [stub_file('test.jpeg')])
|
730
|
+
model.reload
|
731
|
+
|
732
|
+
expect(model.token_embedded_models.first[:images]).to match_array ['test.jpeg']
|
733
|
+
end
|
734
|
+
|
735
|
+
it 'changes the file' do
|
736
|
+
model.update_attributes token_embedded_models_attributes: { '0' => { _id: embedded_model._id, images: [stub_file('test.jpeg')] } }
|
737
|
+
model.reload
|
738
|
+
expect(model.token_embedded_models.first[:images]).to eq ['test.jpeg']
|
739
|
+
end
|
740
|
+
|
741
|
+
it 'removes a file' do
|
742
|
+
model.update_attributes token_embedded_models_attributes: { '0' => { _id: embedded_model._id, remove_images: '1' } }
|
743
|
+
model.reload
|
744
|
+
expect(model.token_embedded_models.first[:images]).not_to be_present
|
745
|
+
end
|
746
|
+
|
747
|
+
describe 'with double embedded documents' do
|
748
|
+
let!(:double_embedded_model) { embedded_model.token_double_embedded_models.create!(images: [stub_file('old.jpeg')]) }
|
749
|
+
|
750
|
+
include_examples 'double embedded documents'
|
751
|
+
end
|
752
|
+
end
|
753
|
+
|
754
|
+
describe 'with embedded documents and parent document not matched the default scope' do
|
755
|
+
let(:model_class) do
|
756
|
+
embedded_model_class # Invoke class definition
|
757
|
+
|
758
|
+
define_mongo_class('TokenModel') do
|
759
|
+
include Mongoid::Document
|
760
|
+
|
761
|
+
store_in collection: :token_models
|
762
|
+
|
763
|
+
field :name
|
764
|
+
|
765
|
+
embeds_many :token_embedded_models
|
766
|
+
|
767
|
+
default_scope -> { where(always_false: false) }
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
let(:embedded_model_class) do
|
772
|
+
double_embedded_model_class # Invoke class definition
|
773
|
+
uploader = uploader_class
|
774
|
+
|
775
|
+
define_mongo_class('TokenEmbeddedModel') do
|
776
|
+
include Mongoid::Document
|
777
|
+
|
778
|
+
embedded_in :token_model
|
779
|
+
embeds_many :token_double_embedded_models
|
780
|
+
|
781
|
+
field :title
|
782
|
+
mount_uploaders :images, uploader
|
783
|
+
end
|
784
|
+
end
|
785
|
+
|
786
|
+
let(:double_embedded_model_class) do
|
787
|
+
uploader = uploader_class
|
788
|
+
|
789
|
+
define_mongo_class('TokenDoubleEmbeddedModel') do
|
790
|
+
include Mongoid::Document
|
791
|
+
|
792
|
+
embedded_in :token_embedded_model
|
793
|
+
|
794
|
+
mount_uploaders :images, uploader
|
795
|
+
end
|
796
|
+
end
|
797
|
+
|
798
|
+
let(:model) { model_class.create! }
|
799
|
+
let!(:embedded_model) { model.token_embedded_models.create!(images: [stub_file('old.jpeg')]) }
|
800
|
+
|
801
|
+
include_examples 'embedded documents'
|
802
|
+
|
803
|
+
describe 'with double embedded documents' do
|
804
|
+
let!(:double_embedded_model) { embedded_model.token_double_embedded_models.create!(images: [stub_file('old.jpeg')]) }
|
805
|
+
|
806
|
+
include_examples 'double embedded documents'
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
describe 'with embedded documents and nested attributes' do
|
811
|
+
let(:model_class) do
|
812
|
+
embedded_model_class # Invoke class definition
|
813
|
+
|
814
|
+
define_mongo_class('TokenModel') do
|
815
|
+
include Mongoid::Document
|
816
|
+
|
817
|
+
store_in collection: :token_models
|
818
|
+
|
819
|
+
field :name
|
820
|
+
|
821
|
+
embeds_many :token_embedded_models, cascade_callbacks: true
|
822
|
+
accepts_nested_attributes_for :token_embedded_models
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
let(:embedded_model_class) do
|
827
|
+
uploader = uploader_class
|
828
|
+
|
829
|
+
define_mongo_class('TokenEmbeddedModel') do
|
830
|
+
include Mongoid::Document
|
831
|
+
|
832
|
+
embedded_in :token_model
|
833
|
+
|
834
|
+
field :title
|
835
|
+
mount_uploaders :images, uploader
|
836
|
+
end
|
837
|
+
end
|
838
|
+
|
839
|
+
let(:model) { model_class.create! }
|
840
|
+
let!(:embedded_model) { model.token_embedded_models.create!(images: [stub_file('old.jpeg')]) }
|
841
|
+
|
842
|
+
it 'sets the image on a save' do
|
843
|
+
model.reload
|
844
|
+
expect(model.token_embedded_models.first.images.first.path).to match(/old\.jpeg$/)
|
845
|
+
expect(embedded_model.images.first.path).to match(/old\.jpeg$/)
|
846
|
+
end
|
847
|
+
|
848
|
+
it 'updates the image on update_attributes' do
|
849
|
+
expect(model.update_attributes(token_embedded_models_attributes: [{ id: embedded_model.id, images: [stub_file('new.jpeg')] }])).to be_truthy
|
850
|
+
model.reload
|
851
|
+
expect(model.token_embedded_models.first.images.first.path).to match(/new\.jpeg$/)
|
852
|
+
expect(embedded_model.reload.images.first.path).to match(/new\.jpeg$/)
|
853
|
+
end
|
854
|
+
end
|
855
|
+
|
856
|
+
context 'with versions' do
|
857
|
+
let(:uploader_class) do
|
858
|
+
Class.new(CarrierWave::Uploader::Base) do
|
859
|
+
version :thumb
|
860
|
+
end
|
861
|
+
end
|
862
|
+
|
863
|
+
let!(:model) { model_class.create!(images: [stub_file('old.jpeg')]) }
|
864
|
+
|
865
|
+
after do
|
866
|
+
FileUtils.rm_rf(file_path('uploads'))
|
867
|
+
end
|
868
|
+
|
869
|
+
it 'removes old file if old file had a different path' do
|
870
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
871
|
+
expect(File).to exist(public_path('uploads/thumb_old.jpeg'))
|
872
|
+
|
873
|
+
model.update!(images: [stub_file('new.jpeg')])
|
874
|
+
|
875
|
+
expect(File).to exist(public_path('uploads/new.jpeg'))
|
876
|
+
expect(File).to exist(public_path('uploads/thumb_new.jpeg'))
|
877
|
+
expect(File).not_to exist(public_path('uploads/old.jpeg'))
|
878
|
+
expect(File).not_to exist(public_path('uploads/thumb_old.jpeg'))
|
879
|
+
end
|
880
|
+
|
881
|
+
it 'does not remove file if old file had the same path' do
|
882
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
883
|
+
expect(File).to exist(public_path('uploads/thumb_old.jpeg'))
|
884
|
+
|
885
|
+
model.update!(images: [stub_file('old.jpeg')])
|
886
|
+
|
887
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
888
|
+
expect(File).to exist(public_path('uploads/thumb_old.jpeg'))
|
889
|
+
end
|
890
|
+
end
|
891
|
+
|
892
|
+
context 'with multiple uploaders' do
|
893
|
+
let(:model_class) do
|
894
|
+
uploader = uploader_class
|
895
|
+
|
896
|
+
Class.new(CarrierWave::Uploader::Base) do
|
897
|
+
include Mongoid::Document
|
898
|
+
|
899
|
+
store_in collection: :token_models
|
900
|
+
|
901
|
+
field :name
|
902
|
+
mount_uploaders :images, uploader
|
903
|
+
mount_uploaders :textfiles, uploader
|
904
|
+
|
905
|
+
def self.model_name
|
906
|
+
ActiveModel::Name.new(self, nil, 'TokenModel')
|
907
|
+
end
|
908
|
+
end
|
909
|
+
end
|
910
|
+
|
911
|
+
let!(:model) { model_class.create!(images: [stub_file('old.jpeg')], textfiles: [stub_file('old.txt')]) }
|
912
|
+
|
913
|
+
after do
|
914
|
+
FileUtils.rm_rf(file_path('uploads'))
|
915
|
+
end
|
916
|
+
|
917
|
+
it 'removes old file1 and file2 if old file1 and file2 had a different paths' do
|
918
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
919
|
+
expect(File).to exist(public_path('uploads/old.txt'))
|
920
|
+
|
921
|
+
model.update!(images: [stub_file('new.jpeg')], textfiles: [stub_file('new.txt')])
|
922
|
+
|
923
|
+
expect(File).to exist(public_path('uploads/new.jpeg'))
|
924
|
+
expect(File).not_to exist(public_path('uploads/old.jpeg'))
|
925
|
+
expect(File).to exist(public_path('uploads/new.txt'))
|
926
|
+
expect(File).not_to exist(public_path('uploads/old.txt'))
|
927
|
+
end
|
928
|
+
|
929
|
+
it 'removes old file1 but not file2 if old file1 had a different path but old file2 has the same path' do
|
930
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
931
|
+
expect(File).to exist(public_path('uploads/old.txt'))
|
932
|
+
|
933
|
+
model.update!(images: [stub_file('new.jpeg')], textfiles: [stub_file('old.txt')])
|
934
|
+
|
935
|
+
expect(File).to exist(public_path('uploads/new.jpeg'))
|
936
|
+
expect(File).not_to exist(public_path('uploads/old.jpeg'))
|
937
|
+
expect(File).to exist(public_path('uploads/old.txt'))
|
938
|
+
end
|
939
|
+
|
940
|
+
it 'does not remove file1 or file2 if file1 and file2 have the same paths' do
|
941
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
942
|
+
expect(File).to exist(public_path('uploads/old.txt'))
|
943
|
+
|
944
|
+
model.update!(images: [stub_file('old.jpeg')], textfiles: [stub_file('old.txt')])
|
945
|
+
|
946
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
947
|
+
expect(File).to exist(public_path('uploads/old.txt'))
|
948
|
+
end
|
949
|
+
end
|
950
|
+
|
951
|
+
describe 'with mount_on' do
|
952
|
+
let(:model_class) do
|
953
|
+
uploader = uploader_class
|
954
|
+
|
955
|
+
Class.new do
|
956
|
+
include Mongoid::Document
|
957
|
+
|
958
|
+
store_in collection: :token_models
|
959
|
+
|
960
|
+
field :name
|
961
|
+
mount_uploaders :avatars, uploader, mount_on: :images
|
962
|
+
|
963
|
+
def self.model_name
|
964
|
+
ActiveModel::Name.new(self, nil, 'TokenModel')
|
965
|
+
end
|
966
|
+
end
|
967
|
+
end
|
968
|
+
|
969
|
+
let!(:model) { model_class.create!(avatars: [stub_file('old.jpeg')]) }
|
970
|
+
|
971
|
+
after do
|
972
|
+
FileUtils.rm_rf(file_path('uploads'))
|
973
|
+
end
|
974
|
+
|
975
|
+
it 'removes old file if old file had a different path' do
|
976
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
977
|
+
|
978
|
+
model.update!(avatars: [stub_file('new.jpeg')])
|
979
|
+
|
980
|
+
expect(File).to exist(public_path('uploads/new.jpeg'))
|
981
|
+
expect(File).not_to exist(public_path('uploads/old.jpeg'))
|
982
|
+
end
|
983
|
+
|
984
|
+
it 'does not remove file if old file had the same path' do
|
985
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
986
|
+
|
987
|
+
model.update!(avatars: [stub_file('old.jpeg')])
|
988
|
+
|
989
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
990
|
+
end
|
991
|
+
end
|
992
|
+
end
|
993
|
+
|
994
|
+
# Mongoid::Paranoia support is only part of Mongoid 3.x.
|
995
|
+
# It was removed from Mongoid 4.x.
|
996
|
+
if defined?(Mongoid::Paranoia)
|
997
|
+
describe 'with paranoia enabled' do
|
998
|
+
let(:model_class) do
|
999
|
+
uploader = uploader_class
|
1000
|
+
|
1001
|
+
Class.new do
|
1002
|
+
include Mongoid::Document
|
1003
|
+
include Mongoid::Paranoia
|
1004
|
+
|
1005
|
+
store_in collection: :token_models
|
1006
|
+
|
1007
|
+
field :name
|
1008
|
+
mount_uploaders :images, uploader
|
1009
|
+
|
1010
|
+
def self.model_name
|
1011
|
+
ActiveModel::Name.new(self, nil, 'TokenModel')
|
1012
|
+
end
|
1013
|
+
end
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
let!(:model) { model_class.create!(images: [stub_file('old.jpeg')]) }
|
1017
|
+
|
1018
|
+
it 'does not remove underlying image after #destroy' do
|
1019
|
+
expect(model.destroy).to be_truthy
|
1020
|
+
|
1021
|
+
expect(model_class.count).to be(0)
|
1022
|
+
expect(model_class.deleted.count).to be(1)
|
1023
|
+
expect(File).to exist(public_path('uploads/old.jpeg'))
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
it 'removes underlying image after #destroy!' do
|
1027
|
+
expect(model.destroy!).to be_truthy
|
1028
|
+
|
1029
|
+
expect(model_class.count).to be(0)
|
1030
|
+
expect(model_class.deleted.count).to be(0)
|
1031
|
+
expect(File).not_to exist(public_path('uploads/old.jpeg'))
|
1032
|
+
end
|
1033
|
+
end
|
1034
|
+
end
|
1035
|
+
end
|
1036
|
+
end
|
1037
|
+
end
|