shrine 3.4.0 → 3.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +96 -0
- data/README.md +11 -11
- data/doc/advantages.md +1 -1
- data/doc/attacher.md +1 -1
- data/doc/carrierwave.md +2 -2
- data/doc/changing_derivatives.md +2 -1
- data/doc/changing_location.md +19 -6
- data/doc/design.md +4 -4
- data/doc/external/articles.md +16 -16
- data/doc/external/extensions.md +1 -1
- data/doc/getting_started.md +73 -30
- data/doc/multiple_files.md +57 -22
- data/doc/plugins/activerecord.md +91 -0
- data/doc/plugins/backgrounding.md +30 -4
- data/doc/plugins/data_uri.md +15 -0
- data/doc/plugins/derivation_endpoint.md +39 -1
- data/doc/plugins/derivatives.md +13 -2
- data/doc/plugins/download_endpoint.md +16 -4
- data/doc/plugins/dynamic_storage.md +15 -0
- data/doc/plugins/fallback_storage.md +90 -0
- data/doc/plugins/keep_files.md +6 -4
- data/doc/plugins/rack_response.md +9 -0
- data/doc/plugins/refresh_metadata.md +20 -0
- data/doc/plugins/sequel.md +57 -0
- data/doc/plugins/signature.md +8 -6
- data/doc/plugins/tempfile.md +14 -6
- data/doc/plugins/upload_options.md +12 -0
- data/doc/plugins/url_options.md +12 -0
- data/doc/plugins/validation_helpers.md +1 -1
- data/doc/processing.md +5 -3
- data/doc/refile.md +3 -3
- data/doc/release_notes/2.1.0.md +1 -1
- data/doc/release_notes/3.10.0.md +30 -0
- data/doc/release_notes/3.5.0.md +63 -0
- data/doc/release_notes/3.6.0.md +23 -0
- data/doc/release_notes/3.7.0.md +75 -0
- data/doc/release_notes/3.7.1.md +31 -0
- data/doc/release_notes/3.8.0.md +37 -0
- data/doc/release_notes/3.9.0.md +42 -0
- data/doc/retrieving_uploads.md +1 -1
- data/doc/storage/s3.md +10 -0
- data/doc/testing.md +45 -17
- data/doc/upgrading_to_3.md +3 -5
- data/lib/shrine/attacher.rb +41 -29
- data/lib/shrine/attachment.rb +2 -2
- data/lib/shrine/plugins/_persistence.rb +1 -1
- data/lib/shrine/plugins/_urlsafe_serialization.rb +4 -4
- data/lib/shrine/plugins/activerecord.rb +25 -4
- data/lib/shrine/plugins/add_metadata.rb +2 -4
- data/lib/shrine/plugins/atomic_helpers.rb +7 -7
- data/lib/shrine/plugins/backgrounding.rb +10 -10
- data/lib/shrine/plugins/column.rb +7 -5
- data/lib/shrine/plugins/data_uri.rb +28 -3
- data/lib/shrine/plugins/default_url.rb +4 -4
- data/lib/shrine/plugins/delete_raw.rb +2 -2
- data/lib/shrine/plugins/derivation_endpoint.rb +65 -59
- data/lib/shrine/plugins/derivatives.rb +35 -21
- data/lib/shrine/plugins/download_endpoint.rb +72 -11
- data/lib/shrine/plugins/entity.rb +15 -9
- data/lib/shrine/plugins/fallback_storage.rb +51 -0
- data/lib/shrine/plugins/infer_extension.rb +5 -1
- data/lib/shrine/plugins/instrumentation.rb +13 -9
- data/lib/shrine/plugins/metadata_attributes.rb +1 -1
- data/lib/shrine/plugins/mirroring.rb +10 -10
- data/lib/shrine/plugins/model.rb +12 -10
- data/lib/shrine/plugins/presign_endpoint.rb +13 -10
- data/lib/shrine/plugins/pretty_location.rb +2 -2
- data/lib/shrine/plugins/processing.rb +3 -3
- data/lib/shrine/plugins/rack_file.rb +2 -2
- data/lib/shrine/plugins/rack_response.rb +12 -6
- data/lib/shrine/plugins/refresh_metadata.rb +6 -6
- data/lib/shrine/plugins/remote_url.rb +4 -4
- data/lib/shrine/plugins/remove_attachment.rb +2 -0
- data/lib/shrine/plugins/remove_invalid.rb +4 -0
- data/lib/shrine/plugins/restore_cached_data.rb +3 -3
- data/lib/shrine/plugins/sequel.rb +15 -1
- data/lib/shrine/plugins/signature.rb +2 -2
- data/lib/shrine/plugins/store_dimensions.rb +2 -2
- data/lib/shrine/plugins/tempfile.rb +0 -2
- data/lib/shrine/plugins/upload_endpoint.rb +7 -5
- data/lib/shrine/plugins/upload_options.rb +18 -4
- data/lib/shrine/plugins/url_options.rb +17 -3
- data/lib/shrine/plugins/validation.rb +8 -8
- data/lib/shrine/plugins/validation_helpers.rb +2 -2
- data/lib/shrine/plugins/versions.rb +10 -10
- data/lib/shrine/plugins.rb +6 -14
- data/lib/shrine/storage/file_system.rb +15 -19
- data/lib/shrine/storage/linter.rb +8 -8
- data/lib/shrine/storage/memory.rb +1 -3
- data/lib/shrine/storage/s3.rb +60 -40
- data/lib/shrine/uploaded_file.rb +23 -19
- data/lib/shrine/version.rb +1 -1
- data/lib/shrine.rb +24 -20
- data/shrine.gemspec +10 -8
- metadata +52 -26
data/doc/plugins/keep_files.md
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
title: Keep Files
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
The [`keep_files`][keep_files] plugin prevents file
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
The [`keep_files`][keep_files] plugin prevents the attached file (and any of
|
|
6
|
+
its [derivatives]) from being deleted when the attachment would normally be
|
|
7
|
+
destroyed, which happens when the attachment is removed/replaced, or when the
|
|
8
|
+
record is deleted. This functionality is useful when implementing soft deletes,
|
|
9
|
+
versioning, or in general any scenario where you need to keep history.
|
|
9
10
|
|
|
10
11
|
```rb
|
|
11
12
|
plugin :keep_files
|
|
@@ -17,3 +18,4 @@ photo.image.exists? #=> true
|
|
|
17
18
|
```
|
|
18
19
|
|
|
19
20
|
[keep_files]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/keep_files.rb
|
|
21
|
+
[derivatives]: https://shrinerb.com/docs/plugins/derivatives
|
|
@@ -95,6 +95,15 @@ headers["Content-Range"] #=> "bytes 100-200/1000"
|
|
|
95
95
|
body # partial content
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
+
## ETag
|
|
99
|
+
|
|
100
|
+
A custom ETag value can be provided via the `:etag` option:
|
|
101
|
+
|
|
102
|
+
```rb
|
|
103
|
+
response = uploaded_file.to_rack_response(etag: "my-custom-etag")
|
|
104
|
+
response[1]["ETag"] #=> "my-custom-etag"
|
|
105
|
+
```
|
|
106
|
+
|
|
98
107
|
## Download options
|
|
99
108
|
|
|
100
109
|
The `#to_rack_response` method will automatically open the `UploadedFile` if it
|
|
@@ -69,5 +69,25 @@ Any options passed in will be forwarded to metadata extraction:
|
|
|
69
69
|
uploaded_file.refresh_metadata!(foo: "bar") # passes `{ foo: "bar" }` options to metadata extraction
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
## Replacing Metadata
|
|
73
|
+
|
|
74
|
+
By default the `#refresh_metadata!` method will merge the results into any existing metadata.
|
|
75
|
+
|
|
76
|
+
```rb
|
|
77
|
+
uploaded_file.metadata["custom"] = "custom value"
|
|
78
|
+
uploaded_file.refresh_metadata!
|
|
79
|
+
uploaded_file.metadata
|
|
80
|
+
# returns {"filename"=>"example.jpg", "size"=>1024, "mime_type"=>"image/jpeg", "custom"=>"custom value"}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Passing `replace: true` will instead fully overwrite the existing metadata with the new metadata.
|
|
84
|
+
|
|
85
|
+
```rb
|
|
86
|
+
uploaded_file.metadata["custom"] = "custom value"
|
|
87
|
+
uploaded_file.refresh_metadata!(replace: true)
|
|
88
|
+
uploaded_file.metadata
|
|
89
|
+
# returns {"filename"=>"example.jpg", "size"=>1024, "mime_type"=>"image/jpeg"}
|
|
90
|
+
```
|
|
91
|
+
|
|
72
92
|
[refresh_metadata]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/refresh_metadata.rb
|
|
73
93
|
[model]: https://shrinerb.com/docs/plugins/model
|
data/doc/plugins/sequel.md
CHANGED
|
@@ -100,6 +100,63 @@ set `:hooks` to `false`:
|
|
|
100
100
|
plugin :sequel, hooks: false
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
#### Duplicating records
|
|
104
|
+
|
|
105
|
+
Since a record being created can't yet have a confirmed attachment of its own
|
|
106
|
+
to safely replace, Shrine never deletes the previous file when the attachment
|
|
107
|
+
changes as part of *creating* a record, only when *updating* one.
|
|
108
|
+
|
|
109
|
+
Note that Sequel's `#dup`/`#clone`, unlike Active Record's, don't reset the
|
|
110
|
+
primary key or persistence state — a duplicated record still refers to the
|
|
111
|
+
*same* row, so saving it just updates that row rather than inserting a new
|
|
112
|
+
one. There's only ever one row here, so there's nothing to protect:
|
|
113
|
+
|
|
114
|
+
```rb
|
|
115
|
+
photo = Photo.create(image: file)
|
|
116
|
+
photo2 = photo.dup # `photo2` refers to the same row as `photo`
|
|
117
|
+
|
|
118
|
+
photo2.update(image: new_file)
|
|
119
|
+
photo.image.exists? #=> false (the row now has `new_file`, so this is expected)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
The common way to duplicate a record in Sequel is instead to construct a new
|
|
123
|
+
one from the original's values, which *does* produce a genuinely new,
|
|
124
|
+
unpersisted row, and so *is* protected:
|
|
125
|
+
|
|
126
|
+
```rb
|
|
127
|
+
photo2 = Photo.new(photo.values.except(:id))
|
|
128
|
+
|
|
129
|
+
photo2.update(image: new_file)
|
|
130
|
+
photo.image.exists? #=> true (not affected)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Keep in mind this only protects against *replacing* the attachment on create.
|
|
134
|
+
As long as `photo` and `photo2` continue to reference the same underlying
|
|
135
|
+
file (i.e. `photo2` is saved without ever changing its attachment),
|
|
136
|
+
destroying either record will still delete the file the other one
|
|
137
|
+
references, since Shrine has no way of knowing the file is shared:
|
|
138
|
+
|
|
139
|
+
```rb
|
|
140
|
+
photo2.save # still references the same file as `photo`
|
|
141
|
+
|
|
142
|
+
photo2.destroy
|
|
143
|
+
photo.image.exists? #=> false
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
If you want `photo2` to have its own independent copy of the file from the
|
|
147
|
+
start, so that destroying either record is also safe, upload a new copy
|
|
148
|
+
explicitly:
|
|
149
|
+
|
|
150
|
+
```rb
|
|
151
|
+
photo2 = Photo.new(photo.values.reject { |k, _| k == :id })
|
|
152
|
+
photo2.image_attacher.set(nil)
|
|
153
|
+
photo2.image_attacher.attach(photo.image, storage: photo.image.storage_key)
|
|
154
|
+
photo2.save
|
|
155
|
+
|
|
156
|
+
photo2.destroy # no longer affects `photo`
|
|
157
|
+
photo.image.exists? #=> true
|
|
158
|
+
```
|
|
159
|
+
|
|
103
160
|
### Validations
|
|
104
161
|
|
|
105
162
|
If you're using the [`validation`][validation] plugin, the attachment module
|
data/doc/plugins/signature.md
CHANGED
|
@@ -78,15 +78,17 @@ plugin :signature
|
|
|
78
78
|
Calculating signature will trigger a `signature.shrine` event with the
|
|
79
79
|
following payload:
|
|
80
80
|
|
|
81
|
-
| Key
|
|
82
|
-
| :--
|
|
83
|
-
| `:
|
|
84
|
-
| `:
|
|
81
|
+
| Key | Description |
|
|
82
|
+
| :-- | :---- |
|
|
83
|
+
| `:algorithm` | The hashing algorithm used |
|
|
84
|
+
| `:format` | The encoding format |
|
|
85
|
+
| `:io` | The IO object |
|
|
86
|
+
| `:uploader` | The uploader class that sent the event |
|
|
85
87
|
|
|
86
88
|
A default log subscriber is added as well which logs these events:
|
|
87
89
|
|
|
88
90
|
```
|
|
89
|
-
|
|
91
|
+
Signature (1ms) – {io: StringIO, algorithm: :md5, format: :hex, uploader: Shrine}
|
|
90
92
|
```
|
|
91
93
|
|
|
92
94
|
You can also use your own log subscriber:
|
|
@@ -97,7 +99,7 @@ plugin :signature, log_subscriber: -> (event) {
|
|
|
97
99
|
}
|
|
98
100
|
```
|
|
99
101
|
```
|
|
100
|
-
{"name":"signature","duration":24,"io":"#<StringIO:0x00007fb7c5b08b80>","uploader":"Shrine"}
|
|
102
|
+
{"name":"signature","duration":24,"io":"#<StringIO:0x00007fb7c5b08b80>","algorithm":"sha512","format":"hex","uploader":"Shrine"}
|
|
101
103
|
```
|
|
102
104
|
|
|
103
105
|
Or disable logging altogether:
|
data/doc/plugins/tempfile.md
CHANGED
|
@@ -9,14 +9,22 @@ uploaded file on disk.
|
|
|
9
9
|
Shrine.plugin :tempfile
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
The plugin provides the `UploadedFile#tempfile` method, which
|
|
13
|
-
|
|
14
|
-
the
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
closed
|
|
12
|
+
The plugin provides the `UploadedFile#tempfile` method, which returns a copy
|
|
13
|
+
of the uploaded file's content on disk. The first time the method is called
|
|
14
|
+
the file content will be downloaded into a temporary file and returned. On
|
|
15
|
+
any subsequent method calls the cached temporary file will be returned
|
|
16
|
+
directly. If the uploaded file is currently open, its tempfile is deleted
|
|
17
|
+
when the uploaded file is closed; otherwise it's deleted whenever it becomes
|
|
18
|
+
unreachable and is garbage collected (so it's still recommended to close the
|
|
19
|
+
uploaded file when you're done with it, to have the tempfile cleaned up
|
|
20
|
+
deterministically).
|
|
18
21
|
|
|
19
22
|
```rb
|
|
23
|
+
uploaded_file.tempfile #=> #<Tempfile:...> (file is downloaded and cached)
|
|
24
|
+
uploaded_file.tempfile #=> #<Tempfile:...> (cache is returned)
|
|
25
|
+
|
|
26
|
+
# OR
|
|
27
|
+
|
|
20
28
|
uploaded_file.open do
|
|
21
29
|
# ...
|
|
22
30
|
uploaded_file.tempfile #=> #<Tempfile:...> (file is cached)
|
|
@@ -29,4 +29,16 @@ the uploader.
|
|
|
29
29
|
uploader.upload(file, upload_options: { acl: "public-read" })
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
If storage keys are generated dynamically (e.g. via the
|
|
33
|
+
[`dynamic_storage`][dynamic_storage] plugin), it's not possible to list every
|
|
34
|
+
storage key upfront. In that case you can use a `Regexp` instead, which will
|
|
35
|
+
be matched against the storage key:
|
|
36
|
+
|
|
37
|
+
```rb
|
|
38
|
+
plugin :upload_options, /_store\z/ => { acl: "private" }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
An exact storage key match always takes precedence over a `Regexp` match.
|
|
42
|
+
|
|
32
43
|
[upload_options]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/upload_options.rb
|
|
44
|
+
[dynamic_storage]: https://shrinerb.com/docs/plugins/dynamic_storage
|
data/doc/plugins/url_options.md
CHANGED
|
@@ -24,4 +24,16 @@ In both cases the default options are merged with options passed to
|
|
|
24
24
|
`UploadedFile#url`, and the latter will always have precedence over default
|
|
25
25
|
options.
|
|
26
26
|
|
|
27
|
+
If storage keys are generated dynamically (e.g. via the
|
|
28
|
+
[`dynamic_storage`][dynamic_storage] plugin), it's not possible to list every
|
|
29
|
+
storage key upfront. In that case you can use a `Regexp` instead, which will
|
|
30
|
+
be matched against the storage key:
|
|
31
|
+
|
|
32
|
+
```rb
|
|
33
|
+
plugin :url_options, /_store\z/ => { expires_in: 24*60*60 }
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
An exact storage key match always takes precedence over a `Regexp` match.
|
|
37
|
+
|
|
27
38
|
[url_options]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/url_options.rb
|
|
39
|
+
[dynamic_storage]: https://shrinerb.com/docs/plugins/dynamic_storage
|
data/doc/processing.md
CHANGED
|
@@ -18,7 +18,8 @@ $ brew install imagemagick
|
|
|
18
18
|
```
|
|
19
19
|
```rb
|
|
20
20
|
# Gemfile
|
|
21
|
-
gem "image_processing", "~>
|
|
21
|
+
gem "image_processing", "~> 2.0"
|
|
22
|
+
gem "mini_magick", "~> 5.0"
|
|
22
23
|
```
|
|
23
24
|
```rb
|
|
24
25
|
require "image_processing/mini_magick"
|
|
@@ -405,7 +406,7 @@ Shrine integration, the ImageProcessing gem that we saw earlier is a completely
|
|
|
405
406
|
generic gem.
|
|
406
407
|
|
|
407
408
|
To demonstrate, here is an example of transcoding videos using
|
|
408
|
-
[streamio-ffmpeg]:
|
|
409
|
+
[streamio-ffmpeg][streamio-ffmpeg]:
|
|
409
410
|
|
|
410
411
|
```rb
|
|
411
412
|
# Gemfile
|
|
@@ -502,7 +503,8 @@ $ brew install vips
|
|
|
502
503
|
|
|
503
504
|
```rb
|
|
504
505
|
# Gemfile
|
|
505
|
-
gem "image_processing", "~>
|
|
506
|
+
gem "image_processing", "~> 2.0"
|
|
507
|
+
gem "ruby-vips", "~> 2.3"
|
|
506
508
|
```
|
|
507
509
|
|
|
508
510
|
```rb
|
data/doc/refile.md
CHANGED
|
@@ -458,7 +458,7 @@ Shrine.plugin :cached_attachment_data
|
|
|
458
458
|
```
|
|
459
459
|
```rb
|
|
460
460
|
form_for @user do |form|
|
|
461
|
-
form.hidden_field :profile_image, value: @user.cached_profile_image_data
|
|
461
|
+
form.hidden_field :profile_image, value: @user.cached_profile_image_data, id: nil
|
|
462
462
|
form.file_field :profile_image
|
|
463
463
|
end
|
|
464
464
|
```
|
|
@@ -475,7 +475,7 @@ Shrine.plugin :remove_attachment
|
|
|
475
475
|
```
|
|
476
476
|
```rb
|
|
477
477
|
form_for @user do |form|
|
|
478
|
-
form.hidden_field :profile_image, value: @user.cached_profile_image_data
|
|
478
|
+
form.hidden_field :profile_image, value: @user.cached_profile_image_data, id: nil
|
|
479
479
|
form.file_field :profile_image
|
|
480
480
|
form.check_box :remove_profile_image
|
|
481
481
|
end
|
|
@@ -491,7 +491,7 @@ Shrine.plugin :remote_url
|
|
|
491
491
|
```
|
|
492
492
|
```rb
|
|
493
493
|
form_for @user do |form|
|
|
494
|
-
form.hidden_field :profile_image, value: @user.cached_profile_image_data
|
|
494
|
+
form.hidden_field :profile_image, value: @user.cached_profile_image_data, id: nil
|
|
495
495
|
form.file_field :profile_image
|
|
496
496
|
form.text_field :profile_image_remote_url
|
|
497
497
|
end
|
data/doc/release_notes/2.1.0.md
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Shrine 3.10.0
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## New features
|
|
6
|
+
|
|
7
|
+
* The new `fallback_storage` plugin makes Shrine read from a secondary (fallback) storage when a file is missing in the primary storage. This is useful for staging or development environments that should read files from production without copying them. `UploadedFile#exists?`, `#url` and `#open` (and methods relying on it) fall back to the secondary storage, while uploads and deletes only affect the primary storage.
|
|
8
|
+
|
|
9
|
+
```rb
|
|
10
|
+
Shrine.storages = {
|
|
11
|
+
cache: Shrine::Storage::S3.new(endpoint: "https://stage.example.com", prefix: "cache"),
|
|
12
|
+
store: Shrine::Storage::S3.new(endpoint: "https://stage.example.com"),
|
|
13
|
+
fallback: Shrine::Storage::S3.new(endpoint: "https://production.example.com"),
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
Shrine.plugin :fallback_storage # looks for the `:fallback` storage by default
|
|
17
|
+
Shrine.plugin :fallback_storage, store: :production_store # custom storage name
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
* The `data_uri` plugin now accepts a `:max_size` option for limiting the size of the data URI content. The size is calculated from the data URI before the content is decoded, so oversized content never gets loaded into memory. It's exact for base64 content, while for percent-encoded content the encoded length is used, which can overestimate. Content over the limit fails parsing the same way as an invalid data URI.
|
|
21
|
+
|
|
22
|
+
```rb
|
|
23
|
+
plugin :data_uri, max_size: 10*1024*1024 # 10 MB
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
* The `data_uri` plugin's `:error_message` proc can now optionally accept the error as a second argument, so the size limit can have its own message.
|
|
27
|
+
|
|
28
|
+
```rb
|
|
29
|
+
plugin :data_uri, error_message: ->(uri, error) { I18n.t("errors.data_uri.#{error.message}") }
|
|
30
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Shrine 3.5.0
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## New features
|
|
6
|
+
|
|
7
|
+
* The website has been migrated to Docusaurus v2. :sparkles:
|
|
8
|
+
|
|
9
|
+
* The `:signer` option has been added to the `derivation_endpoint` plugin, for when you want to use custom URL signing. This is useful when using `:expires_in`, and wanting to have expiring URLs work with CDN caching.
|
|
10
|
+
|
|
11
|
+
```rb
|
|
12
|
+
require "aws-sdk-cloudfront"
|
|
13
|
+
signer = Aws::CloudFront::UrlSigner.new(key_pair_id: "...", private_key: "...")
|
|
14
|
+
|
|
15
|
+
plugin :derivation_endpoint,
|
|
16
|
+
expires_in: 90,
|
|
17
|
+
signer: -> (url, expires_in:) do
|
|
18
|
+
signer.signed_url(url, expires: Time.now.to_i + expires_in)
|
|
19
|
+
end
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
* The S3 storage now supports `:max_multipart_parts` option for specifying the maximum number of concurrent parts in which a large file will get uploaded. This number defaults to `10_000`.
|
|
23
|
+
|
|
24
|
+
```rb
|
|
25
|
+
Shrine::Storage::S3.new(max_multipart_parts: 1000, ...)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
* The `:encoding` option can now be passed to `S3#open`, which is applied to downloaded chunks.
|
|
29
|
+
|
|
30
|
+
```rb
|
|
31
|
+
io = uploaded_file.open(encoding: Encoding::UTF_8)
|
|
32
|
+
csv = CSV.new(io)
|
|
33
|
+
# ...
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Other improvements
|
|
37
|
+
|
|
38
|
+
* Passing a boolean value to the `#remove_attachment=` setter now works on Ruby 3.2. Previously this would raise an error, because Shrine would try to call `=~` on it, but `Object#=~` method has been removed in Ruby 3.2.
|
|
39
|
+
|
|
40
|
+
* When duplicating a model instance, the duplicated attacher now references the duplicated model instance instead of the original one.
|
|
41
|
+
|
|
42
|
+
* The download endpoint now returns a `400 Bad Request` response when the serialized file component is invalid.
|
|
43
|
+
|
|
44
|
+
* The `derivatives` plugin now supports passing `mutex: false` option to disable usage of a mutex. This makes the `Shrine::Attacher` object marshallable, which should enable using `Marshal.dump` and `Marshal.load` on model instances with attachments. This should be safe unless you're adding derivatives on the same attacher object concurrently.
|
|
45
|
+
|
|
46
|
+
* When loading the `derivatives` plugin with `versions_compatibility: true`, this setting doesn't leak to other uploaders anymore. Previously if other uploaders would load `derivatives` plugin without this option, versions compatibility would still get enabled for them. This change also fixes behavior on JRuby.
|
|
47
|
+
|
|
48
|
+
* When S3 storage copies files, the AWS tag are not inherited anymore. This allows passing the `:tagging` upload option when promoting from temporary to permanent storage, and have it take effect.
|
|
49
|
+
|
|
50
|
+
* The `UploadedFile#url` method doesn't call the obsolete `URI.regexp` method anymore, which should avoid warnings.
|
|
51
|
+
|
|
52
|
+
* The `infer_extension` plugin now defines `infer_extension` instance method (in addition to class method) on the uploader for convenience, so that it can be easily called at the uploader instance level.
|
|
53
|
+
|
|
54
|
+
```rb
|
|
55
|
+
class MyUploader < Shrine
|
|
56
|
+
plugin :infer_extension
|
|
57
|
+
|
|
58
|
+
def generate_location(io, metadata:, **)
|
|
59
|
+
extension = infer_extension(metadata["mime_type"])
|
|
60
|
+
# ...
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Shrine 3.6.0
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## New features
|
|
6
|
+
|
|
7
|
+
* The S3 storage now accepts `:copy_options` when initializing. This can be used for supporting Cloudflare R2 by removing `:tagging_directive` when copying file from temporary to permanent storage.
|
|
8
|
+
|
|
9
|
+
```rb
|
|
10
|
+
Shrine::Storage::S3.new(bucket: BUCKET, copy_options: {}, **s3_options)
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Other improvements
|
|
14
|
+
|
|
15
|
+
* Rack 3 is now supported.
|
|
16
|
+
|
|
17
|
+
* When duplicating the attacher, the `Attacher#context` hash is now copied as well, instead of being kept the same between the two attachers.
|
|
18
|
+
|
|
19
|
+
* After `UploadedFile#close` was called, `UploadedFile#opened?` will return `false` and the uploaded file can be implicitly re-opened again.
|
|
20
|
+
|
|
21
|
+
## Backwards compatibility
|
|
22
|
+
|
|
23
|
+
* Shrine API that is returning a rack response triple will now return headers as an instance of `Rack::Headers` on Rack 3, which is a subclass of `Hash`. This should keep user code that references header names in mixed case working (in addition to lowercase), but could theoretically cause issues for code explicitly requiring headers to be an instance of `Hash`.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Shrine 3.7.0
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## New features
|
|
6
|
+
|
|
7
|
+
* The `download_endpoint` plugin now supports expiring URLs. Configure a `secret_key` and optionally a default `expires_in` on the plugin, then pass `expires_in:` when generating a URL. The URL is signed with HMAC-SHA256, and the endpoint will reject requests with an expired or tampered signature.
|
|
8
|
+
|
|
9
|
+
```rb
|
|
10
|
+
plugin :download_endpoint,
|
|
11
|
+
prefix: "downloads",
|
|
12
|
+
secret_key: "<your-secret-key>",
|
|
13
|
+
expires_in: 5 * 60 # optional default; can be overridden per URL
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```rb
|
|
17
|
+
uploaded_file.download_url # uses the default expires_in
|
|
18
|
+
uploaded_file.download_url(expires_in: 10 * 60) # override per URL
|
|
19
|
+
# => "https://example.com/downloads/<token>?signature=...&expires_at=..."
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
* The `derivatives` plugin now accepts a `:keep_derivatives` option. When set to `true`, existing derivatives are kept when a new file is attached via `Attacher#change`, instead of being cleared.
|
|
23
|
+
|
|
24
|
+
```rb
|
|
25
|
+
plugin :derivatives, keep_derivatives: true
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```rb
|
|
29
|
+
attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> }
|
|
30
|
+
attacher.change(new_file)
|
|
31
|
+
attacher.derivatives #=> { thumb: #<Shrine::UploadedFile> } # preserved
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
* The `refresh_metadata` plugin now accepts a `replace:` keyword argument on `refresh_metadata!`. Passing `replace: true` replaces the file's metadata entirely with the freshly extracted values, instead of merging them. This is useful when you want to remove stale custom metadata keys.
|
|
35
|
+
|
|
36
|
+
```rb
|
|
37
|
+
uploaded_file.metadata["custom"] = "stale value"
|
|
38
|
+
|
|
39
|
+
uploaded_file.refresh_metadata! # merge (default)
|
|
40
|
+
uploaded_file.metadata["custom"] #=> "stale value" (preserved)
|
|
41
|
+
|
|
42
|
+
uploaded_file.refresh_metadata!(replace: true) # replace
|
|
43
|
+
uploaded_file.metadata["custom"] #=> nil (removed)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Other improvements
|
|
47
|
+
|
|
48
|
+
* The `s3` storage now prefers using `TransferManager#upload_stream` over the deprecated `#upload_stream` method on the S3 object, when `TransferManager` is available. This avoids deprecation warnings from newer versions of the AWS SDK.
|
|
49
|
+
|
|
50
|
+
* The `column` plugin no longer attempts to deserialize an empty string as JSON. Previously this would raise a parse error; now the attachment is treated as blank.
|
|
51
|
+
|
|
52
|
+
* The `backgrounding` plugin now correctly forwards keyword arguments passed to `Attacher#promote_cached` into the `promote_block` callback.
|
|
53
|
+
|
|
54
|
+
```rb
|
|
55
|
+
Shrine::Attacher.promote_block do |attacher:, my_option:, **|
|
|
56
|
+
# my_option was previously not forwarded here
|
|
57
|
+
SomePromoteJob.perform_async(attacher.dump, my_option: my_option)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
attacher.promote_cached(my_option: "value") # now forwarded correctly
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
* `UploadedFile` no longer produces URI default parser warnings (`URI::RFC3986_PARSER.make_regexp is obsolete`) when verbose warnings are enabled.
|
|
64
|
+
|
|
65
|
+
## Backwards compatibility
|
|
66
|
+
|
|
67
|
+
* Support for Ruby versions below 3.2 has been dropped. Ruby >= 3.2 is now required.
|
|
68
|
+
|
|
69
|
+
* ImageProcessing 2.0 made `mini_magick` and `ruby-vips` soft dependencies that are no longer loaded automatically. If you use either gem for image processing, you will need to add it explicitly to your `Gemfile`:
|
|
70
|
+
|
|
71
|
+
```rb
|
|
72
|
+
gem "mini_magick"
|
|
73
|
+
# or
|
|
74
|
+
gem "ruby-vips"
|
|
75
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Shrine 3.7.1
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## New features
|
|
6
|
+
|
|
7
|
+
* The `derivation_endpoint` plugin now supports adding a file extension to the derivation URL path via the `:format` option on `UploadedFile#derivation_url`. Some HTTP clients and CDNs use the URL path extension to determine the content type of the response.
|
|
8
|
+
|
|
9
|
+
```rb
|
|
10
|
+
uploaded_file.derivation_url(:thumbnail, format: "jpg")
|
|
11
|
+
#=> ".../thumbnail/eyJpZCI6ImZvbyIsInN.jpg?signature=..."
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The extension is included in the URL signature, so it cannot be tampered with.
|
|
15
|
+
|
|
16
|
+
* The `rack_response` plugin now accepts an `:etag` option for setting a custom `ETag` header, overriding the default one Shrine generates.
|
|
17
|
+
|
|
18
|
+
```rb
|
|
19
|
+
response = uploaded_file.to_rack_response(etag: "my-custom-etag")
|
|
20
|
+
response[1]["ETag"] #=> "my-custom-etag"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Bug fixes
|
|
24
|
+
|
|
25
|
+
* When initializing Shrine with the `activerecord` plugin, under Bootsnap 1.24.5 and Ruby 3.4.5 this would raise an error:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
ArgumentError: wrong number of arguments (given 2, expected 1; required keyword: plugin)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The seems to be triggered by a special combination of method signatures, where keyword arguments get converted into positional arguments under Bootsnap. To work around this issue, method signatures of some plugin methods have been updated.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Shrine 3.8.0
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## Security
|
|
6
|
+
|
|
7
|
+
* The `file_system` storage now prevents path traversal outside of the storage
|
|
8
|
+
directory. Previously, an id containing `../` sequences (e.g. coming from
|
|
9
|
+
attacker-controlled data) could resolve to a location outside of the
|
|
10
|
+
configured storage directory. Now `Shrine::Error` is raised whenever an id
|
|
11
|
+
would resolve outside of the storage directory:
|
|
12
|
+
|
|
13
|
+
```rb
|
|
14
|
+
storage = Shrine::Storage::FileSystem.new("uploads")
|
|
15
|
+
storage.open("../../etc/passwd") #~> Shrine::Error
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## New features
|
|
19
|
+
|
|
20
|
+
* The `derivatives` plugin now forwards options passed to `Attacher#promote`
|
|
21
|
+
into `Attacher#upload_derivatives`. This means options such as upload options
|
|
22
|
+
reach the derivatives upload as well, not just the main file.
|
|
23
|
+
|
|
24
|
+
```rb
|
|
25
|
+
attacher.promote(upload_options: { acl: "public-read" })
|
|
26
|
+
# the same options are now forwarded when uploading derivatives
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Other improvements
|
|
30
|
+
|
|
31
|
+
* The `s3` storage again uses single-request uploads for smaller files, instead
|
|
32
|
+
of always using multipart uploads. In Shrine 3.7.0, when a `TransferManager`
|
|
33
|
+
was available, it was used for uploads of any size, but `TransferManager`
|
|
34
|
+
uploads via multipart unconditionally, which is inefficient for smaller files.
|
|
35
|
+
Now files at or below the multipart threshold (`:upload`, 15MB by default) are
|
|
36
|
+
uploaded in a single request, and only larger files use multipart upload via
|
|
37
|
+
the `TransferManager` (falling back to the older API when it's not available).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Shrine 3.9.0
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## New features
|
|
6
|
+
|
|
7
|
+
* `Shrine.find_storage` now raises `Shrine::MissingStorage` (a subclass of `Shrine::Error`) when the storage isn't registered, instead of a generic `Shrine::Error`. This is useful with the `dynamic_storage` plugin, where the resolver might fail to find its dependencies (e.g. a deleted DB record), and lets you rescue that case separately from other errors.
|
|
8
|
+
|
|
9
|
+
```rb
|
|
10
|
+
begin
|
|
11
|
+
Shrine.find_storage(:store)
|
|
12
|
+
rescue Shrine::MissingStorage
|
|
13
|
+
# handle missing storage specifically
|
|
14
|
+
end
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
* The `url_options` and `upload_options` plugins now accept a `Regexp` in place of a storage key. This is useful when storage keys are generated dynamically (e.g. via the `dynamic_storage` plugin), since it's not possible to list every storage key upfront. An exact storage key match always takes precedence over a `Regexp` match.
|
|
18
|
+
|
|
19
|
+
```rb
|
|
20
|
+
plugin :url_options, /_store\z/ => { expires_in: 24*60*60 }
|
|
21
|
+
plugin :upload_options, /_store\z/ => { acl: "private" }
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
* The `activerecord` plugin now accepts an `:attribute_types` option. When enabled, it uses `type_for_attribute` (instead of just `columns_hash`) to detect whether the data attribute is JSON/JSONB, so Shrine also skips its own serialization for attributes declared via the Active Record Attributes API (not just real database columns). It defaults to `false` for backwards compatibility with apps that might be relying on the previous double-serialization behavior.
|
|
25
|
+
|
|
26
|
+
```rb
|
|
27
|
+
class Photo < ActiveRecord::Base # `image_data` is a text column
|
|
28
|
+
include ImageUploader::Attachment(:image)
|
|
29
|
+
|
|
30
|
+
attribute :image_data, :json
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
plugin :activerecord, attribute_types: true
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Bug fixes
|
|
37
|
+
|
|
38
|
+
* The `activerecord` and `sequel` plugins no longer delete the previous attachment when the record being saved was just created. Previously, duplicating a persisted record (e.g. via `#dup`) and immediately assigning a new attachment before saving the duplicate could end up deleting the *original* record's attachment, since both records shared the same underlying file.
|
|
39
|
+
|
|
40
|
+
* The `remove_invalid` plugin now correctly clears/reverts the model attribute after an invalid file is deassigned. Previously the record's raw attribute could be left out of sync with the attacher after validation failed.
|
|
41
|
+
|
|
42
|
+
* `UploadedFile#tempfile` (from the `tempfile` plugin) can now be called without opening the uploaded file first. Previously it raised `Shrine::Error` unless the file was already open; now it downloads and caches the file on demand. If the uploaded file is open, its tempfile is still cleaned up when the file is closed; otherwise it's cleaned up whenever it becomes unreachable and is garbage collected, so it's still recommended to close the uploaded file when you're done with it for deterministic cleanup.
|
data/doc/retrieving_uploads.md
CHANGED
|
@@ -124,7 +124,7 @@ end # underlying IO object is closed
|
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
`Shrine::UploadedFile#open` will return the result of a given block.
|
|
127
|
-
|
|
127
|
+
We can use that to safely retrieve the whole content of a file, without
|
|
128
128
|
leaving any temporary files lying around.
|
|
129
129
|
|
|
130
130
|
```rb
|
data/doc/storage/s3.md
CHANGED
|
@@ -124,6 +124,16 @@ uploader.upload(file, upload_options: { acl: "private" })
|
|
|
124
124
|
the uploader level won't be forwarded for generating presigns, since presigns
|
|
125
125
|
are generated using the storage directly.
|
|
126
126
|
|
|
127
|
+
## Copy options
|
|
128
|
+
|
|
129
|
+
If you wish to override options that are passed when copying objects from
|
|
130
|
+
temporary to permanent storage, you can pass `:copy_options`:
|
|
131
|
+
|
|
132
|
+
```rb
|
|
133
|
+
# Removes default :tagging_directive, which isn't supported by Cloudflare R2
|
|
134
|
+
Shrine::Storage::S3.new(copy_options: {}, **s3_options)
|
|
135
|
+
```
|
|
136
|
+
|
|
127
137
|
## URL Host
|
|
128
138
|
|
|
129
139
|
If you want your S3 object URLs to be generated with a different URL host (e.g.
|