carrierwave-mongoid 1.3.0 → 1.4.0

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