shrine 3.3.0 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -0
  3. data/README.md +14 -12
  4. data/doc/carrierwave.md +2 -2
  5. data/doc/changing_derivatives.md +1 -1
  6. data/doc/changing_location.md +8 -6
  7. data/doc/design.md +5 -5
  8. data/doc/direct_s3.md +25 -0
  9. data/doc/external/articles.md +16 -16
  10. data/doc/external/extensions.md +1 -1
  11. data/doc/getting_started.md +79 -27
  12. data/doc/metadata.md +1 -1
  13. data/doc/multiple_files.md +57 -22
  14. data/doc/paperclip.md +1 -0
  15. data/doc/plugins/backgrounding.md +4 -4
  16. data/doc/plugins/derivation_endpoint.md +24 -0
  17. data/doc/plugins/derivatives.md +11 -1
  18. data/doc/plugins/entity.md +12 -4
  19. data/doc/plugins/instrumentation.md +1 -1
  20. data/doc/plugins/keep_files.md +6 -4
  21. data/doc/plugins/model.md +8 -3
  22. data/doc/plugins/sequel.md +1 -1
  23. data/doc/plugins/validation_helpers.md +1 -1
  24. data/doc/processing.md +3 -2
  25. data/doc/refile.md +3 -3
  26. data/doc/release_notes/2.1.0.md +1 -1
  27. data/doc/release_notes/3.4.0.md +35 -0
  28. data/doc/release_notes/3.5.0.md +63 -0
  29. data/doc/retrieving_uploads.md +1 -1
  30. data/doc/testing.md +55 -17
  31. data/doc/upgrading_to_3.md +6 -8
  32. data/lib/shrine/plugins/activerecord.rb +3 -3
  33. data/lib/shrine/plugins/derivation_endpoint.rb +31 -30
  34. data/lib/shrine/plugins/derivatives.rb +25 -19
  35. data/lib/shrine/plugins/determine_mime_type.rb +2 -0
  36. data/lib/shrine/plugins/download_endpoint.rb +7 -0
  37. data/lib/shrine/plugins/entity.rb +14 -7
  38. data/lib/shrine/plugins/infer_extension.rb +4 -0
  39. data/lib/shrine/plugins/instrumentation.rb +17 -19
  40. data/lib/shrine/plugins/model.rb +3 -1
  41. data/lib/shrine/plugins/remove_attachment.rb +2 -0
  42. data/lib/shrine/plugins/sequel.rb +1 -1
  43. data/lib/shrine/plugins/validation_helpers.rb +1 -1
  44. data/lib/shrine/storage/s3.rb +17 -7
  45. data/lib/shrine/uploaded_file.rb +3 -2
  46. data/lib/shrine/version.rb +1 -1
  47. data/lib/shrine.rb +3 -3
  48. data/shrine.gemspec +3 -2
  49. metadata +26 -10
@@ -3,6 +3,9 @@ id: multiple-files
3
3
  title: Multiple Files
4
4
  ---
5
5
 
6
+ import Tabs from '@theme/Tabs';
7
+ import TabItem from '@theme/TabItem';
8
+
6
9
  There are times when you want to allow users to attach multiple files to a
7
10
  single resource, like an album having many photos or a playlist having many
8
11
  songs. Some file attachment libraries provide a special interface for multiple
@@ -67,8 +70,9 @@ files (or attachments) table will be the photos table.
67
70
  Let's create a table for the main resource and attachments, and add a foreign
68
71
  key in the attachment table for the main table:
69
72
 
70
- <!--DOCUSAURUS_CODE_TABS-->
71
- <!--Sequel-->
73
+ <Tabs>
74
+ <TabItem value="sequel" label="Sequel">
75
+
72
76
  ```rb
73
77
  Sequel.migration do
74
78
  change do
@@ -87,7 +91,10 @@ Sequel.migration do
87
91
  end
88
92
  end
89
93
  ```
90
- <!--ActiveRecord-->
94
+
95
+ </TabItem>
96
+ <TabItem value="activerecord" label="Active Record">
97
+
91
98
  ```rb
92
99
  class CreateAlbumsAndPhotos < ActiveRecord::Migration
93
100
  def change
@@ -104,25 +111,33 @@ class CreateAlbumsAndPhotos < ActiveRecord::Migration
104
111
  end
105
112
  end
106
113
  ```
107
- <!--END_DOCUSAURUS_CODE_TABS-->
114
+
115
+ </TabItem>
116
+ </Tabs>
108
117
 
109
118
  In the Photo model, create a Shrine attachment attribute named `image`
110
119
  (`:image` matches the `_data` column prefix above):
111
120
 
112
- <!--DOCUSAURUS_CODE_TABS-->
113
- <!--Sequel-->
121
+ <Tabs>
122
+ <TabItem value="sequel" label="Sequel">
123
+
114
124
  ```rb
115
125
  class Photo < Sequel::Model
116
126
  include ImageUploader::Attachment(:image)
117
127
  end
118
128
  ```
119
- <!--ActiveRecord-->
129
+
130
+ </TabItem>
131
+ <TabItem value="activerecord" label="Active Record">
132
+
120
133
  ```rb
121
134
  class Photo < ActiveRecord::Base
122
135
  include ImageUploader::Attachment(:image)
123
136
  end
124
137
  ```
125
- <!--END_DOCUSAURUS_CODE_TABS-->
138
+
139
+ </TabItem>
140
+ </Tabs>
126
141
 
127
142
  ### 2. Declare nested attributes
128
143
 
@@ -131,8 +146,9 @@ Using nested attributes is the easiest way to implement any dynamic
131
146
  relationship to the photos table, and allow it to directly accept attributes
132
147
  for the associated photo records by enabling nested attributes:
133
148
 
134
- <!--DOCUSAURUS_CODE_TABS-->
135
- <!--Sequel-->
149
+ <Tabs>
150
+ <TabItem value="sequel" label="Sequel">
151
+
136
152
  ```rb
137
153
  class Album < Sequel::Model
138
154
  one_to_many :photos
@@ -142,14 +158,20 @@ class Album < Sequel::Model
142
158
  nested_attributes :photos, destroy: true
143
159
  end
144
160
  ```
145
- <!--ActiveRecord-->
161
+
162
+ </TabItem>
163
+ <TabItem value="activerecord" label="Active Record">
164
+
146
165
  ```rb
147
166
  class Album < ActiveRecord::Base
148
167
  has_many :photos, dependent: :destroy
149
168
  accepts_nested_attributes_for :photos, allow_destroy: true
150
169
  end
151
170
  ```
152
- <!--Mongoid-->
171
+
172
+ </TabItem>
173
+ <TabItem value="mongoid" label="Mongoid">
174
+
153
175
  ```rb
154
176
  class Album
155
177
  include Mongoid::Document
@@ -157,7 +179,9 @@ class Album
157
179
  accepts_nested_attributes_for :photos
158
180
  end
159
181
  ```
160
- <!--END_DOCUSAURUS_CODE_TABS-->
182
+
183
+ </TabItem>
184
+ </Tabs>
161
185
 
162
186
  Documentation on nested attributes:
163
187
 
@@ -174,13 +198,14 @@ already created photos, so that the same form can be used for updating the
174
198
  album/photos as well (they will be submitted under the
175
199
  `album[photos_attributes]` parameter).
176
200
 
177
- <!--DOCUSAURUS_CODE_TABS-->
178
- <!--Rails form builder-->
201
+ <Tabs>
202
+ <TabItem value="rails" label="Rails form builder">
203
+
179
204
  ```rb
180
205
  form_for @album, html: { enctype: "multipart/form-data" } do |f|
181
206
  f.text_field :title
182
207
  f.fields_for :photos do |p| # adds new `album[photos_attributes]` parameter
183
- p.hidden_field :image, value: p.object.cached_image_data
208
+ p.hidden_field :image, value: p.object.cached_image_data, id: nil
184
209
  p.file_field :image
185
210
  p.check_box :_destroy unless p.object.new_record?
186
211
  end
@@ -188,7 +213,10 @@ form_for @album, html: { enctype: "multipart/form-data" } do |f|
188
213
  f.submit "Create"
189
214
  end
190
215
  ```
191
- <!--Forme-->
216
+
217
+ </TabItem>
218
+ <TabItem value="forme" label="Forme">
219
+
192
220
  ```rb
193
221
  form @album, action: "/photos", enctype: "multipart/form-data" do |f|
194
222
  f.input :title
@@ -201,7 +229,9 @@ form @album, action: "/photos", enctype: "multipart/form-data" do |f|
201
229
  f.button "Create"
202
230
  end
203
231
  ```
204
- <!--END_DOCUSAURUS_CODE_TABS-->
232
+
233
+ </TabItem>
234
+ </Tabs>
205
235
 
206
236
  In your controller you should still be able to assign all the attributes to the
207
237
  album, just remember to whitelist the new parameter for the nested attributes,
@@ -286,21 +316,26 @@ class ImageUploader < Shrine
286
316
  end
287
317
  end
288
318
  ```
289
- <!--DOCUSAURUS_CODE_TABS-->
290
- <!--Sequel-->
319
+ <Tabs>
320
+ <TabItem value="sequel" label="Sequel">
321
+
291
322
  ```rb
292
323
  class Album < Sequel::Model
293
324
  # ... (nested_attributes already enables validating associated photos) ...
294
325
  end
295
326
  ```
296
- <!--ActiveRecord-->
327
+
328
+ </TabItem>
329
+ <TabItem value="activerecord" label="Active Record">
330
+
297
331
  ```rb
298
332
  class Album < ActiveRecord::Base
299
333
  # ...
300
334
  validates_associated :photos
301
335
  end
302
336
  ```
303
- <!--END_DOCUSAURUS_CODE_TABS-->
337
+ </TabItem>
338
+ </Tabs>
304
339
 
305
340
  Note that by default only metadata set on the client side will be available for
306
341
  validations. Shrine will not automatically run metadata extraction for directly
data/doc/paperclip.md CHANGED
@@ -695,6 +695,7 @@ s3.upload(io, "object/destination/path")
695
695
  The Shrine storage has no replacement for the `:url` Paperclip option, and it
696
696
  isn't needed.
697
697
 
698
+ [metadata_attributes]: https://shrinerb.com/docs/plugins/metadata_attributes
698
699
  [Managing Derivatives]: https://shrinerb.com/docs/changing-derivatives
699
700
  [direct uploads]: https://shrinerb.com/docs/getting-started#direct-uploads
700
701
  [S3]: https://shrinerb.com/docs/storage/s3
@@ -46,7 +46,7 @@ Then, in your initializer, you can configure all uploaders to use these jobs:
46
46
 
47
47
  ```rb
48
48
  Shrine::Attacher.promote_block do
49
- PromoteJob.perform_async(self.class.name, record.class.name, record.id, name, file_data)
49
+ PromoteJob.perform_async(self.class.name, record.class.name, record.id, name.to_s, file_data)
50
50
  end
51
51
  Shrine::Attacher.destroy_block do
52
52
  DestroyJob.perform_async(self.class.name, data)
@@ -58,7 +58,7 @@ Alternatively, you can setup backgrounding only for specific uploaders:
58
58
  ```rb
59
59
  class MyUploader < Shrine
60
60
  Attacher.promote_block do
61
- PromoteJob.perform_async(self.class.name, record.class.name, record.id, name, file_data)
61
+ PromoteJob.perform_async(self.class.name, record.class.name, record.id, name.to_s, file_data)
62
62
  end
63
63
  Attacher.destroy_block do
64
64
  DestroyJob.perform_async(self.class.name, data)
@@ -121,7 +121,7 @@ Shrine::Attacher.promote_block do |attacher|
121
121
  attacher.class.name,
122
122
  attacher.record.class.name,
123
123
  attacher.record.id,
124
- attacher.name,
124
+ attacher.name.to_s,
125
125
  attacher.file_data,
126
126
  )
127
127
  end
@@ -143,7 +143,7 @@ photo.image_attacher.promote_block do |attacher|
143
143
  attacher.class.name,
144
144
  attacher.record.class.name,
145
145
  attacher.record.id,
146
- attacher.name,
146
+ attacher.name.to_s,
147
147
  attacher.file_data,
148
148
  current_user.id, # pass arguments known at the controller level
149
149
  )
@@ -331,6 +331,29 @@ uploaded_file.derivation_url(:thumbnail, expires_in: 90)
331
331
  #=> ".../thumbnail/eyJpZCI6ImZvbyIsInN?expires_at=1547843568&signature=..."
332
332
  ```
333
333
 
334
+ ## Custom signer
335
+
336
+ The derivation URLs are signed by default, and the signature is checked when
337
+ the URLs are requested, which prevents tampering. If you have URL expiration
338
+ turned on, this may prevent your CDN from caching the response.
339
+
340
+ In this case, you may need to do custom CDN-specific URL signing. You can
341
+ bypass Shrine's default signing by passing a custom signer:
342
+
343
+ ```rb
344
+ require "aws-sdk-cloudfront"
345
+ signer = Aws::CloudFront::UrlSigner.new(key_pair_id: "...", private_key: "...")
346
+
347
+ plugin :derivation_endpoint,
348
+ expires_in: 90,
349
+ signer: -> (url, expires_in:) do
350
+ signer.signed_url(url, expires: Time.now.to_i + expires_in)
351
+ end
352
+ ```
353
+
354
+ When `:signer` option is used, the `:secret_key` option is not required, as
355
+ that secret is only used for default signing.
356
+
334
357
  ## Response headers
335
358
 
336
359
  ### Content Type
@@ -796,6 +819,7 @@ derivation.option(:upload_location)
796
819
  | `:metadata` | List of metadata keys the source uploaded file should include in the derivation block | `[]` |
797
820
  | `:prefix` | Path prefix added to the URLs | `nil` |
798
821
  | `:secret_key` | Key used to sign derivation URLs in order to prevent tampering | required |
822
+ | `:signer` | Proc accepting URL and query params used for custom signing of URLs. | `nil` |
799
823
  | `:type` | Media type returned in the `Content-Type` response header in the derivation response | determined from derivative's extension when possible |
800
824
  | `:upload` | Whether the generated derivatives will be cached on the storage | `false` |
801
825
  | `:upload_location` | Location to which the derivatives will be uploaded on the storage | `<source id>/<name>-<args>` |
@@ -133,7 +133,7 @@ Attacher.default_url do |derivative: nil, **|
133
133
  end
134
134
  ```
135
135
  ```rb
136
- photo.image_url(:medium) #=> "https://example.com/fallbacks.com/medium.jpg"
136
+ photo.image_url(:medium) #=> "https://example.com/fallbacks/medium.jpg"
137
137
  ```
138
138
 
139
139
  Any additional URL options passed to `#<name>_url` will be forwarded to the
@@ -778,6 +778,16 @@ derivatives #=>
778
778
  Like `Shrine.uploaded_file`, the `Shrine.derivatives` method accepts data as a
779
779
  hash (stringified or symbolized) or a JSON string.
780
780
 
781
+ ### Marshalling
782
+
783
+ The `Attacher` instance uses a mutex to make `Attacher#merge_derivatives`
784
+ thread-safe, which is not marshallable. If you want to be able to marshal the
785
+ attacher instance, you can skip mutex usage:
786
+
787
+ ```rb
788
+ plugin :derivatives, mutex: false
789
+ ```
790
+
781
791
  ## Instrumentation
782
792
 
783
793
  If the `instrumentation` plugin has been loaded, the `derivatives` plugin adds
@@ -22,7 +22,9 @@ These methods read attachment data from the `#<name>_data` attribute on the
22
22
  entity instance.
23
23
 
24
24
  ```rb
25
- class Photo < Entity(:image_data) # has `image_data` reader
25
+ class Photo
26
+ attr_reader :image_data
27
+
26
28
  include ImageUploader::Attachment(:image)
27
29
  end
28
30
  ```
@@ -94,7 +96,9 @@ You can also specify default attacher options when including
94
96
  `Shrine::Attachment`:
95
97
 
96
98
  ```rb
97
- class Photo < Entity(:image_data)
99
+ class Photo
100
+ attr_reader :image_data
101
+
98
102
  include ImageUploader::Attachment(:image, store: :other_store)
99
103
  end
100
104
  ```
@@ -123,7 +127,8 @@ You can also use `Shrine::Attacher` directly (with or without the
123
127
  `Shrine::Attachment` module):
124
128
 
125
129
  ```rb
126
- class Photo < Entity(:image_data) # has `image_data` reader
130
+ class Photo
131
+ attr_reader :image_data
127
132
  end
128
133
  ```
129
134
  ```rb
@@ -191,7 +196,7 @@ attacher.file #=> nil
191
196
  ### Reloading
192
197
 
193
198
  The `Attacher#reload` method reloads attached file from the attachment data on
194
- the entity attribute.
199
+ the entity attribute and resets dirty tracking.
195
200
 
196
201
  ```rb
197
202
  photo = Photo.new
@@ -206,6 +211,9 @@ attacher.reload
206
211
  attacher.file #=> #<ImageUploader::UploadedFile>
207
212
  ```
208
213
 
214
+ If you want to reload attachment data while retaining dirty tracking state, use
215
+ `Attacher#read` instead.
216
+
209
217
  ### Column values
210
218
 
211
219
  The `Attacher#column_values` method returns a hash with the entity attribute as
@@ -173,7 +173,7 @@ methods:
173
173
 
174
174
  ```rb
175
175
  # sends a `my_event.shrine` event to the notifications component
176
- Shrine.instrument(:my_event, foo: "bar") do
176
+ Shrine.instrument(:my_event, { foo: "bar" }) do
177
177
  # do work
178
178
  end
179
179
  ```
@@ -2,10 +2,11 @@
2
2
  title: Keep Files
3
3
  ---
4
4
 
5
- The [`keep_files`][keep_files] plugin prevents file deletion when the attacher
6
- is about to destroy currently attached or previously attached file. This
7
- functionality is useful when implementing soft deletes, versioning, or in
8
- general any scenario where you need to track history.
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
data/doc/plugins/model.md CHANGED
@@ -17,7 +17,9 @@ Including a `Shrine::Attachment` module into a model class will:
17
17
  * add `#<name>=` and `#<name>_changed?` methods
18
18
 
19
19
  ```rb
20
- class Photo < Model(:image_data) # has `image_data` accessor
20
+ class Photo
21
+ attr_accessor :image_data
22
+
21
23
  include ImageUploader::Attachment(:image)
22
24
  end
23
25
  ```
@@ -107,7 +109,9 @@ If you still want to include `Shrine::Attachment` modules to immutable
107
109
  entities, you can disable "model" behaviour by passing `model: false`:
108
110
 
109
111
  ```rb
110
- class Photo < Entity(:image_data)
112
+ class Photo
113
+ attr_reader :image_data
114
+
111
115
  include ImageUploader::Attachment(:image, model: false)
112
116
  end
113
117
  ```
@@ -118,7 +122,8 @@ You can also use `Shrine::Attacher` directly (with or without the
118
122
  `Shrine::Attachment` module):
119
123
 
120
124
  ```rb
121
- class Photo < Model(:image_data) # has `image_data` accessor
125
+ class Photo
126
+ attr_accessor :image_data
122
127
  end
123
128
  ```
124
129
  ```rb
@@ -172,7 +172,7 @@ attacher.file #=> #<Shrine::UploadedFile id="397eca.jpg" storage=:store ...>
172
172
  photo.image_data #=> '{"id":"397eca.jpg","storage":"store","metadata":{...}}'
173
173
  ```
174
174
 
175
- ### Pesistence
175
+ ### Persistence
176
176
 
177
177
  The following persistence methods are added to `Shrine::Attacher`:
178
178
 
@@ -10,7 +10,7 @@ plugin :validation_helpers
10
10
 
11
11
  Attacher.validate do
12
12
  validate_mime_type %w[image/jpeg image/png image/webp]
13
- validate_max_size 5*1024*1024
13
+ validate_max_size 5*1024*1024 # bytes
14
14
  # ...
15
15
  end
16
16
  ```
data/doc/processing.md CHANGED
@@ -315,14 +315,15 @@ previews.
315
315
 
316
316
  Shrine provides on-the-fly processing functionality via the
317
317
  **[`derivation_endpoint`][derivation_endpoint]** plugin. You set it up by
318
- loading the plugin with a secret key and a path prefix, mount its Rack app in
318
+ loading the plugin with a secret key (you generate this yourself, maybe via
319
+ something like `SecureRandom.hex`) and a path prefix, mount its Rack app in
319
320
  your routes on the configured path prefix, and define processing you want to
320
321
  perform:
321
322
 
322
323
  ```rb
323
324
  # config/initializers/shrine.rb (Rails)
324
325
  # ...
325
- Shrine.plugin :derivation_endpoints, secret_key: "<YOUR_SECRET_KEY>"
326
+ Shrine.plugin :derivation_endpoint, secret_key: "<SHRINE_SECRET_KEY>"
326
327
  ```
327
328
  ```rb
328
329
  require "image_processing/mini_magick"
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
@@ -51,7 +51,7 @@ end
51
51
 
52
52
  ```rb
53
53
  form_for @photo do |f|
54
- f.hidden_field :image, value: @photo.cached_image_data
54
+ f.hidden_field :image, value: @photo.cached_image_data, id: nil
55
55
  f.file_filed :image
56
56
  end
57
57
  ```
@@ -0,0 +1,35 @@
1
+ ---
2
+ title: Shrine 3.4.0
3
+ ---
4
+
5
+ * Passing attacher options to `Shrine.Attachment` method now works on Ruby 3.0.
6
+
7
+ * Defining validation errors as an array of I18n key and options in
8
+ `activerecord` plugin now works on Ruby 3.0.
9
+
10
+ * The `:fastimage` MIME type analyzer now correctly detects SVGs as
11
+ `image/svg+html` in the `determine_mime_type` plugin.
12
+
13
+ * The `Shrine::Attacher#read` method provided by the `entity` plugin is now
14
+ public. This is consistent with `Shrine::Attacher#write` from `model` plugin
15
+ being public as well.
16
+
17
+ * The `Shrine::Attacher#reload` method now resets attachment's dirty state.
18
+ This means that for a model whose `Attacher#changed?` returns `true`, calling
19
+ `#reload` on the model will make `Attacher#changed?` return `false`. This was
20
+ the behaviour before Shrine 3.3.0.
21
+
22
+ ```rb
23
+ # before
24
+ model.file_attacher.changed? #=> true
25
+ model.reload
26
+ model.file_attacher.changed? #=> true
27
+
28
+ # after
29
+ model.file_attacher.changed? #=> true
30
+ model.reload
31
+ model.file_attacher.changed? #=> false
32
+ ```
33
+
34
+ * Calling `#reload` on the model will not initialize a `Shrine::Attacher`
35
+ instance anymore if one hasn't previously been initialized.
@@ -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
+ ```
@@ -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
- block. We can use that to safely retrieve the whole content of a file, without
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