shrine 3.0.1 → 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 +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
|