shrine 3.0.1 → 3.3.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 +82 -0
- data/LICENSE.txt +1 -1
- data/README.md +15 -5
- data/doc/advantages.md +33 -16
- data/doc/attacher.md +2 -2
- data/doc/carrierwave.md +78 -34
- data/doc/changing_derivatives.md +39 -39
- data/doc/design.md +134 -85
- data/doc/direct_s3.md +1 -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 +177 -112
- data/doc/metadata.md +79 -43
- data/doc/multiple_files.md +6 -4
- data/doc/paperclip.md +119 -42
- data/doc/plugins/activerecord.md +1 -1
- 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/data_uri.md +2 -2
- data/doc/plugins/default_url.md +6 -3
- data/doc/plugins/derivation_endpoint.md +26 -28
- data/doc/plugins/derivatives.md +238 -171
- data/doc/plugins/determine_mime_type.md +2 -2
- data/doc/plugins/download_endpoint.md +5 -5
- data/doc/plugins/dynamic_storage.md +1 -1
- data/doc/plugins/form_assign.md +5 -5
- data/doc/plugins/included.md +25 -5
- data/doc/plugins/infer_extension.md +11 -2
- data/doc/plugins/instrumentation.md +1 -1
- data/doc/plugins/metadata_attributes.md +22 -10
- data/doc/plugins/mirroring.md +1 -1
- data/doc/plugins/persistence.md +11 -1
- data/doc/plugins/refresh_metadata.md +5 -4
- data/doc/plugins/remote_url.md +8 -3
- data/doc/plugins/remove_invalid.md +9 -1
- data/doc/plugins/signature.md +11 -2
- data/doc/plugins/store_dimensions.md +12 -2
- data/doc/plugins/type_predicates.md +96 -0
- data/doc/plugins/upload_endpoint.md +7 -11
- 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/validation_helpers.md +3 -3
- data/doc/plugins/versions.md +7 -7
- data/doc/processing.md +290 -127
- data/doc/refile.md +39 -18
- data/doc/release_notes/2.19.0.md +1 -1
- 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.0.1.md +4 -0
- data/doc/release_notes/3.1.0.md +73 -0
- 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/securing_uploads.md +3 -3
- data/doc/storage/file_system.md +1 -1
- data/doc/storage/memory.md +19 -0
- data/doc/storage/s3.md +105 -82
- data/doc/testing.md +2 -2
- data/doc/upgrading_to_3.md +97 -49
- data/doc/validation.md +3 -2
- data/lib/shrine.rb +8 -8
- data/lib/shrine/attacher.rb +24 -14
- data/lib/shrine/attachment.rb +5 -5
- data/lib/shrine/plugins.rb +22 -0
- data/lib/shrine/plugins/activerecord.rb +1 -1
- data/lib/shrine/plugins/add_metadata.rb +18 -7
- data/lib/shrine/plugins/backgrounding.rb +2 -2
- data/lib/shrine/plugins/default_storage.rb +6 -6
- data/lib/shrine/plugins/default_url.rb +1 -1
- data/lib/shrine/plugins/derivation_endpoint.rb +12 -7
- data/lib/shrine/plugins/derivatives.rb +61 -29
- data/lib/shrine/plugins/determine_mime_type.rb +3 -3
- data/lib/shrine/plugins/entity.rb +6 -6
- 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/signature.rb +7 -6
- data/lib/shrine/plugins/store_dimensions.rb +22 -11
- 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/uploaded_file.rb +0 -1
- data/lib/shrine/version.rb +2 -2
- data/shrine.gemspec +7 -8
- metadata +25 -31
data/doc/securing_uploads.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
---
|
2
2
|
id: securing-uploads
|
3
|
-
title: Securing
|
3
|
+
title: Securing Uploads
|
4
4
|
---
|
5
5
|
|
6
6
|
Shrine does a lot to make your file uploads secure, but there are still a lot
|
@@ -63,7 +63,7 @@ in the `:max_size` option to reject files that are larger than the specified
|
|
63
63
|
limit:
|
64
64
|
|
65
65
|
```rb
|
66
|
-
plugin :upload_endpoint, max_size: 100*1024*1024 #
|
66
|
+
plugin :upload_endpoint, max_size: 100*1024*1024 # 100 MB
|
67
67
|
```
|
68
68
|
|
69
69
|
If you're doing direct uploads to Amazon S3 using the `presign_endpoint`
|
@@ -151,7 +151,7 @@ processing:
|
|
151
151
|
```rb
|
152
152
|
class ImageUploader < Shrine
|
153
153
|
# ...
|
154
|
-
Attacher.
|
154
|
+
Attacher.derivatives do |original|
|
155
155
|
width, height = Shrine.dimensions(original)
|
156
156
|
|
157
157
|
fail ImageBombError if width > 5000 || height > 5000
|
data/doc/storage/file_system.md
CHANGED
@@ -0,0 +1,19 @@
|
|
1
|
+
---
|
2
|
+
title: Memory
|
3
|
+
---
|
4
|
+
|
5
|
+
The Memory storage stores uploaded files in memory, which is suitable for
|
6
|
+
testing.
|
7
|
+
|
8
|
+
```rb
|
9
|
+
Shrine.storages[:store] = Shrine::Storage::Memory.new
|
10
|
+
```
|
11
|
+
|
12
|
+
By default, each storage instance uses a new Hash object for storing files,
|
13
|
+
but you can pass your own:
|
14
|
+
|
15
|
+
```rb
|
16
|
+
my_store = Hash.new
|
17
|
+
|
18
|
+
Shrine.storages[:store] = Shrine::Storage::Memory.new(my_store)
|
19
|
+
```
|
data/doc/storage/s3.md
CHANGED
@@ -1,31 +1,41 @@
|
|
1
1
|
---
|
2
|
-
title:
|
2
|
+
title: AWS S3
|
3
3
|
---
|
4
4
|
|
5
|
-
The S3 storage handles uploads to
|
5
|
+
The S3 storage handles uploads to [AWS S3] service (or any s3-compatible
|
6
|
+
service such as [DigitalOcean Spaces] or [MinIO]). It requires the [aws-sdk-s3]
|
6
7
|
gem:
|
7
8
|
|
8
9
|
```rb
|
10
|
+
# Gemfile
|
9
11
|
gem "aws-sdk-s3", "~> 1.14"
|
10
12
|
```
|
11
13
|
|
12
|
-
|
14
|
+
## Initialization
|
15
|
+
|
16
|
+
The storage is initialized by providing your bucket name, region and
|
17
|
+
credentials:
|
13
18
|
|
14
19
|
```rb
|
15
20
|
require "shrine/storage/s3"
|
16
21
|
|
17
22
|
s3 = Shrine::Storage::S3.new(
|
18
23
|
bucket: "my-app", # required
|
24
|
+
region: "eu-west-1", # required
|
19
25
|
access_key_id: "abc",
|
20
26
|
secret_access_key: "xyz",
|
21
|
-
region: "eu-west-1",
|
22
27
|
)
|
23
28
|
```
|
24
29
|
|
25
|
-
The
|
26
|
-
|
27
|
-
|
28
|
-
|
30
|
+
> The storage requires the following AWS S3 permissions:
|
31
|
+
>
|
32
|
+
> * `s3:ListBucket` for the bucket resource
|
33
|
+
> * `s3:GetObject`, `s3:PutObject`, `s3:PutObjectAcl`, `s3:DeleteObject`,
|
34
|
+
> `s3:ListMultipartUploadParts` and `s3:AbortMultipartUpload` for the object
|
35
|
+
> resources
|
36
|
+
|
37
|
+
> The `:access_key_id` and `:secret_access_key` options is just one form of
|
38
|
+
> authentication, see the [AWS SDK docs][credentials] for more options.
|
29
39
|
|
30
40
|
The storage exposes the underlying Aws objects:
|
31
41
|
|
@@ -45,14 +55,29 @@ s3.object("key") #=> #<Aws::S3::Object>
|
|
45
55
|
|
46
56
|
By default, uploaded S3 objects will have private visibility, meaning they can
|
47
57
|
only be accessed via signed expiring URLs generated using your private S3
|
48
|
-
credentials.
|
49
|
-
to make uploads public:
|
58
|
+
credentials.
|
50
59
|
|
51
60
|
```rb
|
52
|
-
s3 = Shrine::Storage::S3.new(
|
61
|
+
s3 = Shrine::Storage::S3.new(**s3_options)
|
62
|
+
s3.upload(io, "key") # uploads with default "private" ACL
|
63
|
+
s3.url("key") # https://my-bucket.s3.amazonaws.com/key?X-Amz-Expires=900&X-Amz-Signature=b22d37c37d...
|
64
|
+
```
|
53
65
|
|
66
|
+
If you would like to generate public URLs, you can tell S3 storage to make
|
67
|
+
uploads public:
|
68
|
+
|
69
|
+
```rb
|
70
|
+
s3 = Shrine::Storage::S3.new(public: true, **s3_options)
|
54
71
|
s3.upload(io, "key") # uploads with "public-read" ACL
|
55
|
-
s3.url("key") #
|
72
|
+
s3.url("key") # https://my-bucket.s3.amazonaws.com/key
|
73
|
+
```
|
74
|
+
|
75
|
+
If you want to make only *some* uploads public, you can conditionally apply the
|
76
|
+
`:acl` upload option and `:public` URL option:
|
77
|
+
|
78
|
+
```rb
|
79
|
+
Shrine.plugin :upload_options, store: -> (io, **) { { acl: "public-read" } }
|
80
|
+
Shrine.plugin :url_options, store: -> (io, **) { { public: true } }
|
56
81
|
```
|
57
82
|
|
58
83
|
## Prefix
|
@@ -80,13 +105,11 @@ You can also generate upload options per upload with the `upload_options`
|
|
80
105
|
plugin
|
81
106
|
|
82
107
|
```rb
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
{ acl: "private" }
|
89
|
-
end
|
108
|
+
Shrine.plugin :upload_options, store: -> (io, derivative: nil, **) do
|
109
|
+
if derivative == :thumb
|
110
|
+
{ acl: "public-read" }
|
111
|
+
else
|
112
|
+
{ acl: "private" }
|
90
113
|
end
|
91
114
|
end
|
92
115
|
```
|
@@ -97,23 +120,9 @@ or when using the uploader directly
|
|
97
120
|
uploader.upload(file, upload_options: { acl: "private" })
|
98
121
|
```
|
99
122
|
|
100
|
-
|
101
|
-
the uploader level won't be forwarded for generating presigns, since presigns
|
102
|
-
are generated using the storage directly.
|
103
|
-
|
104
|
-
## URL options
|
105
|
-
|
106
|
-
Other than [`:host`](#url-host) and [`:public`](#public-uploads) URL options,
|
107
|
-
all additional options are forwarded to [`Aws::S3::Object#presigned_url`].
|
108
|
-
|
109
|
-
```rb
|
110
|
-
s3.url(
|
111
|
-
expires_in: 15,
|
112
|
-
response_content_disposition: ContentDisposition.attachment("my-filename"),
|
113
|
-
response_content_type: "foo/bar",
|
114
|
-
# ...
|
115
|
-
)
|
116
|
-
```
|
123
|
+
> Unlike the `:upload_options` storage option, upload options given on
|
124
|
+
the uploader level won't be forwarded for generating presigns, since presigns
|
125
|
+
are generated using the storage directly.
|
117
126
|
|
118
127
|
## URL Host
|
119
128
|
|
@@ -133,12 +142,14 @@ s3.url("image.jpg", host: "https://your-s3-host.com/prefix/") # needs to end wit
|
|
133
142
|
```
|
134
143
|
|
135
144
|
To have the `:host` option passed automatically for every URL, use the
|
136
|
-
`url_options` plugin
|
145
|
+
`url_options` plugin:
|
137
146
|
|
138
147
|
```rb
|
139
148
|
plugin :url_options, store: { host: "http://abc123.cloudfront.net" }
|
140
149
|
```
|
141
150
|
|
151
|
+
### Signer
|
152
|
+
|
142
153
|
If you would like to [serve private content via CloudFront], you need to sign
|
143
154
|
the object URLs with a special signer, such as [`Aws::CloudFront::UrlSigner`]
|
144
155
|
provided by the `aws-sdk-cloudfront` gem. The S3 storage initializer accepts a
|
@@ -157,13 +168,27 @@ Shrine::Storage::S3.new(signer: signer.method(:signed_url))
|
|
157
168
|
Shrine::Storage::S3.new(signer: -> (url, **options) { signer.signed_url(url, **options) })
|
158
169
|
```
|
159
170
|
|
171
|
+
## URL options
|
172
|
+
|
173
|
+
Other than `:host` and `:public` URL options, all additional `S3#url` options
|
174
|
+
are forwarded to [`Aws::S3::Object#presigned_url`].
|
175
|
+
|
176
|
+
```rb
|
177
|
+
s3.url(
|
178
|
+
expires_in: 15,
|
179
|
+
response_content_disposition: ContentDisposition.attachment("my-filename"),
|
180
|
+
response_content_type: "foo/bar",
|
181
|
+
# ...
|
182
|
+
)
|
183
|
+
```
|
184
|
+
|
160
185
|
## Presigns
|
161
186
|
|
162
|
-
The
|
163
|
-
to
|
187
|
+
The `S3#presign` method can be used for generating parameters for direct upload
|
188
|
+
to S3:
|
164
189
|
|
165
190
|
```rb
|
166
|
-
s3.presign("
|
191
|
+
s3.presign("key") #=>
|
167
192
|
# {
|
168
193
|
# url: "https://my-bucket.s3.amazonaws.com/...",
|
169
194
|
# fields: { ... }, # blank for PUT presigns
|
@@ -172,11 +197,25 @@ s3.presign("/path/to/file") #=>
|
|
172
197
|
# }
|
173
198
|
```
|
174
199
|
|
175
|
-
|
200
|
+
By default, parameters for a POST upload is generated, but you can also
|
201
|
+
generate PUT upload parameters:
|
202
|
+
|
203
|
+
```rb
|
204
|
+
s3.presign("key", method: :put)
|
205
|
+
```
|
176
206
|
|
177
|
-
|
178
|
-
|
179
|
-
|
207
|
+
Any additional options are forwarded to [`Aws::S3::Object#presigned_post`]
|
208
|
+
(for POST uploads) and [`Aws::S3::Object#presigned_url`] (for PUT uploads).
|
209
|
+
|
210
|
+
```rb
|
211
|
+
s3.presign("key", method: :put, content_disposition: "attachment; filename=my-file.txt") #=>
|
212
|
+
# {
|
213
|
+
# url: "https://my-bucket.s3.amazonaws.com/...",
|
214
|
+
# fields: {},
|
215
|
+
# headers: { "Content-Disposition" => "attachment; filename=my-file.txt" },
|
216
|
+
# method :put,
|
217
|
+
# }
|
218
|
+
```
|
180
219
|
|
181
220
|
## Large files
|
182
221
|
|
@@ -184,33 +223,24 @@ The aws-sdk-s3 gem has the ability to automatically use multipart upload/copy
|
|
184
223
|
for larger files, splitting the file into multiple chunks and uploading/copying
|
185
224
|
them in parallel.
|
186
225
|
|
187
|
-
By default
|
188
|
-
|
189
|
-
|
190
|
-
`:multipart_threshold`.
|
191
|
-
|
192
|
-
```rb
|
193
|
-
thresholds = { upload: 30*1024*1024, copy: 200*1024*1024 }
|
194
|
-
Shrine::Storage::S3.new(multipart_threshold: thresholds, **s3_options)
|
195
|
-
```
|
196
|
-
|
197
|
-
If you want to change how many threads aws-sdk-s3 will use for multipart
|
198
|
-
upload/copy, you can use the `upload_options` plugin to specify
|
199
|
-
`:thread_count`.
|
226
|
+
By default, multipart upload will be used for files larger than 15MB, and
|
227
|
+
multipart copy for files larger than 100MB, but you can change the thresholds
|
228
|
+
via `:multipart_threshold`:
|
200
229
|
|
201
230
|
```rb
|
202
|
-
|
203
|
-
{
|
204
|
-
|
231
|
+
Shrine::Storage::S3.new(
|
232
|
+
multipart_threshold: { upload: 30*1024*1024, copy: 200*1024*1024 },
|
233
|
+
**s3_options,
|
234
|
+
)
|
205
235
|
```
|
206
236
|
|
207
237
|
## Encryption
|
208
238
|
|
209
|
-
The easiest way to use server-side encryption for uploaded S3 objects is to
|
239
|
+
The easiest way to use **server-side** encryption for uploaded S3 objects is to
|
210
240
|
configure default encryption for your S3 bucket. Alternatively, you can pass
|
211
241
|
server-side encryption parameters to the API calls.
|
212
242
|
|
213
|
-
The
|
243
|
+
The `S3#upload` method accepts `:sse_*` options:
|
214
244
|
|
215
245
|
```rb
|
216
246
|
s3.upload(io, "key", sse_customer_algorithm: "AES256",
|
@@ -219,7 +249,7 @@ s3.upload(io, "key", sse_customer_algorithm: "AES256",
|
|
219
249
|
ssekms_key_id: "key_id")
|
220
250
|
```
|
221
251
|
|
222
|
-
The
|
252
|
+
The `S3#presign` method accepts `:server_side_encryption_*` options for POST
|
223
253
|
presigns, and the same `:sse_*` options as above for PUT presigns.
|
224
254
|
|
225
255
|
```rb
|
@@ -232,24 +262,19 @@ When downloading encrypted S3 objects, the same server-side encryption
|
|
232
262
|
parameters need to be passed in.
|
233
263
|
|
234
264
|
```rb
|
235
|
-
s3.download("key", sse_customer_algorithm: "AES256",
|
236
|
-
sse_customer_key: "secret_key",
|
237
|
-
sse_customer_key_md5: "secret_key_md5")
|
238
|
-
|
239
265
|
s3.open("key", sse_customer_algorithm: "AES256",
|
240
266
|
sse_customer_key: "secret_key",
|
241
267
|
sse_customer_key_md5: "secret_key_md5")
|
242
268
|
```
|
243
269
|
|
244
|
-
|
245
|
-
storage with an `Aws::S3::Encryption::Client` instance.
|
270
|
+
**Client-side** encryption is supported as well:
|
246
271
|
|
247
272
|
```rb
|
248
|
-
|
249
|
-
|
250
|
-
)
|
273
|
+
encryption_client = Aws::S3::EncryptionV2::Client.new(...)
|
274
|
+
s3 = Shrine::Storage::S3.new(client: encryption_client, **other_options)
|
251
275
|
|
252
|
-
|
276
|
+
s3.upload(io, "key") # encrypts on upload
|
277
|
+
s3.open("key") # decrypts on download
|
253
278
|
```
|
254
279
|
|
255
280
|
## Accelerate endpoint
|
@@ -283,20 +308,18 @@ Alternatively you can periodically call the `#clear!` method:
|
|
283
308
|
s3.clear! { |object| object.last_modified < Time.now - 7*24*60*60 }
|
284
309
|
```
|
285
310
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
per second per prefix in a bucket (a prefix is a top-level "directory" in the
|
291
|
-
bucket). If your app needs to support higher request rates to S3 than that, you
|
292
|
-
can scale exponentially by using more prefixes.
|
293
|
-
|
311
|
+
[AWS S3]: https://aws.amazon.com/s3/
|
312
|
+
[MinIO]: https://min.io/
|
313
|
+
[DigitalOcean Spaces]: https://www.digitalocean.com/products/spaces/
|
314
|
+
[aws-sdk-s3]: https://rubygems.org/gems/aws-sdk-s3
|
294
315
|
[uploading]: http://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#put-instance_method
|
295
316
|
[copying]: http://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#copy_from-instance_method
|
296
317
|
[presigning]: http://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#presigned_post-instance_method
|
297
318
|
[`Aws::S3::Object#presigned_url`]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#presigned_url-instance_method
|
298
|
-
[aws-sdk-s3]: https://github.com/aws/aws-sdk-ruby/tree/master/gems/aws-sdk-s3
|
299
319
|
[Transfer Acceleration]: http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html
|
300
320
|
[object lifecycle]: http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html
|
301
321
|
[serve private content via CloudFront]: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/PrivateContent.html
|
302
322
|
[`Aws::CloudFront::UrlSigner`]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/CloudFront/UrlSigner.html
|
323
|
+
[credentials]: https://docs.aws.amazon.com/sdk-for-ruby/v3/developer-guide/setup-config.html
|
324
|
+
[`Aws::S3::Object#presigned_post`]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#presigned_post-instance_method
|
325
|
+
[`Aws::S3::Object#presigned_url`]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#presigned_url-instance_method
|
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
|
)
|
data/doc/upgrading_to_3.md
CHANGED
@@ -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,27 +308,35 @@ 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
|
326
|
+
With `derivatives`, the original file is automatically downloaded and retained
|
327
|
+
during processing, so the setup is simpler:
|
322
328
|
|
323
329
|
```rb
|
324
|
-
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
|
325
333
|
```
|
326
334
|
```rb
|
327
335
|
class ImageUploader < Shrine
|
328
|
-
Attacher.
|
336
|
+
Attacher.derivatives do |original|
|
329
337
|
magick = ImageProcessing::MiniMagick.source(original)
|
330
338
|
|
339
|
+
# the :original file should NOT be included anymore
|
331
340
|
{
|
332
341
|
large: magick.resize_to_limit!(800, 800),
|
333
342
|
medium: magick.resize_to_limit!(500, 500),
|
@@ -340,28 +349,13 @@ end
|
|
340
349
|
photo = Photo.new(photo_params)
|
341
350
|
|
342
351
|
if photo.valid?
|
343
|
-
photo.
|
344
|
-
photo.save # automatically calls processing block
|
352
|
+
photo.save # creates derivatives on promotion
|
345
353
|
# ...
|
346
354
|
else
|
347
355
|
# ...
|
348
356
|
end
|
349
357
|
```
|
350
358
|
|
351
|
-
If you have multiple places where you need to generate derivatives, and want it
|
352
|
-
to happen automatically like it did with the `versions` plugin, you can
|
353
|
-
override `Attacher#promote` to call `Attacher#create_derivatives` before
|
354
|
-
promotion:
|
355
|
-
|
356
|
-
```rb
|
357
|
-
class Shrine::Attacher
|
358
|
-
def promote(*)
|
359
|
-
create_derivatives
|
360
|
-
super
|
361
|
-
end
|
362
|
-
end
|
363
|
-
```
|
364
|
-
|
365
359
|
### Accessing derivatives
|
366
360
|
|
367
361
|
The derivative URLs are accessed in the same way as versions:
|
@@ -370,7 +364,7 @@ The derivative URLs are accessed in the same way as versions:
|
|
370
364
|
photo.image_url(:small)
|
371
365
|
```
|
372
366
|
|
373
|
-
But the
|
367
|
+
But the files themselves are accessed differently:
|
374
368
|
|
375
369
|
```rb
|
376
370
|
# versions
|
@@ -424,8 +418,8 @@ database column in different formats:
|
|
424
418
|
|
425
419
|
The `:versions_compatibility` flag to the `derivatives` plugin enables it to
|
426
420
|
read the `versions` format, which aids in transition. Once the `derivatives`
|
427
|
-
plugin has been deployed to production, you can
|
428
|
-
new column format:
|
421
|
+
plugin has been deployed to production, you can update existing records with
|
422
|
+
the new column format:
|
429
423
|
|
430
424
|
```rb
|
431
425
|
Photo.find_each do |photo|
|
@@ -465,11 +459,11 @@ creating another derivatives processor that you will trigger in the controller:
|
|
465
459
|
|
466
460
|
```rb
|
467
461
|
class ImageUploader < Shrine
|
468
|
-
Attacher.
|
462
|
+
Attacher.derivatives do |original|
|
469
463
|
# this will be triggered in the background job
|
470
464
|
end
|
471
465
|
|
472
|
-
Attacher.
|
466
|
+
Attacher.derivatives :foreground do |original|
|
473
467
|
# this will be triggered in the controller
|
474
468
|
end
|
475
469
|
end
|
@@ -486,48 +480,77 @@ else
|
|
486
480
|
end
|
487
481
|
```
|
488
482
|
|
489
|
-
|
483
|
+
### Default URL
|
490
484
|
|
491
|
-
|
492
|
-
|
485
|
+
If you were using the `default_url` plugin, the `Attacher.default_url` now
|
486
|
+
receives a `:derivative` option:
|
493
487
|
|
494
488
|
```rb
|
495
|
-
|
496
|
-
|
489
|
+
Attacher.default_url do |derivative: nil, **|
|
490
|
+
"https://my-app.com/fallbacks/#{derivative}.jpg" if derivative
|
491
|
+
end
|
497
492
|
```
|
498
|
-
```rb
|
499
|
-
require "concurrent"
|
500
493
|
|
501
|
-
|
494
|
+
#### Fallback to original
|
502
495
|
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
end
|
507
|
-
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:
|
508
499
|
|
509
|
-
|
500
|
+
```rb
|
501
|
+
Attacher.default_url do |derivative: nil, **|
|
502
|
+
file&.url if derivative
|
503
|
+
end
|
510
504
|
```
|
511
505
|
|
512
|
-
####
|
506
|
+
#### Fallback to version
|
513
507
|
|
514
|
-
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:
|
515
511
|
|
516
512
|
```rb
|
513
|
+
DERIVATIVE_FALLBACKS = { foo: :bar, ... }
|
514
|
+
|
517
515
|
Attacher.default_url do |derivative: nil, **|
|
518
|
-
|
516
|
+
derivatives[DERIVATIVE_FALLBACKS[derivative]]&.url if derivative
|
517
|
+
end
|
518
|
+
```
|
519
|
+
|
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
|
519
531
|
end
|
520
532
|
```
|
521
533
|
|
522
|
-
|
523
|
-
|
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.
|
524
546
|
|
525
547
|
## Other
|
526
548
|
|
527
549
|
### Processing
|
528
550
|
|
529
551
|
The `processing` plugin has been deprecated over the new
|
530
|
-
[`derivatives`][derivatives] plugin. If you were
|
552
|
+
[`derivatives`][derivatives] plugin. If you were previously replacing the
|
553
|
+
original file:
|
531
554
|
|
532
555
|
```rb
|
533
556
|
class MyUploader < Shrine
|
@@ -547,7 +570,7 @@ you should now add the processed file as a derivative:
|
|
547
570
|
class MyUploader < Shrine
|
548
571
|
plugin :derivatives
|
549
572
|
|
550
|
-
Attacher.
|
573
|
+
Attacher.derivatives do |original|
|
551
574
|
magick = ImageProcessing::MiniMagick.source(original)
|
552
575
|
|
553
576
|
{ normalized: magick.resize_to_limit!(1600, 1600) }
|
@@ -555,6 +578,30 @@ class MyUploader < Shrine
|
|
555
578
|
end
|
556
579
|
```
|
557
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
|
+
|
558
605
|
### Logging
|
559
606
|
|
560
607
|
The `logging` plugin has been removed in favour of the
|
@@ -602,7 +649,7 @@ attacher.copy(other_attacher)
|
|
602
649
|
with
|
603
650
|
|
604
651
|
```rb
|
605
|
-
attacher.
|
652
|
+
attacher.set attacher.upload(other_attacher.file)
|
606
653
|
attacher.add_derivatives other_attacher.derivatives # if using derivatives
|
607
654
|
```
|
608
655
|
|
@@ -660,3 +707,4 @@ end
|
|
660
707
|
[derivatives]: https://shrinerb.com/docs/plugins/derivatives
|
661
708
|
[instrumentation]: https://shrinerb.com/docs/plugins/instrumentation
|
662
709
|
[mirroring]: https://shrinerb.com/docs/plugins/mirroring
|
710
|
+
[overwriting original]: https://discourse.shrinerb.com/t/keep-original-file-after-processing/50/4
|