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.
@@ -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