shrine 3.2.2 → 3.3.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 +36 -0
- data/README.md +5 -0
- data/doc/advantages.md +1 -1
- data/doc/carrierwave.md +7 -4
- data/doc/direct_s3.md +1 -0
- data/doc/external/articles.md +6 -8
- data/doc/external/extensions.md +3 -0
- data/doc/external/misc.md +23 -8
- data/doc/getting_started.md +4 -8
- data/doc/multiple_files.md +1 -1
- data/doc/paperclip.md +14 -5
- data/doc/plugins/add_metadata.md +20 -0
- data/doc/plugins/atomic_helpers.md +41 -3
- data/doc/plugins/derivatives.md +43 -12
- data/doc/plugins/download_endpoint.md +5 -5
- data/doc/plugins/dynamic_storage.md +1 -1
- data/doc/plugins/infer_extension.md +9 -0
- data/doc/plugins/metadata_attributes.md +1 -0
- data/doc/plugins/mirroring.md +1 -1
- data/doc/plugins/persistence.md +10 -1
- data/doc/plugins/store_dimensions.md +10 -0
- data/doc/plugins/url_options.md +2 -2
- data/doc/processing.md +7 -8
- data/doc/release_notes/2.8.0.md +1 -1
- data/doc/release_notes/3.2.1.md +2 -3
- data/doc/release_notes/3.3.0.md +105 -0
- data/doc/storage/s3.md +9 -5
- data/doc/upgrading_to_3.md +11 -27
- data/lib/shrine/attacher.rb +6 -1
- data/lib/shrine/plugins/activerecord.rb +1 -1
- data/lib/shrine/plugins/add_metadata.rb +6 -4
- data/lib/shrine/plugins/backgrounding.rb +2 -2
- data/lib/shrine/plugins/derivation_endpoint.rb +2 -1
- data/lib/shrine/plugins/derivatives.rb +45 -15
- data/lib/shrine/plugins/mirroring.rb +8 -8
- data/lib/shrine/plugins/presign_endpoint.rb +14 -2
- data/lib/shrine/plugins/remove_attachment.rb +5 -0
- data/lib/shrine/plugins/sequel.rb +1 -1
- data/lib/shrine/plugins/store_dimensions.rb +4 -2
- data/lib/shrine/plugins/upload_endpoint.rb +7 -2
- data/lib/shrine/storage/memory.rb +5 -3
- data/lib/shrine/storage/s3.rb +61 -6
- data/lib/shrine/version.rb +2 -2
- data/shrine.gemspec +1 -1
- metadata +6 -5
@@ -87,6 +87,10 @@ class DownloadsController < ApplicationController
|
|
87
87
|
end
|
88
88
|
```
|
89
89
|
|
90
|
+
If you want to create an endpoint with a custom path, you can use the
|
91
|
+
[`rack_response`][rack_response] plugin directly, which this plugin uses
|
92
|
+
internally.
|
93
|
+
|
90
94
|
## Host
|
91
95
|
|
92
96
|
You can specify download URL host via the `:host` plugin option:
|
@@ -155,11 +159,6 @@ You can override any of the options above when creating the endpoint:
|
|
155
159
|
Shrine.download_endpoint(disposition: "attachment")
|
156
160
|
```
|
157
161
|
|
158
|
-
## Custom endpoint
|
159
|
-
|
160
|
-
If you want to have more control on download requests, you can use the
|
161
|
-
`rack_response` plugin which this plugin uses internally.
|
162
|
-
|
163
162
|
## Plugin options
|
164
163
|
|
165
164
|
| Name | Description | Default |
|
@@ -171,3 +170,4 @@ If you want to have more control on download requests, you can use the
|
|
171
170
|
| `:redirect` | Whether to redirect to uploaded files on the storage | `false` |
|
172
171
|
|
173
172
|
[download_endpoint]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/download_endpoint.rb
|
173
|
+
[rack_response]: https://shrinerb.com/docs/plugins/rack_response
|
@@ -11,6 +11,15 @@ extension might not be known.
|
|
11
11
|
plugin :infer_extension
|
12
12
|
```
|
13
13
|
|
14
|
+
By default an extension will only be inferred if needed to supply an otherwise
|
15
|
+
missing extension. But option `force: true` will normalize even an already
|
16
|
+
present extension to the extension inferred from MIME type. This could be used
|
17
|
+
to fix incorrect or malicious extensions on user-submitted files.
|
18
|
+
|
19
|
+
```rb
|
20
|
+
plugin :infer_extension, force: true
|
21
|
+
```
|
22
|
+
|
14
23
|
## Inferrers
|
15
24
|
|
16
25
|
By default, the [mini_mime] gem will be used for inferring the extension, but
|
data/doc/plugins/mirroring.md
CHANGED
@@ -50,7 +50,7 @@ Shrine.plugin :mirroring, mirror: { ... }, delete: false
|
|
50
50
|
You can have mirroring performed in a background job:
|
51
51
|
|
52
52
|
```rb
|
53
|
-
Shrine.mirror_upload_block do |file|
|
53
|
+
Shrine.mirror_upload_block do |file, **options|
|
54
54
|
MirrorUploadJob.perform_async(file.shrine_class.name, file.data)
|
55
55
|
end
|
56
56
|
|
data/doc/plugins/persistence.md
CHANGED
@@ -6,6 +6,10 @@ This is an internal plugin that provides uniform persistence interface across
|
|
6
6
|
different persistence plugins (e.g. [`activerecord`][activerecord],
|
7
7
|
[`sequel`][sequel]).
|
8
8
|
|
9
|
+
For these activerecord and sequel, atomic persistence is implemented in terms
|
10
|
+
of database locks, eg "SELECT... FOR UPDATE". For more discussion of concurrency
|
11
|
+
challenges, see the [atomic_helpers] documentation.
|
12
|
+
|
9
13
|
## Atomic promotion
|
10
14
|
|
11
15
|
If you're promoting cached file to permanent storage
|
@@ -65,11 +69,15 @@ changed, and if it hasn't the attachment is persisted. If the attachment has
|
|
65
69
|
changed, `Shrine::AttachmentChanged` exception is raised.
|
66
70
|
|
67
71
|
If you want to execute code after the attachment change check but before
|
68
|
-
persistence, you can pass a block
|
72
|
+
persistence, you can pass a block. For instance, one way to allow concurrent
|
73
|
+
changes to metadata, perhaps in different background workers, without
|
74
|
+
overwriting each other might be:
|
69
75
|
|
70
76
|
```rb
|
71
77
|
attacher.atomic_persist do |reloaded_attacher|
|
72
78
|
# run code after attachment change check but before persistence
|
79
|
+
attacher.file.metadata.merge!(reloaded_attacher.file.metadata)
|
80
|
+
attacher.file.metadata["some_key"] = "changed_value"
|
73
81
|
end
|
74
82
|
```
|
75
83
|
|
@@ -89,4 +97,5 @@ attacher.persist # saves the underlying record
|
|
89
97
|
|
90
98
|
[activerecord]: https://shrinerb.com/docs/plugins/activerecord
|
91
99
|
[sequel]: https://shrinerb.com/docs/plugins/sequel
|
100
|
+
[atomic_helpers]: https://shrinerb.com/docs/plugins/atomic_helpers
|
92
101
|
[backgrounding]: https://shrinerb.com/docs/plugins/backgrounding
|
@@ -72,6 +72,16 @@ Shrine.dimensions(io) #=> [300, 400] (calls the defined analyzer)
|
|
72
72
|
Shrine.dimensions_analyzers[:fastimage].call(io) #=> [300, 400] (calls a built-in analyzer)
|
73
73
|
```
|
74
74
|
|
75
|
+
### Disabling auto-extraction
|
76
|
+
|
77
|
+
If you want to use the dimensions extraction methods but not automatically
|
78
|
+
extract dimensions on upload, you can setup this plugin with the
|
79
|
+
`auto_extraction: false` option.
|
80
|
+
|
81
|
+
```rb
|
82
|
+
plugin :store_dimensions, auto_extraction: false
|
83
|
+
```
|
84
|
+
|
75
85
|
## Errors
|
76
86
|
|
77
87
|
By default, any exceptions that the analyzer raises while extracting dimensions
|
data/doc/plugins/url_options.md
CHANGED
@@ -4,10 +4,10 @@ title: URL Options
|
|
4
4
|
|
5
5
|
The [`url_options`][url_options] plugin allows you to specify
|
6
6
|
URL options that will be applied by default for uploaded files of specified
|
7
|
-
storages.
|
7
|
+
storages. `url_options` are parameters specific to the storage service.
|
8
8
|
|
9
9
|
```rb
|
10
|
-
plugin :url_options, store: { expires_in: 24*60*60 }
|
10
|
+
plugin :url_options, store: { expires_in: 24*60*60 } # `expires_in` is a URL option for AWS S3
|
11
11
|
```
|
12
12
|
|
13
13
|
You can also generate the default URL options dynamically by using a block,
|
data/doc/processing.md
CHANGED
@@ -60,8 +60,11 @@ end
|
|
60
60
|
```
|
61
61
|
```rb
|
62
62
|
photo = Photo.new(image: file)
|
63
|
-
|
64
|
-
photo.
|
63
|
+
|
64
|
+
if photo.valid?
|
65
|
+
photo.image_derivatives! if photo.image_changed? # creates derivatives
|
66
|
+
photo.save
|
67
|
+
end
|
65
68
|
```
|
66
69
|
|
67
70
|
After the processed files are uploaded, their data is saved into the
|
@@ -468,15 +471,11 @@ end
|
|
468
471
|
### Automatic derivatives
|
469
472
|
|
470
473
|
If you would like derivatives to be automatically created with promotion, you
|
471
|
-
can
|
472
|
-
promotion:
|
474
|
+
can use the `create_on_promote` option built-in to the derivatives plugin.
|
473
475
|
|
474
476
|
```rb
|
475
477
|
class Shrine::Attacher
|
476
|
-
|
477
|
-
create_derivatives
|
478
|
-
super
|
479
|
-
end
|
478
|
+
plugin :derivatives, create_on_promote: true
|
480
479
|
end
|
481
480
|
```
|
482
481
|
|
data/doc/release_notes/2.8.0.md
CHANGED
data/doc/release_notes/3.2.1.md
CHANGED
@@ -20,10 +20,9 @@ title: Shrine 3.2.1
|
|
20
20
|
gem "image_processing", ">= 1.10.3", "< 2"
|
21
21
|
```
|
22
22
|
|
23
|
-
## Rack 2.1
|
23
|
+
## Rack 2.1 compatibility
|
24
24
|
|
25
|
-
* The `derivation_endpoint` plugin now uses `Rack::Files` on Rack 2.1
|
26
|
-
newer.
|
25
|
+
* The `derivation_endpoint` plugin now uses `Rack::Files` on Rack 2.1 or newer.
|
27
26
|
|
28
27
|
## Other improvements
|
29
28
|
|
@@ -0,0 +1,105 @@
|
|
1
|
+
---
|
2
|
+
title: Shrine 3.3.0
|
3
|
+
---
|
4
|
+
|
5
|
+
## New features
|
6
|
+
|
7
|
+
* The `:create_on_promote` option has been added to the `derivatives` plugin
|
8
|
+
for automatically creating derivatives after the attached cached file is
|
9
|
+
promoted to permanent storage.
|
10
|
+
|
11
|
+
```rb
|
12
|
+
Shrine.plugin :derivatives, create_on_promote: true
|
13
|
+
```
|
14
|
+
|
15
|
+
* The `:auto_extraction` option has been added to the `store_dimensions` plugin
|
16
|
+
for skipping automatically extracting dimensions on upload.
|
17
|
+
|
18
|
+
```rb
|
19
|
+
Shrine.plugin :store_dimensions, auto_extraction: false
|
20
|
+
```
|
21
|
+
|
22
|
+
* The `:skip_nil` option has been added to the `add_metadata` plugin for
|
23
|
+
excluding metadata keys whose values are nil.
|
24
|
+
|
25
|
+
```rb
|
26
|
+
class PdfUploader < Shrine
|
27
|
+
add_metadata :pages, skip_nil: true do |io|
|
28
|
+
if is_pdf?(io)
|
29
|
+
reader = PDF::Reader.new(io)
|
30
|
+
reader.page_count
|
31
|
+
else
|
32
|
+
# If this is not a PDF, then the pages metadata will not be stored
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
* The `:download` option has been added to derivatives processors in
|
40
|
+
`derivatives` plugin for skipping converting the source IO object into a
|
41
|
+
file. This can be used to avoid a potentially expensive download/copy when
|
42
|
+
the derivatives processor doesn't need the file.
|
43
|
+
|
44
|
+
```rb
|
45
|
+
Attacher.derivatives :my_processor, download: false do |source|
|
46
|
+
source #=> Could be File, Shrine::UploadedFile, or other IO-like object
|
47
|
+
shrine_class.with_file(source) do |file|
|
48
|
+
# can force download/copy if necessary with `with_file`,
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
## Bug fixes
|
54
|
+
|
55
|
+
* The `upload_endpoint` now handles calling `Shrine.upload_response` method
|
56
|
+
from a Rails controller.
|
57
|
+
|
58
|
+
* The `derivation_endpoint` plugin now applies the `version` query parameter
|
59
|
+
to the derivation when creating the response.
|
60
|
+
|
61
|
+
## Other improvements
|
62
|
+
|
63
|
+
* The new `Aws:S3::EncryptionV2::Client` is now supported by the S3 storage for
|
64
|
+
client-side encryption.
|
65
|
+
|
66
|
+
* The `derivation_endpoint` now reduces the possibility of timing attacks by
|
67
|
+
comparing URL signatures in constant time using `Rack::Utils.secure_compare`.
|
68
|
+
|
69
|
+
* The `derivatives` plugin now copies non-file source IO objects to disk before
|
70
|
+
passing them to the processor. This is consistent with how a
|
71
|
+
`Shrine::UploadedFile` object is downloaded to disk.
|
72
|
+
|
73
|
+
* The `sequel` and `activerecord` plugins now call `Attacher#reload` when
|
74
|
+
reloading the model, which reloads the attached files but keeps other
|
75
|
+
attacher state.
|
76
|
+
|
77
|
+
* The `derivatives` plugin doesn't download the attached file anymore if
|
78
|
+
attempting to process derivatives when no derivatives processor was defined.
|
79
|
+
|
80
|
+
* The `mirroring` plugin now forwards attacher options when uploading to mirror
|
81
|
+
storages.
|
82
|
+
|
83
|
+
* The `presign_endpoint` plugin now handles the `OPTIONS` HTTP verb, which
|
84
|
+
newer versions of Uppy are requesting.
|
85
|
+
|
86
|
+
* `Shrine::Storage::Memory#open` now always returns a `StringIO` in the file
|
87
|
+
content's original encoding, instead of the encoding set by
|
88
|
+
`Encoding.default_internal`. This works around a [bug][ruby-lang #16497]
|
89
|
+
in `StringIO` introduced in Ruby 2.7.0.
|
90
|
+
|
91
|
+
* The `remove_attachment` plugin now deletes the removed file if a new file was
|
92
|
+
attached right after removal.
|
93
|
+
|
94
|
+
## Backwards compatibility
|
95
|
+
|
96
|
+
* If you were passing a non-file IO object to the derivatives processor, Shrine
|
97
|
+
will now convert it into a file beforehand. If you're currently doing this
|
98
|
+
and are converting the IO object into a file inside the processor, you can
|
99
|
+
now remove the conversion code to avoid doubling the amount of disk writes.
|
100
|
+
|
101
|
+
* When reloading a Sequel/ActiveRecord model, any attacher state other than
|
102
|
+
uploaded files will now be retained after the reload. If you were relying on
|
103
|
+
all the attacher state being re-initialized, you'll need to update your code.
|
104
|
+
|
105
|
+
[ruby-lang #16497]: https://bugs.ruby-lang.org/issues/16497
|
data/doc/storage/s3.md
CHANGED
@@ -267,9 +267,15 @@ s3.open("key", sse_customer_algorithm: "AES256",
|
|
267
267
|
sse_customer_key_md5: "secret_key_md5")
|
268
268
|
```
|
269
269
|
|
270
|
-
|
271
|
-
|
272
|
-
|
270
|
+
**Client-side** encryption is supported as well:
|
271
|
+
|
272
|
+
```rb
|
273
|
+
encryption_client = Aws::S3::EncryptionV2::Client.new(...)
|
274
|
+
s3 = Shrine::Storage::S3.new(client: encryption_client, **other_options)
|
275
|
+
|
276
|
+
s3.upload(io, "key") # encrypts on upload
|
277
|
+
s3.open("key") # decrypts on download
|
278
|
+
```
|
273
279
|
|
274
280
|
## Accelerate endpoint
|
275
281
|
|
@@ -317,5 +323,3 @@ s3.clear! { |object| object.last_modified < Time.now - 7*24*60*60 }
|
|
317
323
|
[credentials]: https://docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/setup-config.html
|
318
324
|
[`Aws::S3::Object#presigned_post`]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#presigned_post-instance_method
|
319
325
|
[`Aws::S3::Object#presigned_url`]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#presigned_url-instance_method
|
320
|
-
[#348]: https://github.com/shrinerb/shrine/issues/348
|
321
|
-
[client-side encryption workaround]: https://github.com/shrinerb/shrine/issues/348#issuecomment-486445382
|
data/doc/upgrading_to_3.md
CHANGED
@@ -323,15 +323,17 @@ else
|
|
323
323
|
end
|
324
324
|
```
|
325
325
|
|
326
|
-
With `derivatives`, the original file is automatically downloaded and retained
|
327
|
-
so the
|
326
|
+
With `derivatives`, the original file is automatically downloaded and retained
|
327
|
+
during processing, so the setup is simpler:
|
328
328
|
|
329
329
|
```rb
|
330
|
-
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
|
331
333
|
```
|
332
334
|
```rb
|
333
335
|
class ImageUploader < Shrine
|
334
|
-
Attacher.
|
336
|
+
Attacher.derivatives do |original|
|
335
337
|
magick = ImageProcessing::MiniMagick.source(original)
|
336
338
|
|
337
339
|
# the :original file should NOT be included anymore
|
@@ -343,35 +345,17 @@ class ImageUploader < Shrine
|
|
343
345
|
end
|
344
346
|
end
|
345
347
|
```
|
346
|
-
|
347
|
-
However, you now need to trigger processing manually during attachment:
|
348
|
-
|
349
348
|
```rb
|
350
349
|
photo = Photo.new(photo_params)
|
351
350
|
|
352
351
|
if photo.valid?
|
353
|
-
photo.
|
354
|
-
photo.save
|
352
|
+
photo.save # creates derivatives on promotion
|
355
353
|
# ...
|
356
354
|
else
|
357
355
|
# ...
|
358
356
|
end
|
359
357
|
```
|
360
358
|
|
361
|
-
### Automatic processing
|
362
|
-
|
363
|
-
If you prefer processing to happen automatically with promotion (like it did
|
364
|
-
with the `versions` plugin), you can put the following in your initializer:
|
365
|
-
|
366
|
-
```rb
|
367
|
-
class Shrine::Attacher
|
368
|
-
def promote(*)
|
369
|
-
create_derivatives
|
370
|
-
super
|
371
|
-
end
|
372
|
-
end
|
373
|
-
```
|
374
|
-
|
375
359
|
### Accessing derivatives
|
376
360
|
|
377
361
|
The derivative URLs are accessed in the same way as versions:
|
@@ -475,11 +459,11 @@ creating another derivatives processor that you will trigger in the controller:
|
|
475
459
|
|
476
460
|
```rb
|
477
461
|
class ImageUploader < Shrine
|
478
|
-
Attacher.
|
462
|
+
Attacher.derivatives do |original|
|
479
463
|
# this will be triggered in the background job
|
480
464
|
end
|
481
465
|
|
482
|
-
Attacher.
|
466
|
+
Attacher.derivatives :foreground do |original|
|
483
467
|
# this will be triggered in the controller
|
484
468
|
end
|
485
469
|
end
|
@@ -586,7 +570,7 @@ you should now add the processed file as a derivative:
|
|
586
570
|
class MyUploader < Shrine
|
587
571
|
plugin :derivatives
|
588
572
|
|
589
|
-
Attacher.
|
573
|
+
Attacher.derivatives do |original|
|
590
574
|
magick = ImageProcessing::MiniMagick.source(original)
|
591
575
|
|
592
576
|
{ normalized: magick.resize_to_limit!(1600, 1600) }
|
@@ -665,7 +649,7 @@ attacher.copy(other_attacher)
|
|
665
649
|
with
|
666
650
|
|
667
651
|
```rb
|
668
|
-
attacher.
|
652
|
+
attacher.set attacher.upload(other_attacher.file)
|
669
653
|
attacher.add_derivatives other_attacher.derivatives # if using derivatives
|
670
654
|
```
|
671
655
|
|
data/lib/shrine/attacher.rb
CHANGED
@@ -217,7 +217,7 @@ class Shrine
|
|
217
217
|
# attacher.file #=> #<Shrine::UploadedFile>
|
218
218
|
# attacher.changed? #=> true
|
219
219
|
def change(file)
|
220
|
-
@previous = dup
|
220
|
+
@previous = dup if change?(file)
|
221
221
|
set(file)
|
222
222
|
end
|
223
223
|
|
@@ -373,6 +373,11 @@ class Shrine
|
|
373
373
|
attached? && !cached?
|
374
374
|
end
|
375
375
|
|
376
|
+
# Whether assigning the given file is considered a change.
|
377
|
+
def change?(file)
|
378
|
+
@file != file
|
379
|
+
end
|
380
|
+
|
376
381
|
# Returns whether the file is uploaded to specified storage.
|
377
382
|
def uploaded?(file, storage_key)
|
378
383
|
file&.storage_key == storage_key
|
@@ -9,8 +9,8 @@ class Shrine
|
|
9
9
|
end
|
10
10
|
|
11
11
|
module ClassMethods
|
12
|
-
def add_metadata(name = nil, &block)
|
13
|
-
opts[:add_metadata][:definitions] << [name, block]
|
12
|
+
def add_metadata(name = nil, **options, &block)
|
13
|
+
opts[:add_metadata][:definitions] << [name, options, block]
|
14
14
|
|
15
15
|
metadata_method(name) if name
|
16
16
|
end
|
@@ -40,10 +40,12 @@ class Shrine
|
|
40
40
|
private
|
41
41
|
|
42
42
|
def extract_custom_metadata(io, **options)
|
43
|
-
opts[:add_metadata][:definitions].each do |name, block|
|
43
|
+
opts[:add_metadata][:definitions].each do |name, definition_options, block|
|
44
44
|
result = instance_exec(io, **options, &block)
|
45
45
|
|
46
|
-
if
|
46
|
+
if result.nil? && definition_options[:skip_nil]
|
47
|
+
# Do not store this metadata
|
48
|
+
elsif name
|
47
49
|
options[:metadata].merge! name.to_s => result
|
48
50
|
else
|
49
51
|
options[:metadata].merge! result.transform_keys(&:to_s) if result
|