shrine 3.1.0 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +82 -0
- data/README.md +11 -4
- data/doc/advantages.md +4 -4
- data/doc/attacher.md +2 -2
- data/doc/carrierwave.md +24 -12
- data/doc/changing_derivatives.md +1 -1
- data/doc/changing_location.md +6 -5
- data/doc/design.md +134 -85
- data/doc/direct_s3.md +26 -0
- data/doc/external/articles.md +57 -45
- data/doc/external/extensions.md +41 -35
- data/doc/external/misc.md +23 -8
- data/doc/getting_started.md +156 -85
- data/doc/metadata.md +80 -44
- data/doc/multiple_files.md +1 -1
- data/doc/paperclip.md +28 -9
- data/doc/plugins/add_metadata.md +112 -35
- data/doc/plugins/atomic_helpers.md +41 -3
- data/doc/plugins/backgrounding.md +12 -2
- data/doc/plugins/column.md +36 -7
- data/doc/plugins/default_url.md +6 -3
- data/doc/plugins/derivatives.md +83 -44
- data/doc/plugins/download_endpoint.md +5 -5
- data/doc/plugins/dynamic_storage.md +1 -1
- data/doc/plugins/entity.md +12 -4
- data/doc/plugins/form_assign.md +5 -5
- data/doc/plugins/included.md +25 -5
- data/doc/plugins/infer_extension.md +9 -0
- data/doc/plugins/instrumentation.md +1 -1
- data/doc/plugins/metadata_attributes.md +1 -0
- data/doc/plugins/mirroring.md +1 -1
- data/doc/plugins/model.md +8 -3
- data/doc/plugins/persistence.md +10 -1
- data/doc/plugins/remote_url.md +6 -1
- data/doc/plugins/remove_invalid.md +9 -1
- data/doc/plugins/sequel.md +1 -1
- data/doc/plugins/store_dimensions.md +10 -0
- data/doc/plugins/type_predicates.md +96 -0
- data/doc/plugins/upload_endpoint.md +1 -1
- data/doc/plugins/upload_options.md +1 -1
- data/doc/plugins/url_options.md +4 -4
- data/doc/plugins/validation.md +14 -4
- data/doc/plugins/versions.md +7 -7
- data/doc/processing.md +287 -123
- data/doc/refile.md +9 -9
- data/doc/release_notes/2.8.0.md +1 -1
- data/doc/release_notes/3.0.0.md +1 -1
- data/doc/release_notes/3.2.0.md +96 -0
- data/doc/release_notes/3.2.1.md +31 -0
- data/doc/release_notes/3.2.2.md +14 -0
- data/doc/release_notes/3.3.0.md +105 -0
- data/doc/release_notes/3.4.0.md +35 -0
- data/doc/securing_uploads.md +2 -2
- data/doc/storage/memory.md +19 -0
- data/doc/storage/s3.md +104 -77
- data/doc/testing.md +12 -2
- data/doc/upgrading_to_3.md +99 -53
- data/lib/shrine.rb +9 -8
- data/lib/shrine/attacher.rb +20 -10
- data/lib/shrine/attachment.rb +2 -2
- data/lib/shrine/plugins.rb +22 -0
- data/lib/shrine/plugins/activerecord.rb +3 -3
- data/lib/shrine/plugins/add_metadata.rb +20 -5
- data/lib/shrine/plugins/backgrounding.rb +2 -2
- data/lib/shrine/plugins/default_url.rb +1 -1
- data/lib/shrine/plugins/derivation_endpoint.rb +13 -8
- data/lib/shrine/plugins/derivatives.rb +59 -30
- data/lib/shrine/plugins/determine_mime_type.rb +5 -3
- data/lib/shrine/plugins/entity.rb +12 -11
- data/lib/shrine/plugins/instrumentation.rb +12 -18
- data/lib/shrine/plugins/mirroring.rb +8 -8
- data/lib/shrine/plugins/model.rb +3 -3
- data/lib/shrine/plugins/presign_endpoint.rb +16 -4
- data/lib/shrine/plugins/pretty_location.rb +1 -1
- data/lib/shrine/plugins/processing.rb +1 -1
- data/lib/shrine/plugins/refresh_metadata.rb +2 -2
- data/lib/shrine/plugins/remote_url.rb +3 -3
- data/lib/shrine/plugins/remove_attachment.rb +5 -0
- data/lib/shrine/plugins/remove_invalid.rb +10 -5
- data/lib/shrine/plugins/sequel.rb +1 -1
- data/lib/shrine/plugins/store_dimensions.rb +4 -2
- data/lib/shrine/plugins/type_predicates.rb +113 -0
- data/lib/shrine/plugins/upload_endpoint.rb +10 -5
- data/lib/shrine/plugins/upload_options.rb +2 -2
- data/lib/shrine/plugins/url_options.rb +2 -2
- data/lib/shrine/plugins/validation.rb +9 -7
- data/lib/shrine/storage/linter.rb +4 -4
- data/lib/shrine/storage/memory.rb +5 -3
- data/lib/shrine/storage/s3.rb +117 -38
- data/lib/shrine/version.rb +1 -1
- data/shrine.gemspec +8 -8
- metadata +42 -34
data/doc/testing.md
CHANGED
@@ -119,7 +119,7 @@ module TestData
|
|
119
119
|
small: uploaded_image,
|
120
120
|
)
|
121
121
|
|
122
|
-
attacher.column_data
|
122
|
+
attacher.column_data # or attacher.data in case of postgres jsonb column
|
123
123
|
end
|
124
124
|
|
125
125
|
def uploaded_image
|
@@ -128,7 +128,7 @@ module TestData
|
|
128
128
|
# for performance we skip metadata extraction and assign test metadata
|
129
129
|
uploaded_file = Shrine.upload(file, :store, metadata: false)
|
130
130
|
uploaded_file.metadata.merge!(
|
131
|
-
"size" => file.
|
131
|
+
"size" => File.size(file.path),
|
132
132
|
"mime_type" => "image/jpeg",
|
133
133
|
"filename" => "test.jpg",
|
134
134
|
)
|
@@ -251,6 +251,16 @@ TestMode.disable_processing(Photo.image_attacher) do
|
|
251
251
|
end
|
252
252
|
```
|
253
253
|
|
254
|
+
## Testing direct upload
|
255
|
+
|
256
|
+
If you'd like to unit-test direct upload on the server side, you can
|
257
|
+
emulate it by uploading a file to `cache` and then assigning it to the record.
|
258
|
+
|
259
|
+
```rb
|
260
|
+
cached_file = Shrine.upload(some_file, :cache)
|
261
|
+
record.attachment = cached_file.to_json
|
262
|
+
```
|
263
|
+
|
254
264
|
[DatabaseCleaner]: https://github.com/DatabaseCleaner/database_cleaner
|
255
265
|
[`#attach_file`]: http://www.rubydoc.info/github/jnicklas/capybara/master/Capybara/Node/Actions#attach_file-instance_method
|
256
266
|
[aws-sdk-ruby stubs]: http://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/ClientStubs.html
|
data/doc/upgrading_to_3.md
CHANGED
@@ -237,7 +237,7 @@ class PromoteJob
|
|
237
237
|
rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
|
238
238
|
# attachment has changed or record has been deleted, nothing to do
|
239
239
|
end
|
240
|
-
|
240
|
+
end
|
241
241
|
```
|
242
242
|
```rb
|
243
243
|
class DestroyJob
|
@@ -258,7 +258,7 @@ class DestroyJob
|
|
258
258
|
attacher = attacher_class.from_data(data)
|
259
259
|
attacher.destroy
|
260
260
|
end
|
261
|
-
|
261
|
+
end
|
262
262
|
```
|
263
263
|
|
264
264
|
### Attacher backgrounding
|
@@ -283,8 +283,9 @@ attacher.destroy_background # calls destroy block
|
|
283
283
|
## Versions
|
284
284
|
|
285
285
|
The `versions`, `processing`, `recache`, and `delete_raw` plugins have been
|
286
|
-
deprecated in favour of the new **[`derivatives`][derivatives]** plugin.
|
287
|
-
|
286
|
+
deprecated in favour of the new **[`derivatives`][derivatives]** plugin.
|
287
|
+
|
288
|
+
Let's assume you have the following `versions` configuration:
|
288
289
|
|
289
290
|
```rb
|
290
291
|
class ImageUploader < Shrine
|
@@ -307,26 +308,32 @@ class ImageUploader < Shrine
|
|
307
308
|
end
|
308
309
|
end
|
309
310
|
```
|
311
|
+
|
312
|
+
When an attached file is promoted to permanent storage, the versions would
|
313
|
+
automatically get generated:
|
314
|
+
|
310
315
|
```rb
|
311
316
|
photo = Photo.new(photo_params)
|
312
317
|
|
313
318
|
if photo.valid?
|
314
|
-
photo.save #
|
319
|
+
photo.save # generates versions on promotion
|
315
320
|
# ...
|
316
321
|
else
|
317
322
|
# ...
|
318
323
|
end
|
319
324
|
```
|
320
325
|
|
321
|
-
With `derivatives`, the original file is automatically downloaded and retained
|
322
|
-
so the
|
326
|
+
With `derivatives`, the original file is automatically downloaded and retained
|
327
|
+
during processing, so the setup is simpler:
|
323
328
|
|
324
329
|
```rb
|
325
|
-
Shrine.plugin :derivatives,
|
330
|
+
Shrine.plugin :derivatives,
|
331
|
+
create_on_promote: true, # automatically create derivatives on promotion
|
332
|
+
versions_compatibility: true # handle versions column format
|
326
333
|
```
|
327
334
|
```rb
|
328
335
|
class ImageUploader < Shrine
|
329
|
-
Attacher.
|
336
|
+
Attacher.derivatives do |original|
|
330
337
|
magick = ImageProcessing::MiniMagick.source(original)
|
331
338
|
|
332
339
|
# the :original file should NOT be included anymore
|
@@ -342,28 +349,13 @@ end
|
|
342
349
|
photo = Photo.new(photo_params)
|
343
350
|
|
344
351
|
if photo.valid?
|
345
|
-
photo.
|
346
|
-
photo.save # automatically calls processing block
|
352
|
+
photo.save # creates derivatives on promotion
|
347
353
|
# ...
|
348
354
|
else
|
349
355
|
# ...
|
350
356
|
end
|
351
357
|
```
|
352
358
|
|
353
|
-
If you have multiple places where you need to generate derivatives, and want it
|
354
|
-
to happen automatically like it did with the `versions` plugin, you can
|
355
|
-
override `Attacher#promote` to call `Attacher#create_derivatives` before
|
356
|
-
promotion:
|
357
|
-
|
358
|
-
```rb
|
359
|
-
class Shrine::Attacher
|
360
|
-
def promote(*)
|
361
|
-
create_derivatives
|
362
|
-
super
|
363
|
-
end
|
364
|
-
end
|
365
|
-
```
|
366
|
-
|
367
359
|
### Accessing derivatives
|
368
360
|
|
369
361
|
The derivative URLs are accessed in the same way as versions:
|
@@ -372,7 +364,7 @@ The derivative URLs are accessed in the same way as versions:
|
|
372
364
|
photo.image_url(:small)
|
373
365
|
```
|
374
366
|
|
375
|
-
But the
|
367
|
+
But the files themselves are accessed differently:
|
376
368
|
|
377
369
|
```rb
|
378
370
|
# versions
|
@@ -426,8 +418,8 @@ database column in different formats:
|
|
426
418
|
|
427
419
|
The `:versions_compatibility` flag to the `derivatives` plugin enables it to
|
428
420
|
read the `versions` format, which aids in transition. Once the `derivatives`
|
429
|
-
plugin has been deployed to production, you can
|
430
|
-
new column format:
|
421
|
+
plugin has been deployed to production, you can update existing records with
|
422
|
+
the new column format:
|
431
423
|
|
432
424
|
```rb
|
433
425
|
Photo.find_each do |photo|
|
@@ -447,7 +439,7 @@ creation in the `PromoteJob` instead of the controller:
|
|
447
439
|
class PromoteJob
|
448
440
|
include Sidekiq::Worker
|
449
441
|
|
450
|
-
def perform(attacher_class, record_class,
|
442
|
+
def perform(attacher_class, record_class, record_id, name, file_data)
|
451
443
|
attacher_class = Object.const_get(attacher_class)
|
452
444
|
record = Object.const_get(record_class).find(record_id) # if using Active Record
|
453
445
|
|
@@ -467,11 +459,11 @@ creating another derivatives processor that you will trigger in the controller:
|
|
467
459
|
|
468
460
|
```rb
|
469
461
|
class ImageUploader < Shrine
|
470
|
-
Attacher.
|
462
|
+
Attacher.derivatives do |original|
|
471
463
|
# this will be triggered in the background job
|
472
464
|
end
|
473
465
|
|
474
|
-
Attacher.
|
466
|
+
Attacher.derivatives :foreground do |original|
|
475
467
|
# this will be triggered in the controller
|
476
468
|
end
|
477
469
|
end
|
@@ -488,48 +480,77 @@ else
|
|
488
480
|
end
|
489
481
|
```
|
490
482
|
|
491
|
-
|
483
|
+
### Default URL
|
492
484
|
|
493
|
-
|
494
|
-
|
485
|
+
If you were using the `default_url` plugin, the `Attacher.default_url` now
|
486
|
+
receives a `:derivative` option:
|
495
487
|
|
496
488
|
```rb
|
497
|
-
|
498
|
-
|
489
|
+
Attacher.default_url do |derivative: nil, **|
|
490
|
+
"https://my-app.com/fallbacks/#{derivative}.jpg" if derivative
|
491
|
+
end
|
499
492
|
```
|
500
|
-
```rb
|
501
|
-
require "concurrent"
|
502
493
|
|
503
|
-
|
494
|
+
#### Fallback to original
|
504
495
|
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
end
|
509
|
-
end
|
496
|
+
With the `versions` plugin, a missing version URL would automatically fall back
|
497
|
+
to the original file. The `derivatives` plugin has no such fallback, but you
|
498
|
+
can configure it manually:
|
510
499
|
|
511
|
-
|
500
|
+
```rb
|
501
|
+
Attacher.default_url do |derivative: nil, **|
|
502
|
+
file&.url if derivative
|
503
|
+
end
|
512
504
|
```
|
513
505
|
|
514
|
-
####
|
506
|
+
#### Fallback to version
|
515
507
|
|
516
|
-
The `
|
508
|
+
The `versions` plugin had the ability to fall back missing version URL to
|
509
|
+
another version that already exists. The `derivatives` plugin doesn't have this
|
510
|
+
built in, but you can implement it as follows:
|
517
511
|
|
518
512
|
```rb
|
513
|
+
DERIVATIVE_FALLBACKS = { foo: :bar, ... }
|
514
|
+
|
519
515
|
Attacher.default_url do |derivative: nil, **|
|
520
|
-
|
516
|
+
derivatives[DERIVATIVE_FALLBACKS[derivative]]&.url if derivative
|
521
517
|
end
|
522
518
|
```
|
523
519
|
|
524
|
-
|
525
|
-
|
520
|
+
### Location
|
521
|
+
|
522
|
+
The `Shrine#generate_location` method will now receive a `:derivative`
|
523
|
+
parameter instead of `:version`:
|
524
|
+
|
525
|
+
```rb
|
526
|
+
class MyUploader < Shrine
|
527
|
+
def generate_location(io, derivative: nil, **)
|
528
|
+
derivative #=> :large, :medium, :small, ...
|
529
|
+
# ...
|
530
|
+
end
|
531
|
+
end
|
532
|
+
```
|
533
|
+
|
534
|
+
### Overwriting original
|
535
|
+
|
536
|
+
With the `derivatives` plugin, saving processed files separately from the
|
537
|
+
original file, so the original file is automatically kept. This means it's not
|
538
|
+
possible anymore to overwrite the original file as part of processing.
|
539
|
+
|
540
|
+
However, **it's highly recommended to always keep the original file**, even if
|
541
|
+
you don't plan to use it. That way, if there is ever a need to reprocess
|
542
|
+
derivatives, you have the original file to use as a base.
|
543
|
+
|
544
|
+
That being said, if you still want to overwrite the original file, [this
|
545
|
+
thread][overwriting original] has some tips.
|
526
546
|
|
527
547
|
## Other
|
528
548
|
|
529
549
|
### Processing
|
530
550
|
|
531
551
|
The `processing` plugin has been deprecated over the new
|
532
|
-
[`derivatives`][derivatives] plugin. If you were
|
552
|
+
[`derivatives`][derivatives] plugin. If you were previously replacing the
|
553
|
+
original file:
|
533
554
|
|
534
555
|
```rb
|
535
556
|
class MyUploader < Shrine
|
@@ -549,7 +570,7 @@ you should now add the processed file as a derivative:
|
|
549
570
|
class MyUploader < Shrine
|
550
571
|
plugin :derivatives
|
551
572
|
|
552
|
-
Attacher.
|
573
|
+
Attacher.derivatives do |original|
|
553
574
|
magick = ImageProcessing::MiniMagick.source(original)
|
554
575
|
|
555
576
|
{ normalized: magick.resize_to_limit!(1600, 1600) }
|
@@ -557,6 +578,30 @@ class MyUploader < Shrine
|
|
557
578
|
end
|
558
579
|
```
|
559
580
|
|
581
|
+
### Parallelize
|
582
|
+
|
583
|
+
The `parallelize` plugin has been removed. With `derivatives` plugin you can
|
584
|
+
parallelize uploading processed files manually:
|
585
|
+
|
586
|
+
```rb
|
587
|
+
# Gemfile
|
588
|
+
gem "concurrent-ruby"
|
589
|
+
```
|
590
|
+
```rb
|
591
|
+
require "concurrent"
|
592
|
+
|
593
|
+
attacher = photo.image_attacher
|
594
|
+
derivatives = attacher.process_derivatives
|
595
|
+
|
596
|
+
tasks = derivatives.map do |name, file|
|
597
|
+
Concurrent::Promises.future(name, file) do |name, file|
|
598
|
+
attacher.add_derivative(name, file)
|
599
|
+
end
|
600
|
+
end
|
601
|
+
|
602
|
+
Concurrent::Promises.zip(*tasks).wait!
|
603
|
+
```
|
604
|
+
|
560
605
|
### Logging
|
561
606
|
|
562
607
|
The `logging` plugin has been removed in favour of the
|
@@ -604,7 +649,7 @@ attacher.copy(other_attacher)
|
|
604
649
|
with
|
605
650
|
|
606
651
|
```rb
|
607
|
-
attacher.
|
652
|
+
attacher.set attacher.upload(other_attacher.file)
|
608
653
|
attacher.add_derivatives other_attacher.derivatives # if using derivatives
|
609
654
|
```
|
610
655
|
|
@@ -662,3 +707,4 @@ end
|
|
662
707
|
[derivatives]: https://shrinerb.com/docs/plugins/derivatives
|
663
708
|
[instrumentation]: https://shrinerb.com/docs/plugins/instrumentation
|
664
709
|
[mirroring]: https://shrinerb.com/docs/plugins/mirroring
|
710
|
+
[overwriting original]: https://discourse.shrinerb.com/t/keep-original-file-after-processing/50/4
|
data/lib/shrine.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "shrine/version"
|
4
3
|
require "shrine/uploaded_file"
|
5
4
|
require "shrine/attacher"
|
6
5
|
require "shrine/attachment"
|
7
6
|
require "shrine/plugins"
|
7
|
+
require "shrine/version"
|
8
8
|
|
9
9
|
require "securerandom"
|
10
10
|
require "json"
|
@@ -14,7 +14,8 @@ require "logger"
|
|
14
14
|
# Core class that handles uploading files to specified storage.
|
15
15
|
class Shrine
|
16
16
|
# A generic exception used by Shrine.
|
17
|
-
class Error < StandardError
|
17
|
+
class Error < StandardError
|
18
|
+
end
|
18
19
|
|
19
20
|
# Raised when a file is not a valid IO.
|
20
21
|
class InvalidFile < Error
|
@@ -67,9 +68,9 @@ class Shrine
|
|
67
68
|
#
|
68
69
|
# Shrine.plugin MyPlugin
|
69
70
|
# Shrine.plugin :my_plugin
|
70
|
-
def plugin(plugin, *args, &block)
|
71
|
+
def plugin(plugin, *args, **kwargs, &block)
|
71
72
|
plugin = Plugins.load_plugin(plugin) if plugin.is_a?(Symbol)
|
72
|
-
|
73
|
+
Plugins.load_dependencies(plugin, self, *args, **kwargs, &block)
|
73
74
|
self.include(plugin::InstanceMethods) if defined?(plugin::InstanceMethods)
|
74
75
|
self.extend(plugin::ClassMethods) if defined?(plugin::ClassMethods)
|
75
76
|
self::UploadedFile.include(plugin::FileMethods) if defined?(plugin::FileMethods)
|
@@ -78,7 +79,7 @@ class Shrine
|
|
78
79
|
self::Attachment.extend(plugin::AttachmentClassMethods) if defined?(plugin::AttachmentClassMethods)
|
79
80
|
self::Attacher.include(plugin::AttacherMethods) if defined?(plugin::AttacherMethods)
|
80
81
|
self::Attacher.extend(plugin::AttacherClassMethods) if defined?(plugin::AttacherClassMethods)
|
81
|
-
|
82
|
+
Plugins.configure(plugin, self, *args, **kwargs, &block)
|
82
83
|
plugin
|
83
84
|
end
|
84
85
|
|
@@ -94,8 +95,8 @@ class Shrine
|
|
94
95
|
# class Photo
|
95
96
|
# include Shrine::Attachment(:image) # creates a Shrine::Attachment object
|
96
97
|
# end
|
97
|
-
def Attachment(name,
|
98
|
-
self::Attachment.new(name,
|
98
|
+
def Attachment(name, **args)
|
99
|
+
self::Attachment.new(name, **args)
|
99
100
|
end
|
100
101
|
alias attachment Attachment
|
101
102
|
alias [] Attachment
|
@@ -296,7 +297,7 @@ class Shrine
|
|
296
297
|
# Retrieves the location for the given IO and context. First it looks
|
297
298
|
# for the `:location` option, otherwise it calls #generate_location.
|
298
299
|
def get_location(io, location: nil, **options)
|
299
|
-
location ||= generate_location(io, options)
|
300
|
+
location ||= generate_location(io, **options)
|
300
301
|
location or fail Error, "location generated for #{io.inspect} was nil"
|
301
302
|
end
|
302
303
|
|
data/lib/shrine/attacher.rb
CHANGED
@@ -39,10 +39,11 @@ class Shrine
|
|
39
39
|
|
40
40
|
# Initializes the attached file, temporary and permanent storage.
|
41
41
|
def initialize(file: nil, cache: :cache, store: :store)
|
42
|
-
@file
|
43
|
-
@cache
|
44
|
-
@store
|
45
|
-
@context
|
42
|
+
@file = file
|
43
|
+
@cache = cache
|
44
|
+
@store = store
|
45
|
+
@context = {}
|
46
|
+
@previous = nil
|
46
47
|
end
|
47
48
|
|
48
49
|
# Returns the temporary storage identifier.
|
@@ -69,6 +70,10 @@ class Shrine
|
|
69
70
|
def assign(value, **options)
|
70
71
|
return if value == "" # skip empty hidden field
|
71
72
|
|
73
|
+
if value.is_a?(Hash) || value.is_a?(String)
|
74
|
+
return if uploaded_file(value) == file # skip assignment for current file
|
75
|
+
end
|
76
|
+
|
72
77
|
attach_cached(value, **options)
|
73
78
|
end
|
74
79
|
|
@@ -89,7 +94,7 @@ class Shrine
|
|
89
94
|
# attacher.attach_cached({ "id" => "...", "storage" => "cache", "metadata" => {} })
|
90
95
|
def attach_cached(value, **options)
|
91
96
|
if value.is_a?(String) || value.is_a?(Hash)
|
92
|
-
change(cached(value, **options)
|
97
|
+
change(cached(value, **options))
|
93
98
|
else
|
94
99
|
attach(value, storage: cache_key, action: :cache, **options)
|
95
100
|
end
|
@@ -111,7 +116,7 @@ class Shrine
|
|
111
116
|
def attach(io, storage: store_key, **options)
|
112
117
|
file = upload(io, storage, **options) if io
|
113
118
|
|
114
|
-
change(file
|
119
|
+
change(file)
|
115
120
|
end
|
116
121
|
|
117
122
|
# Deletes any previous file and promotes newly attached cached file.
|
@@ -138,7 +143,7 @@ class Shrine
|
|
138
143
|
def finalize
|
139
144
|
destroy_previous
|
140
145
|
promote_cached
|
141
|
-
|
146
|
+
@previous = nil
|
142
147
|
end
|
143
148
|
|
144
149
|
# Plugins can override this if they want something to be done in a
|
@@ -211,8 +216,8 @@ class Shrine
|
|
211
216
|
# attacher.change(uploaded_file)
|
212
217
|
# attacher.file #=> #<Shrine::UploadedFile>
|
213
218
|
# attacher.changed? #=> true
|
214
|
-
def change(file
|
215
|
-
@previous = dup
|
219
|
+
def change(file)
|
220
|
+
@previous = dup if change?(file)
|
216
221
|
set(file)
|
217
222
|
end
|
218
223
|
|
@@ -254,7 +259,7 @@ class Shrine
|
|
254
259
|
# attacher.attach(file)
|
255
260
|
# attacher.changed? #=> true
|
256
261
|
def changed?
|
257
|
-
|
262
|
+
!!@previous
|
258
263
|
end
|
259
264
|
|
260
265
|
# Returns whether a file is attached.
|
@@ -368,6 +373,11 @@ class Shrine
|
|
368
373
|
attached? && !cached?
|
369
374
|
end
|
370
375
|
|
376
|
+
# Whether assigning the given file is considered a change.
|
377
|
+
def change?(file)
|
378
|
+
@file != file
|
379
|
+
end
|
380
|
+
|
371
381
|
# Returns whether the file is uploaded to specified storage.
|
372
382
|
def uploaded?(file, storage_key)
|
373
383
|
file&.storage_key == storage_key
|