shrine 3.0.0 → 3.2.2

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.

Potentially problematic release.


This version of shrine might be problematic. Click here for more details.

Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -33
  3. data/LICENSE.txt +1 -1
  4. data/README.md +94 -4
  5. data/doc/advantages.md +35 -18
  6. data/doc/attacher.md +16 -17
  7. data/doc/carrierwave.md +75 -34
  8. data/doc/changing_derivatives.md +39 -39
  9. data/doc/design.md +134 -85
  10. data/doc/external/articles.md +56 -41
  11. data/doc/external/extensions.md +38 -34
  12. data/doc/getting_started.md +182 -112
  13. data/doc/metadata.md +79 -43
  14. data/doc/multiple_files.md +5 -3
  15. data/doc/paperclip.md +110 -42
  16. data/doc/plugins/activerecord.md +5 -5
  17. data/doc/plugins/add_metadata.md +92 -35
  18. data/doc/plugins/backgrounding.md +12 -2
  19. data/doc/plugins/column.md +36 -7
  20. data/doc/plugins/data_uri.md +2 -2
  21. data/doc/plugins/default_url.md +6 -3
  22. data/doc/plugins/derivation_endpoint.md +26 -28
  23. data/doc/plugins/derivatives.md +205 -169
  24. data/doc/plugins/determine_mime_type.md +2 -2
  25. data/doc/plugins/entity.md +3 -3
  26. data/doc/plugins/form_assign.md +5 -5
  27. data/doc/plugins/included.md +25 -5
  28. data/doc/plugins/infer_extension.md +2 -2
  29. data/doc/plugins/instrumentation.md +1 -1
  30. data/doc/plugins/metadata_attributes.md +21 -10
  31. data/doc/plugins/model.md +4 -4
  32. data/doc/plugins/persistence.md +1 -0
  33. data/doc/plugins/refresh_metadata.md +5 -4
  34. data/doc/plugins/remote_url.md +8 -3
  35. data/doc/plugins/remove_invalid.md +9 -1
  36. data/doc/plugins/sequel.md +4 -4
  37. data/doc/plugins/signature.md +11 -2
  38. data/doc/plugins/store_dimensions.md +2 -2
  39. data/doc/plugins/type_predicates.md +96 -0
  40. data/doc/plugins/upload_endpoint.md +7 -11
  41. data/doc/plugins/upload_options.md +1 -1
  42. data/doc/plugins/url_options.md +2 -2
  43. data/doc/plugins/validation.md +14 -4
  44. data/doc/plugins/validation_helpers.md +3 -3
  45. data/doc/plugins/versions.md +11 -11
  46. data/doc/processing.md +289 -125
  47. data/doc/refile.md +39 -18
  48. data/doc/release_notes/2.19.0.md +1 -1
  49. data/doc/release_notes/3.0.0.md +275 -258
  50. data/doc/release_notes/3.0.1.md +22 -0
  51. data/doc/release_notes/3.1.0.md +73 -0
  52. data/doc/release_notes/3.2.0.md +96 -0
  53. data/doc/release_notes/3.2.1.md +32 -0
  54. data/doc/release_notes/3.2.2.md +14 -0
  55. data/doc/securing_uploads.md +3 -3
  56. data/doc/storage/file_system.md +1 -1
  57. data/doc/storage/memory.md +19 -0
  58. data/doc/storage/s3.md +105 -86
  59. data/doc/testing.md +2 -2
  60. data/doc/upgrading_to_3.md +115 -33
  61. data/doc/validation.md +3 -2
  62. data/lib/shrine.rb +8 -8
  63. data/lib/shrine/attacher.rb +19 -14
  64. data/lib/shrine/attachment.rb +5 -5
  65. data/lib/shrine/plugins.rb +22 -0
  66. data/lib/shrine/plugins/add_metadata.rb +12 -3
  67. data/lib/shrine/plugins/default_storage.rb +6 -6
  68. data/lib/shrine/plugins/default_url.rb +1 -1
  69. data/lib/shrine/plugins/derivation_endpoint.rb +10 -6
  70. data/lib/shrine/plugins/derivatives.rb +19 -17
  71. data/lib/shrine/plugins/determine_mime_type.rb +3 -3
  72. data/lib/shrine/plugins/entity.rb +6 -6
  73. data/lib/shrine/plugins/metadata_attributes.rb +1 -1
  74. data/lib/shrine/plugins/model.rb +3 -3
  75. data/lib/shrine/plugins/presign_endpoint.rb +2 -2
  76. data/lib/shrine/plugins/pretty_location.rb +1 -1
  77. data/lib/shrine/plugins/processing.rb +1 -1
  78. data/lib/shrine/plugins/refresh_metadata.rb +2 -2
  79. data/lib/shrine/plugins/remote_url.rb +3 -3
  80. data/lib/shrine/plugins/remove_invalid.rb +10 -5
  81. data/lib/shrine/plugins/signature.rb +7 -6
  82. data/lib/shrine/plugins/store_dimensions.rb +18 -9
  83. data/lib/shrine/plugins/type_predicates.rb +113 -0
  84. data/lib/shrine/plugins/upload_endpoint.rb +3 -3
  85. data/lib/shrine/plugins/upload_options.rb +2 -2
  86. data/lib/shrine/plugins/url_options.rb +2 -2
  87. data/lib/shrine/plugins/validation.rb +9 -7
  88. data/lib/shrine/storage/linter.rb +4 -4
  89. data/lib/shrine/storage/s3.rb +62 -38
  90. data/lib/shrine/uploaded_file.rb +5 -1
  91. data/lib/shrine/version.rb +2 -2
  92. data/shrine.gemspec +6 -7
  93. metadata +23 -29
@@ -1,12 +1,12 @@
1
1
  ---
2
- title: Shrine for Refile Users
2
+ title: Upgrading from Refile
3
3
  ---
4
4
 
5
5
  This guide is aimed at helping Refile users transition to Shrine, and it consists
6
6
  of three parts:
7
7
 
8
8
  1. Explanation of the key differences in design between Refile and Shrine
9
- 2. Instructions how to migrate and existing app that uses Refile to Shrine
9
+ 2. Instructions how to migrate an existing app that uses Refile to Shrine
10
10
  3. Extensive reference of Refile's interface with Shrine equivalents
11
11
 
12
12
  ## Overview
@@ -110,13 +110,6 @@ into separate columns.
110
110
  Shrine provides on-the-fly processing via the
111
111
  [`derivation_endpoint`][derivation_endpoint] plugin:
112
112
 
113
- ```rb
114
- # config/routes.rb (Rails)
115
- Rails.application.routes.draw do
116
- # ...
117
- mount ImageUploader.derivation_endpoint => "/derivations/image"
118
- end
119
- ```
120
113
  ```rb
121
114
  require "image_processing/mini_magick"
122
115
 
@@ -132,8 +125,15 @@ class ImageUploader < Shrine
132
125
  end
133
126
  end
134
127
  ```
128
+ ```rb
129
+ # config/routes.rb (Rails)
130
+ Rails.application.routes.draw do
131
+ # ...
132
+ mount ImageUploader.derivation_endpoint => "/derivations/image"
133
+ end
134
+ ```
135
135
 
136
- Shrine also support processing up front using the [`derivatives`][derivatives]
136
+ Shrine also support eager processing using the [`derivatives`][derivatives]
137
137
  plugin.
138
138
 
139
139
  ### Validation
@@ -199,13 +199,18 @@ explains this setup in more detail.
199
199
  ## Migrating from Refile
200
200
 
201
201
  You have an existing app using Refile and you want to transfer it to
202
- Shrine. Let's assume we have a `Photo` model with the "image" attachment. First
203
- we need to create the `image_data` column for Shrine:
202
+ Shrine. Let's assume we have a `Photo` model with the "image" attachment.
203
+
204
+ ### 1. Add Shrine column
205
+
206
+ First we need to create the `image_data` column for Shrine:
204
207
 
205
208
  ```rb
206
209
  add_column :photos, :image_data, :text
207
210
  ```
208
211
 
212
+ ### 2. Dual write
213
+
209
214
  Afterwards we need to make new uploads write to the `image_data` column. This
210
215
  can be done by including the below module to all models that have Refile
211
216
  attachments:
@@ -219,8 +224,7 @@ Shrine.storages = {
219
224
  }
220
225
 
221
226
  Shrine.plugin :model
222
- ```
223
- ```rb
227
+
224
228
  module RefileShrineSynchronization
225
229
  def write_shrine_data(name)
226
230
  attacher = Shrine::Attacher.from_model(self, name)
@@ -257,8 +261,12 @@ end
257
261
  ```
258
262
 
259
263
  After you deploy this code, the `image_data` column should now be successfully
260
- synchronized with new attachments. Next step is to run a script which writes
261
- all existing Refile attachments to `image_data`:
264
+ synchronized with new attachments.
265
+
266
+ ### 3. Data migration
267
+
268
+ Next step is to run a script which writes all existing Refile attachments to
269
+ `image_data`:
262
270
 
263
271
  ```rb
264
272
  Photo.find_each do |photo|
@@ -267,9 +275,22 @@ Photo.find_each do |photo|
267
275
  end
268
276
  ```
269
277
 
278
+ ### 4. Rewrite code
279
+
270
280
  Now you should be able to rewrite your application so that it uses Shrine
271
- instead of Refile, using equivalent Shrine storages. For help with translating
272
- the code from Refile to Shrine, you can consult the reference below.
281
+ instead of Refile (you can consult the reference in the next section). You can
282
+ remove the `RefileShrineSynchronization` module as well.
283
+
284
+ ### 5. Remove Refile columns
285
+
286
+ If everything is looking good, we can remove Refile columns:
287
+
288
+ ```rb
289
+ remove_column :photos, :image_id
290
+ remove_column :photos, :image_size
291
+ remove_column :photos, :image_filename
292
+ remove_column :photos, :image_content_type
293
+ ```
273
294
 
274
295
  ## Refile to Shrine direct mapping
275
296
 
@@ -16,7 +16,7 @@ title: Shrine 2.19.0
16
16
  uploaded_file.download
17
17
  uploaded_file.delete
18
18
  ```
19
- ```plaintext
19
+ ```
20
20
  Metadata (32ms) – {:storage=>:store, :io=>StringIO, :uploader=>Shrine}
21
21
  Upload (1523ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :io=>StringIO, :upload_options=>{}, :uploader=>Shrine}
22
22
  Exists (755ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}
@@ -3,312 +3,326 @@ title: Shrine 3.0.0
3
3
  ---
4
4
 
5
5
  This guide covers all the changes in the 3.0.0 version of Shrine. If you're
6
- currently using Shrine 2.x, see [Upgrading to Shrine 3.x] for instructions on
7
- how to upgrade.
6
+ currently using Shrine 2.x, see **[Upgrading to Shrine 3.x]** for instructions
7
+ on how to upgrade.
8
8
 
9
9
  ## Major features
10
10
 
11
- * The new **[`derivatives`][derivatives]** plugin has been added for storing
12
- additional processed files alongside the main file.
11
+ ### Derivatives
13
12
 
14
- ```rb
15
- Shrine.plugin :derivatives
16
- ```
17
- ```rb
18
- class ImageUploader < Shrine
19
- Attacher.derivatives_processor do |original|
20
- magick = ImageProcessing::MiniMagick.source(original)
21
-
22
- {
23
- large: magick.resize_to_limit!(800, 800),
24
- medium: magick.resize_to_limit!(500, 500),
25
- small: magick.resize_to_limit!(300, 300),
26
- }
27
- end
28
- end
29
- ```
30
- ```rb
31
- photo = Photo.new(photo_params)
32
- photo.image_derivatives! # creates derivatives
33
- photo.save
34
- ```
35
-
36
- This is a rewrite of the [`versions`][versions] plugin, bringing numerous
37
- improvements:
38
-
39
- - processed files are separated from the main file
40
-
41
- ```rb
42
- photo.image_data #=>
43
- # {
44
- # "id": "original.jpg",
45
- # "storage": "store",
46
- # "metadata": { ... },
47
- # "derivatives": {
48
- # "large": { "id": "large.jpg", "storage": "store", "metadata": { ... } },
49
- # "medium": { "id": "medium.jpg", "storage": "store", "metadata": { ... } },
50
- # "small": { "id": "small.jpg", "storage": "store", "metadata": { ... } }
51
- # }
52
- # }
53
-
54
- photo.image #=> #<Shrine::UploadedFile @id="original.jpg" ...>
55
- photo.image_derivatives #=>
56
- # {
57
- # large: #<Shrine::UploadedFile @id="large.jpg" ...>,
58
- # medium: #<Shrine::UploadedFile @id="medium.jpg" ...>,
59
- # small: #<Shrine::UploadedFile @id="small.jpg" ...>,
60
- # }
61
- ```
62
-
63
- - processing is decoupled from promotion
64
-
65
- ```rb
66
- photo = Photo.create(image: file) # promote original file to permanent storage
67
- photo.image_derivatives! # generate derivatives after promotion
68
- photo.save # save derivatives data
69
- ```
70
-
71
- - ability to add or remove processed files at any point
72
-
73
- ```rb
74
- class ImageUploader < Shrine
75
- Attacher.derivatives_processor :thumbnails do |original|
76
- # ...
77
- end
78
-
79
- Attacher.derivatives_processor :crop do |original, left:, top:, width:, height:|
80
- vips = ImageProcessing::Vips.source(original)
81
-
82
- { cropped: vips.crop!(left, top, width, height) }
83
- end
84
- end
85
- ```
86
- ```rb
87
- photo.image_derivatives!(:thumbnails)
88
- photo.image_derivatives #=> { large: ..., medium: ..., small: ... }
89
- photo.save
90
-
91
- # ... sometime later ...
92
-
93
- photo.image_derivatives!(:crop, left: 0, top: 0, width: 300, height: 300)
94
- photo.image_derivatives #=> { large: ..., medium: ..., small: ..., cropped: ... }
95
- photo.save
96
- ```
13
+ The new **[`derivatives`][derivatives]** plugin has been added for storing
14
+ additional processed files alongside the main file.
97
15
 
98
- - possibility of uploading processed files to different storage
16
+ ```rb
17
+ Shrine.plugin :derivatives
18
+ ```
19
+ ```rb
20
+ class ImageUploader < Shrine
21
+ Attacher.derivatives_processor do |original|
22
+ magick = ImageProcessing::MiniMagick.source(original)
99
23
 
100
- ```rb
101
- class ImageUploader < Shrine
102
- # specify storage for all derivatives
103
- Attacher.derivatives_storage :other_store
104
-
105
- # or specify storage per derivative
106
- Attacher.derivatives_storage { |derivative| :other_store }
107
- end
108
- ```
109
- ```rb
110
- photo = Photo.create(image: file)
111
- photo.image.storage_key #=> :store
24
+ {
25
+ large: magick.resize_to_limit!(800, 800),
26
+ medium: magick.resize_to_limit!(500, 500),
27
+ small: magick.resize_to_limit!(300, 300),
28
+ }
29
+ end
30
+ end
31
+ ```
32
+ ```rb
33
+ photo = Photo.new(photo_params)
34
+ photo.image_derivatives! # creates derivatives
35
+ photo.save
36
+ ```
112
37
 
113
- photo.image_derivatives!
114
- photo.image_derivatives[:large].storage_key #=> :other_store
115
- ```
38
+ This is a rewrite of the [`versions`][versions] plugin, bringing numerous
39
+ improvements:
116
40
 
117
- * The [`Shrine::Attacher`][attacher] class has been rewritten and can now be
118
- used without models:
41
+ * processed files are separated from the main file
119
42
 
120
43
  ```rb
121
- attacher = Shrine::Attacher.new
122
- attacher.attach(file)
123
- attacher.file #=> #<Shrine::UploadedFile>
44
+ photo.image_data #=>
45
+ # {
46
+ # "id": "original.jpg",
47
+ # "storage": "store",
48
+ # "metadata": { ... },
49
+ # "derivatives": {
50
+ # "large": { "id": "large.jpg", "storage": "store", "metadata": { ... } },
51
+ # "medium": { "id": "medium.jpg", "storage": "store", "metadata": { ... } },
52
+ # "small": { "id": "small.jpg", "storage": "store", "metadata": { ... } }
53
+ # }
54
+ # }
55
+
56
+ photo.image #=> #<Shrine::UploadedFile id="original.jpg" ...>
57
+ photo.image_derivatives #=>
58
+ # {
59
+ # large: #<Shrine::UploadedFile id="large.jpg" ...>,
60
+ # medium: #<Shrine::UploadedFile id="medium.jpg" ...>,
61
+ # small: #<Shrine::UploadedFile id="small.jpg" ...>,
62
+ # }
124
63
  ```
125
64
 
126
- The `Attacher#data`, `Attacher#load_data`, and `Attacher.from_data` methods
127
- have been added for dumping and loading the attached file:
65
+ * processing is decoupled from promotion
128
66
 
129
67
  ```rb
130
- # dump attached file into a serializable Hash
131
- data = attacher.data #=> { "id" => "abc123.jpg", "storage" => "store", "metadata" => { ... } }
132
- ```
133
- ```rb
134
- # initialize attacher from attached file data...
135
- attacher = Shrine::Attacher.from_data(data)
136
- attacher.file #=> #<Shrine::UploadedFile @id="abc123.jpg" @storage_key=:store @metadata={...}>
137
-
138
- # ...or load attached file into an existing attacher
139
- attacher = Shrine::Attacher.new
140
- attacher.load_data(data)
141
- attacher.file #=> #<Shrine::UploadedFile>
68
+ photo = Photo.create(image: file) # promote original file to permanent storage
69
+ photo.image_derivatives! # generate derivatives after promotion
70
+ photo.save # save derivatives data
142
71
  ```
143
72
 
144
- Several more methods have been added:
73
+ - ability to add or remove processed files at any point
145
74
 
146
- - `Attacher#attach` – attaches the file directly to permanent storage
147
- - `Attacher#attach_cached` – extracted from `Attacher#assign`
148
- - `Attacher#upload` – calls `Shrine#upload`, passing `:record` and `:name` context
149
- - `Attacher#file` – alias for `Attacher#get`
150
- - `Attacher#cache_key` – returns temporary storage key (`:cache` by default)
151
- - `Attacher#store_key` – returns permanent storage key (`:store` by default)
152
-
153
- * The new [`column`][column] plugin adds the ability to serialize attached file
154
- data, in format suitable for writing into a database column.
155
-
156
- ```rb
157
- Shrine.plugin :column
158
- ```
159
75
  ```rb
160
- # dump attached file data into a JSON string
161
- data = attacher.column_data #=> '{"id":"abc123.jpg","storage":"store","metadata":{...}}'
162
- ```
163
- ```rb
164
- # initialize attacher from attached file data...
165
- attacher = Shrine::Attacher.from_column(data)
166
- attacher.file #=> #<Shrine::UploadedFile @id="abc123.jpg" @storage_key=:store @metadata={...}>
167
-
168
- # ...or load attached file into an existing attacher
169
- attacher = Shrine::Attacher.new
170
- attacher.load_column(data)
171
- attacher.file #=> #<Shrine::UploadedFile>
172
- ```
76
+ class ImageUploader < Shrine
77
+ Attacher.derivatives_processor :thumbnails do |original|
78
+ # ...
79
+ end
173
80
 
174
- * The new [`entity`][entity] plugin adds support for immutable structs, which
175
- are commonly used with ROM, Hanami and dry-rb.
81
+ Attacher.derivatives_processor :crop do |original, left:, top:, width:, height:|
82
+ vips = ImageProcessing::Vips.source(original)
176
83
 
177
- ```rb
178
- Shrine.plugin :entity
179
- ```
180
- ```rb
181
- class Photo < Hanami::Entity
182
- include Shrine::Attachment(:image)
84
+ { cropped: vips.crop!(left, top, width, height) }
85
+ end
183
86
  end
184
87
  ```
185
88
  ```rb
186
- photo = Photo.new(image_data: '{"id":"abc123.jpg","storage":"store","metadata":{...}}')
187
- photo.image #=> #<Shrine::UploadedFile @id="abc123.jpg" @storage_key=:store ...>
188
- ```
89
+ photo.image_derivatives!(:thumbnails)
90
+ photo.image_derivatives #=> { large: ..., medium: ..., small: ... }
91
+ photo.save
189
92
 
190
- * The new [`model`][model] plugin adds support for mutable structs, which is
191
- used by `activerecord` and `sequel` plugins.
93
+ # ... sometime later ...
192
94
 
193
- ```rb
194
- Shrine.plugin :model
195
- ```
196
- ```rb
197
- class Photo < Struct.new(:image_data)
198
- include Shrine::Attachment(:image)
199
- end
200
- ```
201
- ```rb
202
- photo = Photo.new
203
- photo.image = file
204
- photo.image #=> #<Shrine::UploadedFile @id="abc123.jpg" @storage_key=:cache ...>
205
- photo.image_data #=> #=> '{"id":"abc123.jpg", "storage":"cache", "metadata":{...}}'
95
+ photo.image_derivatives!(:crop, left: 0, top: 0, width: 300, height: 300)
96
+ photo.image_derivatives #=> { large: ..., medium: ..., small: ..., cropped: ... }
97
+ photo.save
206
98
  ```
207
99
 
208
- * The [`backgrounding`][backgrounding] plugin has been rewritten for more
209
- flexibility and simplicity. The new usage is much more explicit:
100
+ * possibility of uploading processed files to different storage
210
101
 
211
102
  ```rb
212
- Shrine.plugin :backgrounding
213
- Shrine::Attacher.promote_block do
214
- PromoteJob.perform_async(self.class.name, record.class.name, record.id, name, file_data)
215
- end
216
- Shrine::Attacher.destroy_block do
217
- DestroyJob.perform_async(self.class.name, data)
218
- end
219
- ```
220
- ```rb
221
- class PromoteJob
222
- include Sidekiq::Worker
223
-
224
- def perform(attacher_class, record_class, record.id, name, file_data)
225
- attacher_class = Object.const_get(attacher_class)
226
- record = Object.const_get(record_class).find(record_id) # if using Active Record
103
+ class ImageUploader < Shrine
104
+ # specify storage for all derivatives
105
+ Attacher.derivatives_storage :other_store
227
106
 
228
- attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
229
- attacher.atomic_promote
230
- rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
231
- # attachment has changed or the record has been deleted, nothing to do
232
- end
107
+ # or specify storage per derivative
108
+ Attacher.derivatives_storage { |derivative| :other_store }
233
109
  end
234
110
  ```
235
111
  ```rb
236
- class DestroyJob
237
- include Sidekiq::Worker
238
-
239
- def perform(attacher_class, data)
240
- attacher_class = Object.const_get(attacher_class)
112
+ photo = Photo.create(image: file)
113
+ photo.image.storage_key #=> :store
114
+
115
+ photo.image_derivatives!
116
+ photo.image_derivatives[:large].storage_key #=> :other_store
117
+ ```
118
+
119
+ ### Attacher redesign
120
+
121
+ The [`Shrine::Attacher`][attacher] class has been rewritten and can now be
122
+ used without models:
123
+
124
+ ```rb
125
+ attacher = Shrine::Attacher.new
126
+ attacher.attach(file)
127
+ attacher.file #=> #<Shrine::UploadedFile>
128
+ ```
129
+
130
+ The `Attacher#data`, `Attacher#load_data`, and `Attacher.from_data` methods
131
+ have been added for dumping and loading the attached file:
132
+
133
+ ```rb
134
+ # dump attached file into a serializable Hash
135
+ data = attacher.data #=> { "id" => "abc123.jpg", "storage" => "store", "metadata" => { ... } }
136
+ ```
137
+ ```rb
138
+ # initialize attacher from attached file data...
139
+ attacher = Shrine::Attacher.from_data(data)
140
+ attacher.file #=> #<Shrine::UploadedFile id="abc123.jpg" storage=:store metadata={...}>
141
+
142
+ # ...or load attached file into an existing attacher
143
+ attacher = Shrine::Attacher.new
144
+ attacher.load_data(data)
145
+ attacher.file #=> #<Shrine::UploadedFile>
146
+ ```
147
+
148
+ Several more methods have been added:
149
+
150
+ - `Attacher#attach` – attaches the file directly to permanent storage
151
+ - `Attacher#attach_cached` – extracted from `Attacher#assign`
152
+ - `Attacher#upload` – calls `Shrine#upload`, passing `:record` and `:name` context
153
+ - `Attacher#file` – alias for `Attacher#get`
154
+ - `Attacher#cache_key` – returns temporary storage key (`:cache` by default)
155
+ - `Attacher#store_key` – returns permanent storage key (`:store` by default)
156
+
157
+ #### Column
158
+
159
+ The new [`column`][column] plugin adds the ability to serialize attached file
160
+ data, in format suitable for writing into a database column.
161
+
162
+ ```rb
163
+ Shrine.plugin :column
164
+ ```
165
+ ```rb
166
+ # dump attached file data into a JSON string
167
+ data = attacher.column_data #=> '{"id":"abc123.jpg","storage":"store","metadata":{...}}'
168
+ ```
169
+ ```rb
170
+ # initialize attacher from attached file data...
171
+ attacher = Shrine::Attacher.from_column(data)
172
+ attacher.file #=> #<Shrine::UploadedFile id="abc123.jpg" storage=:store metadata={...}>
173
+
174
+ # ...or load attached file into an existing attacher
175
+ attacher = Shrine::Attacher.new
176
+ attacher.load_column(data)
177
+ attacher.file #=> #<Shrine::UploadedFile>
178
+ ```
179
+
180
+ #### Entity
181
+
182
+ The new [`entity`][entity] plugin adds support for immutable structs, which
183
+ are commonly used with ROM, Hanami and dry-rb.
184
+
185
+ ```rb
186
+ Shrine.plugin :entity
187
+ ```
188
+ ```rb
189
+ class Photo < Hanami::Entity
190
+ include Shrine::Attachment(:image)
191
+ end
192
+ ```
193
+ ```rb
194
+ photo = Photo.new(image_data: '{"id":"abc123.jpg","storage":"store","metadata":{...}}')
195
+ photo.image #=> #<Shrine::UploadedFile id="abc123.jpg" storage=:store ...>
196
+ ```
197
+
198
+ #### Model
199
+
200
+ The new [`model`][model] plugin adds support for mutable structs, which is
201
+ used by `activerecord` and `sequel` plugins.
202
+
203
+ ```rb
204
+ Shrine.plugin :model
205
+ ```
206
+ ```rb
207
+ class Photo < Struct.new(:image_data)
208
+ include Shrine::Attachment(:image)
209
+ end
210
+ ```
211
+ ```rb
212
+ photo = Photo.new
213
+ photo.image = file
214
+ photo.image #=> #<Shrine::UploadedFile id="abc123.jpg" storage=:cache ...>
215
+ photo.image_data #=> #=> '{"id":"abc123.jpg", "storage":"cache", "metadata":{...}}'
216
+ ```
217
+
218
+ ### Backgrounding rewrite
241
219
 
242
- attacher = attacher_class.from_data(data)
243
- attacher.destroy
244
- end
220
+ * The [`backgrounding`][backgrounding] plugin has been rewritten for more
221
+ flexibility and simplicity. The new usage is much more explicit:
222
+
223
+ ```rb
224
+ Shrine.plugin :backgrounding
225
+ Shrine::Attacher.promote_block do
226
+ PromoteJob.perform_async(self.class.name, record.class.name, record.id, name, file_data)
227
+ end
228
+ Shrine::Attacher.destroy_block do
229
+ DestroyJob.perform_async(self.class.name, data)
230
+ end
231
+ ```
232
+ ```rb
233
+ class PromoteJob
234
+ include Sidekiq::Worker
235
+
236
+ def perform(attacher_class, record_class, record.id, name, file_data)
237
+ attacher_class = Object.const_get(attacher_class)
238
+ record = Object.const_get(record_class).find(record_id) # if using Active Record
239
+
240
+ attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
241
+ attacher.atomic_promote
242
+ rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
243
+ # attachment has changed or the record has been deleted, nothing to do
245
244
  end
246
- ```
245
+ end
246
+ ```
247
+ ```rb
248
+ class DestroyJob
249
+ include Sidekiq::Worker
247
250
 
248
- There are several main differences compared to the old implementation:
249
-
250
- - we are in charge of passing the record to the background job
251
- - we can access the attacher before promotion
252
- - we can react to errors that caused promotion to abort
253
-
254
- We can now also register backgrounding hooks on an attacher instance, allowing
255
- us to pass additional parameters to the background job:
256
-
257
- ```rb
258
- photo = Photo.new(photo_params)
251
+ def perform(attacher_class, data)
252
+ attacher_class = Object.const_get(attacher_class)
259
253
 
260
- photo.image_attacher.promote_block do |attacher|
261
- PromoteJob.perform_async(
262
- attacher.class.name,
263
- attacher.record.class.name,
264
- attacher.record.id,
265
- attacher.name,
266
- attacher.file_data,
267
- current_user.id, # <== parameters from the controller
268
- )
254
+ attacher = attacher_class.from_data(data)
255
+ attacher.destroy
269
256
  end
257
+ end
258
+ ```
270
259
 
271
- photo.save # will call our instance-level backgrounding hook
272
- ```
260
+ There are several main differences compared to the old implementation:
273
261
 
274
- * The persistence plugins (`activerecord`, `sequel`) now implement a unified
275
- [persistence] interface:
262
+ - we are in charge of passing the record to the background job
263
+ - we can access the attacher before promotion
264
+ - we can react to errors that caused promotion to abort
276
265
 
277
- | Method | Description |
278
- | :----------------- | :---------- |
279
- | `Attacher#persist` | persists attachment data |
280
- | `Attacher#atomic_persist` | persists attachment data if attachment hasn’t changed |
281
- | `Attacher#atomic_promote` | promotes cached file and atomically persists changes |
266
+ We can now also register backgrounding hooks on an attacher instance, allowing
267
+ us to pass additional parameters to the background job:
282
268
 
283
- The "atomic" methods use the new [`atomic_helpers`][atomic_helpers] plugin,
284
- and are useful for background jobs. For example, this is how we'd use them to
285
- implement metadata extraction in the background in a concurrency-safe way:
269
+ ```rb
270
+ photo = Photo.new(photo_params)
286
271
 
287
- ```rb
288
- MetadataJob.perform_async(
272
+ photo.image_attacher.promote_block do |attacher|
273
+ PromoteJob.perform_async(
289
274
  attacher.class.name,
290
275
  attacher.record.class.name,
291
276
  attacher.record.id,
292
277
  attacher.name,
293
278
  attacher.file_data,
279
+ current_user.id, # <== parameters from the controller
294
280
  )
295
- ```
296
- ```rb
297
- class MetadataJob
298
- include Sidekiq::Worker
299
-
300
- def perform(attacher_class, record_class, record_id, name, file_data)
301
- attacher_class = Object.const_get(attacher_class)
302
- record = Object.const_get(record_class).find(record_id) # if using Active Record
303
-
304
- attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
305
- attacher.refresh_metadata! # extract metadata
306
- attacher.atomic_persist # persist if attachment hasn't changed
307
- rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
308
- # attachment has changed or record has been deleted, nothing to do
309
- end
281
+ end
282
+
283
+ photo.save # will call our instance-level backgrounding hook
284
+ ```
285
+
286
+ ### Persistence interface
287
+
288
+ The persistence plugins (`activerecord`, `sequel`) now implement a unified
289
+ [persistence] interface:
290
+
291
+ | Method | Description |
292
+ | :----------------- | :---------- |
293
+ | `Attacher#persist` | persists attachment data |
294
+ | `Attacher#atomic_persist` | persists attachment data if attachment hasn’t changed |
295
+ | `Attacher#atomic_promote` | promotes cached file and atomically persists changes |
296
+
297
+ The "atomic" methods use the new [`atomic_helpers`][atomic_helpers] plugin,
298
+ and are useful for background jobs. For example, this is how we'd use them to
299
+ implement metadata extraction in the background in a concurrency-safe way:
300
+
301
+ ```rb
302
+ MetadataJob.perform_async(
303
+ attacher.class.name,
304
+ attacher.record.class.name,
305
+ attacher.record.id,
306
+ attacher.name,
307
+ attacher.file_data,
308
+ )
309
+ ```
310
+ ```rb
311
+ class MetadataJob
312
+ include Sidekiq::Worker
313
+
314
+ def perform(attacher_class, record_class, record_id, name, file_data)
315
+ attacher_class = Object.const_get(attacher_class)
316
+ record = Object.const_get(record_class).find(record_id) # if using Active Record
317
+
318
+ attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
319
+ attacher.refresh_metadata! # extract metadata
320
+ attacher.atomic_persist # persist if attachment hasn't changed
321
+ rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
322
+ # attachment has changed or record has been deleted, nothing to do
310
323
  end
311
- ```
324
+ end
325
+ ```
312
326
 
313
327
  ## Other new plugins
314
328
 
@@ -352,8 +366,8 @@ how to upgrade.
352
366
  ```
353
367
  ```rb
354
368
  attacher = photo.image_attacher
355
- attacher.form_assign("image" => file, "title" => "...", "description" => "...")
356
- attacher.file #=> #<Shrine::UploadedFile @id="..." @storage_key=:cache ...>
369
+ attacher.form_assign({ "image" => file, "title" => "...", "description" => "..." })
370
+ attacher.file #=> #<Shrine::UploadedFile id="..." storage=:cache ...>
357
371
  ```
358
372
 
359
373
  ## Other features
@@ -686,6 +700,9 @@ how to upgrade.
686
700
  * The `Attacher#replace` method has been renamed to
687
701
  `Attacher#destroy_previous`.
688
702
 
703
+ * The `Attacher#assign` method now raises an exception when non-cached uploaded
704
+ file is assigned.
705
+
689
706
  * The `Attacher#attached?` method now returns whether a file is attached,
690
707
  regardless of whether it was changed or not.
691
708