shrine 3.0.0.beta2 → 3.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -1
  3. data/README.md +100 -106
  4. data/doc/advantages.md +90 -88
  5. data/doc/attacher.md +322 -152
  6. data/doc/carrierwave.md +105 -113
  7. data/doc/changing_derivatives.md +308 -0
  8. data/doc/changing_location.md +92 -21
  9. data/doc/changing_storage.md +107 -0
  10. data/doc/creating_plugins.md +1 -1
  11. data/doc/design.md +8 -9
  12. data/doc/direct_s3.md +3 -2
  13. data/doc/metadata.md +97 -78
  14. data/doc/multiple_files.md +3 -3
  15. data/doc/paperclip.md +89 -88
  16. data/doc/plugins/activerecord.md +3 -12
  17. data/doc/plugins/backgrounding.md +126 -100
  18. data/doc/plugins/derivation_endpoint.md +4 -5
  19. data/doc/plugins/derivatives.md +63 -32
  20. data/doc/plugins/download_endpoint.md +54 -1
  21. data/doc/plugins/entity.md +1 -0
  22. data/doc/plugins/form_assign.md +53 -0
  23. data/doc/plugins/mirroring.md +37 -16
  24. data/doc/plugins/multi_cache.md +22 -0
  25. data/doc/plugins/presign_endpoint.md +1 -1
  26. data/doc/plugins/remote_url.md +19 -4
  27. data/doc/plugins/validation.md +83 -0
  28. data/doc/processing.md +149 -133
  29. data/doc/refile.md +68 -63
  30. data/doc/release_notes/3.0.0.md +835 -0
  31. data/doc/securing_uploads.md +56 -36
  32. data/doc/storage/s3.md +2 -2
  33. data/doc/testing.md +104 -120
  34. data/doc/upgrading_to_3.md +538 -0
  35. data/doc/validation.md +48 -87
  36. data/lib/shrine.rb +7 -4
  37. data/lib/shrine/attacher.rb +16 -6
  38. data/lib/shrine/plugins/activerecord.rb +33 -14
  39. data/lib/shrine/plugins/atomic_helpers.rb +1 -1
  40. data/lib/shrine/plugins/backgrounding.rb +23 -89
  41. data/lib/shrine/plugins/data_uri.rb +13 -2
  42. data/lib/shrine/plugins/derivation_endpoint.rb +7 -11
  43. data/lib/shrine/plugins/derivatives.rb +44 -20
  44. data/lib/shrine/plugins/download_endpoint.rb +26 -0
  45. data/lib/shrine/plugins/form_assign.rb +6 -3
  46. data/lib/shrine/plugins/keep_files.rb +2 -2
  47. data/lib/shrine/plugins/mirroring.rb +62 -22
  48. data/lib/shrine/plugins/model.rb +2 -2
  49. data/lib/shrine/plugins/multi_cache.rb +27 -0
  50. data/lib/shrine/plugins/remote_url.rb +25 -10
  51. data/lib/shrine/plugins/remove_invalid.rb +1 -1
  52. data/lib/shrine/plugins/sequel.rb +39 -20
  53. data/lib/shrine/plugins/validation.rb +3 -0
  54. data/lib/shrine/storage/s3.rb +16 -1
  55. data/lib/shrine/uploaded_file.rb +1 -0
  56. data/lib/shrine/version.rb +1 -1
  57. data/shrine.gemspec +1 -1
  58. metadata +12 -7
  59. data/doc/migrating_storage.md +0 -76
  60. data/doc/regenerating_versions.md +0 -143
  61. data/lib/shrine/plugins/attacher_options.rb +0 -55
@@ -0,0 +1,538 @@
1
+ # Upgrading to Shrine 3.x
2
+
3
+ This guide provides instructions for upgrading Shrine in your apps to version
4
+ 3.x. We will cover the following areas:
5
+
6
+ * [Attacher](#attacher)
7
+ * [Backgrounding](#backgrounding)
8
+ * [Versions](#versions)
9
+ * [Miscellaneous](#miscellaneous)
10
+ - [Logging](#logging)
11
+ - [Backup](#backup)
12
+ - [Copy](#copy)
13
+ - [Moving](#moving)
14
+ - [Memory](#memory)
15
+
16
+ ## Attacher
17
+
18
+ The `Shrine::Attacher` class has been rewritten in Shrine 3.0, though much of
19
+ the main API remained the same.
20
+
21
+ ### Model
22
+
23
+ The main change is that `Attacher.new` is now used for initializing the
24
+ attacher without a model:
25
+
26
+ ```rb
27
+ attacher = Shrine::Attacher.new
28
+ # ...
29
+
30
+ attacher = Shrine::Attacher.new(photo, :image)
31
+ # ~> ArgumentError: invalid number of arguments
32
+ ```
33
+
34
+ To initialize an attacher with a model, use `Attacher.from_model` provided by
35
+ the new [`model`][model] plugin (which is automatically loaded by
36
+ `activerecord` and `sequel` plugins):
37
+
38
+ ```rb
39
+ attacher = Shrine::Attacher.from_model(photo, :image)
40
+ # ...
41
+ ```
42
+
43
+ If you're using the `Shrine::Attachment` module with POROs, make sure to load
44
+ the `model` plugin.
45
+
46
+ ```rb
47
+ Shrine.plugin :model
48
+ ```
49
+ ```rb
50
+ class Photo < Struct.new(:image_data)
51
+ include Shrine::Attachment(:image)
52
+ end
53
+ ```
54
+
55
+ ### Data attribute
56
+
57
+ The `Attacher#read` method has been removed. If you want to generate serialized
58
+ attachment data, use `Attacher#column_data`. Otherwise if you want to generate
59
+ hash attachment data, use `Attacher#data`.
60
+
61
+ ```rb
62
+ attacher.column_data #=> '{"id":"...","storage":"...","metadata":{...}}'
63
+ attacher.data #=> { "id" => "...", "storage" => "...", "metadata" => { ... } }
64
+ ```
65
+
66
+ The `Attacher#data_attribute` has been renamed to `Attacher#attribute`.
67
+
68
+ ### State
69
+
70
+ The attacher now maintains its own state, so if you've previously modified the
71
+ `#<name>_data` record attribute and expected the changes to be picked up by the
72
+ attacher, you'll now need to call `Attacher#reload` for that:
73
+
74
+ ```rb
75
+ attacher.file #=> nil
76
+ record.image_data = '{"id":"...","storage":"...","metadata":{...}}'
77
+ attacher.file #=> nil
78
+ attacher.reload
79
+ attacher.file #=> #<Shrine::UploadedFile ...>
80
+ ```
81
+
82
+ ### Validation
83
+
84
+ The validation functionality has been extracted into the `validation` plugin.
85
+ If you're using the `validation_helpers` plugin, it will automatically load
86
+ `validation` for you. Otherwise you'll have to load it explicitly:
87
+
88
+ ```rb
89
+ Shrine.plugin :validation
90
+ ```
91
+ ```rb
92
+ class MyUploader < Shrine
93
+ Attacher.validate do
94
+ # ...
95
+ end
96
+ end
97
+ ```
98
+
99
+ ### Setting
100
+
101
+ The `Attacher#set` method has been renamed to `Attacher#change`, and the
102
+ private `Attacher#_set` method has been renamed to `Attacher#set` and made
103
+ public:
104
+
105
+ ```rb
106
+ attacher.change(uploaded_file) # sets file, remembers previous file, runs validations
107
+ attacher.set(uploaded_file) # sets file
108
+ ```
109
+
110
+ If you've previously used `Attacher#replace` directly to delete previous file,
111
+ it has now been renamed to `Attacher#destroy_previous`.
112
+
113
+ Also note that `Attacher#attached?` now returns whether a file is attached,
114
+ while `Attacher#changed?` continues to return whether the attachment has
115
+ changed.
116
+
117
+ ### Uploading and deleting
118
+
119
+ The `Attacher#store!` and `Attacher#cache!` methods have been removed, you
120
+ should now use `Attacher#upload` instead:
121
+
122
+ ```rb
123
+ attacher.upload(io) # uploads to permanent storage
124
+ attacher.upload(io, :cache) # uploads to temporary storage
125
+ attacher.upload(io, :other_store) # uploads to another storage
126
+ ```
127
+
128
+ The `Attacher#delete!` method has been removed as well, you should instead just
129
+ delete the file directly via `UploadedFile#delete`.
130
+
131
+ ### Promoting
132
+
133
+ If you were promoting manually, the `Attacher#promote` method will now only
134
+ save promoted file in memory, it won't persist the changes.
135
+
136
+ ```rb
137
+ attacher.promote
138
+ # ...
139
+ record.save # you need to persist the changes
140
+ ```
141
+
142
+ If you want the concurrenct-safe promotion with persistence, use the new
143
+ `Attacher#atomic_promote` method.
144
+
145
+ ```rb
146
+ attacher.atomic_promote
147
+ ```
148
+
149
+ The `Attacher#swap` method has been removed. If you were using it directly, you
150
+ can use `Attacher#set` and `Attacher#atomic_persist` instead:
151
+
152
+ ```rb
153
+ current_file = attacher.file
154
+ attacher.set(new_file)
155
+ attacher.atomic_persist(current_file)
156
+ ```
157
+
158
+ ## Backgrounding
159
+
160
+ The `backgrounding` plugin has been rewritten in Shrine 3.0 and has a new API.
161
+
162
+ ```rb
163
+ Shrine.plugin :backgrounding
164
+ Shrine::Attacher.promote_block { PromoteJob.perform_later(self.class, record, name, file_data) }
165
+ Shrine::Attacher.destroy_block { DestroyJob.perform_later(self.class, data) }
166
+ ```
167
+ ```rb
168
+ class PromoteJob < ActiveJob::Base
169
+ def perform(attacher_class, record, name, file_data)
170
+ attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
171
+ attacher.atomic_promote
172
+ rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
173
+ # attachment has changed or record has been deleted, nothing to do
174
+ end
175
+ end
176
+ ```
177
+ ```rb
178
+ class DestroyJob < ActiveJob::Base
179
+ def perform(attacher_class, data)
180
+ attacher = attacher_class.from_data(data)
181
+ attacher.destroy
182
+ end
183
+ end
184
+ ```
185
+
186
+ ### Dual support
187
+
188
+ When you're making the switch in production, there might still be jobs in the
189
+ queue that have the old argument format. So, we'll initially want to handle
190
+ both argument formats, and then switch to the new one once the jobs with old
191
+ format have been drained.
192
+
193
+ ```rb
194
+ class PromoteJob < ActiveJob::Base
195
+ def perform(*args)
196
+ if args.one?
197
+ file_data, (record_class, record_id), name, shrine_class =
198
+ args.first.values_at("attachment", "record", "name", "shrine_class")
199
+
200
+ record = Object.const_get(record_class).find(record_id)
201
+ attacher_class = Object.const_get(shrine_class)::Attacher
202
+ else
203
+ attacher_class, record, name, file_data = args
204
+ end
205
+
206
+ attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
207
+ attacher.atomic_promote
208
+ rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
209
+ # attachment has changed or record has been deleted, nothing to do
210
+ end
211
+ and
212
+ ```
213
+ ```rb
214
+ class DestroyJob < ActiveJob::Base
215
+ def perform(*args)
216
+ if args.one?
217
+ data, shrine_class = args.first.values_at("attachment", "shrine_class")
218
+
219
+ data = JSON.parse(data)
220
+ attacher_class = Object.const_get(shrine_class)::Attacher
221
+ else
222
+ attacher_class, data = args
223
+ end
224
+
225
+ attacher = attacher_class.from_data(data)
226
+ attacher.destroy
227
+ end
228
+ and
229
+ ```
230
+
231
+ ### Attacher backgrounding
232
+
233
+ In Shrine 2.x, `Attacher#_promote` and `Attacher#_delete` methods could be used
234
+ to spawn promote and delete jobs. This is now done by `Attacher#promote_cached`
235
+ and `Attacher#destroy_attached`:
236
+
237
+ ```rb
238
+ attacher.promote_cached # will spawn background job if registered
239
+ attacher.destroy_attached # will spawn background job if registered
240
+ ```
241
+
242
+ If you want to explicitly call backgrounding blocks, you can use
243
+ `Attacher#promote_background` and `Attacher#destroy_background`:
244
+
245
+ ```rb
246
+ attacher.promote_background # calls promote block
247
+ attacher.destroy_background # calls destroy block
248
+ ```
249
+
250
+ ## Versions
251
+
252
+ The `versions`, `processing`, `recache`, and `delete_raw` plugins have been
253
+ deprecated in favour of the new [`derivatives`][derivatives] plugin. Let's
254
+ assume you have the following `versions` code:
255
+
256
+ ```rb
257
+ class ImageUploader < Shrine
258
+ plugin :processing
259
+ plugin :versions
260
+ plugin :delete_raw
261
+
262
+ process(:store) do |file, context|
263
+ versions = { original: file }
264
+
265
+ file.download do |original|
266
+ magick = ImageProcessing::MiniMagick.source(original)
267
+
268
+ versions[:large] = magick.resize_to_limit!(800, 800)
269
+ versions[:medium] = magick.resize_to_limit!(500, 500)
270
+ versions[:small] = magick.resize_to_limit!(300, 300)
271
+ end
272
+
273
+ versions
274
+ end
275
+ end
276
+ ```
277
+ ```rb
278
+ photo = Photo.new(photo_params)
279
+
280
+ if photo.valid?
281
+ photo.save # automatically calls processing block
282
+ # ...
283
+ else
284
+ # ...
285
+ end
286
+ ```
287
+
288
+ With `derivatives` it becomes this:
289
+
290
+ ```rb
291
+ Shrine.plugin :derivatives, versions_compatibility: true # handle versions column format
292
+ ```
293
+ ```rb
294
+ class ImageUploader < Shrine
295
+ Attacher.derivatives_processor do |original|
296
+ magick = ImageProcessing::MiniMagick.source(original)
297
+
298
+ {
299
+ large: magick.resize_to_limit!(800, 800),
300
+ medium: magick.resize_to_limit!(500, 500),
301
+ small: magick.resize_to_limit!(300, 300),
302
+ }
303
+ end
304
+ end
305
+ ```
306
+ ```rb
307
+ photo = Photo.new(photo_params)
308
+
309
+ if photo.valid?
310
+ photo.image_derivatives! if photo.image_changed? # create derivatives
311
+ photo.save # automatically calls processing block
312
+ # ...
313
+ else
314
+ # ...
315
+ end
316
+ ```
317
+
318
+ The derivative URLs are accessed in the same way as versions:
319
+
320
+ ```rb
321
+ photo.image_url(:small)
322
+ ```
323
+
324
+ But the derivatives themselves are accessed differently:
325
+
326
+ ```rb
327
+ # versions
328
+ photo.image #=>
329
+ # {
330
+ # original: #<Shrine::UploadedFile ...>,
331
+ # large: #<Shrine::UploadedFile ...>,
332
+ # medium: #<Shrine::UploadedFile ...>,
333
+ # small: #<Shrine::UploadedFile ...>,
334
+ # }
335
+ photo.image[:medium] #=> #<Shrine::UploadedFile ...>
336
+ ```
337
+ ```rb
338
+ # derivatives
339
+ photo.image_derivatives #=>
340
+ # {
341
+ # large: #<Shrine::UploadedFile ...>,
342
+ # medium: #<Shrine::UploadedFile ...>,
343
+ # small: #<Shrine::UploadedFile ...>,
344
+ # }
345
+ photo.image(:medium) #=> #<Shrine::UploadedFile ...>
346
+ ```
347
+
348
+ ### Migrating versions
349
+
350
+ The `versions` and `derivatives` plugins save processed file data to the
351
+ database column in different formats:
352
+
353
+ ```rb
354
+ # versions
355
+ {
356
+ "original": { "id": "...", "storage": "...", "metadata": { ... } },
357
+ "large": { "id": "...", "storage": "...", "metadata": { ... } },
358
+ "medium": { "id": "...", "storage": "...", "metadata": { ... } },
359
+ "small": { "id": "...", "storage": "...", "metadata": { ... } }
360
+ }
361
+ ```
362
+ ```rb
363
+ # derivatives
364
+ {
365
+ "id": "...",
366
+ "storage": "...",
367
+ "metadata": { ... },
368
+ "derivatives": {
369
+ "large": { "id": "...", "storage": "...", "metadata": { ... } },
370
+ "medium": { "id": "...", "storage": "...", "metadata": { ... } },
371
+ "small": { "id": "...", "storage": "...", "metadata": { ... } }
372
+ }
373
+ }
374
+ ```
375
+
376
+ The `:versions_compatibility` flag to the `derivatives` plugin enables it to
377
+ read the `versions` format, which aids in transition. Once the `derivatives`
378
+ plugin has been deployed to production, you can switch existing records to the
379
+ new column format:
380
+
381
+ ```rb
382
+ Photo.find_each do |photo|
383
+ photo.image_attacher.write
384
+ photo.image_attacher.atomic_persist
385
+ end
386
+ ```
387
+
388
+ Afterwards you should be able to remove the `:versions_compatibility` flag.
389
+
390
+ ### Backgrounding derivatives
391
+
392
+ If you're using the `backgrounding` plugin, you can trigger derivatives
393
+ creation in the `PromoteJob` instead of the controller:
394
+
395
+ ```rb
396
+ class PromoteJob < ActiveJob::Base
397
+ def perform(attacher_class, record, name, file_data)
398
+ attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
399
+ attacher.create_derivatives # call derivatives processor
400
+ attacher.atomic_promote
401
+ rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
402
+ # attachment has changed or record has beeen deleted, nothing to do
403
+ end
404
+ end
405
+ ```
406
+
407
+ #### Recache
408
+
409
+ If you were using the `recache` plugin, you can replicate the behaviour by
410
+ creating another derivatives processor that you will trigger in the controller:
411
+
412
+ ```rb
413
+ class ImageUploader < Shrine
414
+ Attacher.derivatives_processor do |original|
415
+ # this will be triggered in the background job
416
+ end
417
+
418
+ Attacher.derivatives_processor :foreground do |original|
419
+ # this will be triggered in the controller
420
+ end
421
+ end
422
+ ```
423
+ ```rb
424
+ photo = Photo.new(photo_params)
425
+
426
+ if photo.valid?
427
+ photo.image_derivatives!(:foreground) if photo.image_changed?
428
+ photo.save
429
+ # ...
430
+ else
431
+ # ...
432
+ end
433
+ ```
434
+
435
+ #### Parallelize
436
+
437
+ The `parallelize` plugin has been removed. The `derivatives` plugin is
438
+ thread-safe, so you can parallelize uploading processed files manually:
439
+
440
+ ```rb
441
+ # Gemfile
442
+ gem "concurrent-ruby"
443
+ ```
444
+ ```rb
445
+ require "concurrent"
446
+
447
+ derivatives = attacher.process_derivatives
448
+
449
+ tasks = derivatives.map do |name, file|
450
+ Concurrent::Promises.future(name, file) do |name, file|
451
+ attacher.add_derivative(name, file)
452
+ end
453
+ end
454
+
455
+ Concurrent::Promises.zip(*tasks).wait!
456
+ ```
457
+
458
+ #### Default URL
459
+
460
+ The `derivatives` plugin integrates with the `default_url` plugin:
461
+
462
+ ```rb
463
+ Attacher.default_url do |derivative: nil, **|
464
+ "https://my-app.com/fallbacks/#{derivative}.jpg" if derivative
465
+ end
466
+ ```
467
+
468
+ However, it doesn't implement any other URL fallbacks that the `versions`
469
+ plugin has for missing derivatives.
470
+
471
+ ## Miscellaneous
472
+
473
+ ### Logging
474
+
475
+ The `logging` plugin has been removed in favour of the
476
+ [`instrumentation`][instrumentation] plugin. You can replace
477
+
478
+ ```rb
479
+ Shrine.plugin :logging, logger: Rails.logger
480
+ ```
481
+
482
+ with
483
+
484
+ ```rb
485
+ Shrine.logger = Rails.logger
486
+
487
+ Shrine.plugin :instrumentation
488
+ ```
489
+
490
+ ### Backup
491
+
492
+ The `backup` plugin has been removed in favour of the new
493
+ [`mirroring`][mirroring] plugin. You can replace
494
+
495
+ ```rb
496
+ Shrine.plugin :backup, storage: :backup_store
497
+ ```
498
+
499
+ with
500
+
501
+ ```rb
502
+ Shrine.plugin :mirroring, mirror: { store: :backup_store }
503
+ ```
504
+
505
+ ### Copy
506
+
507
+ The `copy` plugin has been removed. You can replace
508
+
509
+ ```rb
510
+ Shrine.plugin :copy
511
+ ```
512
+ ```rb
513
+ attacher.copy(other_attacher)
514
+ ```
515
+
516
+ with
517
+
518
+ ```rb
519
+ attacher.attach other_attacher.file
520
+ attacher.add_derivatives other_attacher.derivatives # if using derivatives
521
+ ```
522
+
523
+ ### Moving
524
+
525
+ The `moving` plugin has been removed in favour of the `:move` option for
526
+ `FileSystem#upload`. You can set up moving in basic places with the
527
+ `upload_options` plugin:
528
+
529
+ ```rb
530
+ Shrine.plugin :upload_options,
531
+ cache: -> (io, action: nil, **) { { move: true } if action == :cache },
532
+ store: -> (io, action: nil, **) { { move: true } if action == :store }
533
+ ```
534
+
535
+ [model]: /doc/plugins/model.md#readme
536
+ [derivatives]: /doc/plugins/derivatives.md#readme
537
+ [instrumentation]: /doc/plugins/instrumentation.md#readme
538
+ [mirroring]: /doc/plugins/mirroring.md#readme