shrine 3.1.0 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -0
  3. data/README.md +11 -4
  4. data/doc/advantages.md +4 -4
  5. data/doc/attacher.md +2 -2
  6. data/doc/carrierwave.md +24 -12
  7. data/doc/changing_derivatives.md +1 -1
  8. data/doc/changing_location.md +6 -5
  9. data/doc/design.md +134 -85
  10. data/doc/direct_s3.md +26 -0
  11. data/doc/external/articles.md +57 -45
  12. data/doc/external/extensions.md +41 -35
  13. data/doc/external/misc.md +23 -8
  14. data/doc/getting_started.md +156 -85
  15. data/doc/metadata.md +80 -44
  16. data/doc/multiple_files.md +1 -1
  17. data/doc/paperclip.md +28 -9
  18. data/doc/plugins/add_metadata.md +112 -35
  19. data/doc/plugins/atomic_helpers.md +41 -3
  20. data/doc/plugins/backgrounding.md +12 -2
  21. data/doc/plugins/column.md +36 -7
  22. data/doc/plugins/default_url.md +6 -3
  23. data/doc/plugins/derivatives.md +83 -44
  24. data/doc/plugins/download_endpoint.md +5 -5
  25. data/doc/plugins/dynamic_storage.md +1 -1
  26. data/doc/plugins/entity.md +12 -4
  27. data/doc/plugins/form_assign.md +5 -5
  28. data/doc/plugins/included.md +25 -5
  29. data/doc/plugins/infer_extension.md +9 -0
  30. data/doc/plugins/instrumentation.md +1 -1
  31. data/doc/plugins/metadata_attributes.md +1 -0
  32. data/doc/plugins/mirroring.md +1 -1
  33. data/doc/plugins/model.md +8 -3
  34. data/doc/plugins/persistence.md +10 -1
  35. data/doc/plugins/remote_url.md +6 -1
  36. data/doc/plugins/remove_invalid.md +9 -1
  37. data/doc/plugins/sequel.md +1 -1
  38. data/doc/plugins/store_dimensions.md +10 -0
  39. data/doc/plugins/type_predicates.md +96 -0
  40. data/doc/plugins/upload_endpoint.md +1 -1
  41. data/doc/plugins/upload_options.md +1 -1
  42. data/doc/plugins/url_options.md +4 -4
  43. data/doc/plugins/validation.md +14 -4
  44. data/doc/plugins/versions.md +7 -7
  45. data/doc/processing.md +287 -123
  46. data/doc/refile.md +9 -9
  47. data/doc/release_notes/2.8.0.md +1 -1
  48. data/doc/release_notes/3.0.0.md +1 -1
  49. data/doc/release_notes/3.2.0.md +96 -0
  50. data/doc/release_notes/3.2.1.md +31 -0
  51. data/doc/release_notes/3.2.2.md +14 -0
  52. data/doc/release_notes/3.3.0.md +105 -0
  53. data/doc/release_notes/3.4.0.md +35 -0
  54. data/doc/securing_uploads.md +2 -2
  55. data/doc/storage/memory.md +19 -0
  56. data/doc/storage/s3.md +104 -77
  57. data/doc/testing.md +12 -2
  58. data/doc/upgrading_to_3.md +99 -53
  59. data/lib/shrine.rb +9 -8
  60. data/lib/shrine/attacher.rb +20 -10
  61. data/lib/shrine/attachment.rb +2 -2
  62. data/lib/shrine/plugins.rb +22 -0
  63. data/lib/shrine/plugins/activerecord.rb +3 -3
  64. data/lib/shrine/plugins/add_metadata.rb +20 -5
  65. data/lib/shrine/plugins/backgrounding.rb +2 -2
  66. data/lib/shrine/plugins/default_url.rb +1 -1
  67. data/lib/shrine/plugins/derivation_endpoint.rb +13 -8
  68. data/lib/shrine/plugins/derivatives.rb +59 -30
  69. data/lib/shrine/plugins/determine_mime_type.rb +5 -3
  70. data/lib/shrine/plugins/entity.rb +12 -11
  71. data/lib/shrine/plugins/instrumentation.rb +12 -18
  72. data/lib/shrine/plugins/mirroring.rb +8 -8
  73. data/lib/shrine/plugins/model.rb +3 -3
  74. data/lib/shrine/plugins/presign_endpoint.rb +16 -4
  75. data/lib/shrine/plugins/pretty_location.rb +1 -1
  76. data/lib/shrine/plugins/processing.rb +1 -1
  77. data/lib/shrine/plugins/refresh_metadata.rb +2 -2
  78. data/lib/shrine/plugins/remote_url.rb +3 -3
  79. data/lib/shrine/plugins/remove_attachment.rb +5 -0
  80. data/lib/shrine/plugins/remove_invalid.rb +10 -5
  81. data/lib/shrine/plugins/sequel.rb +1 -1
  82. data/lib/shrine/plugins/store_dimensions.rb +4 -2
  83. data/lib/shrine/plugins/type_predicates.rb +113 -0
  84. data/lib/shrine/plugins/upload_endpoint.rb +10 -5
  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/memory.rb +5 -3
  90. data/lib/shrine/storage/s3.rb +117 -38
  91. data/lib/shrine/version.rb +1 -1
  92. data/shrine.gemspec +8 -8
  93. metadata +42 -34
@@ -3,14 +3,48 @@ title: Atomic Helpers
3
3
  ---
4
4
 
5
5
  The [`atomic_helpers`][atomic_helpers] plugin provides API for retrieving and
6
- persisting attachments in a concurrency-safe way, which is useful when using
7
- the `backgrounding` plugin. The database plugins (`activerecord` and `sequel`)
8
- implement atomic promotion and atomic persistence on top of this plugin.
6
+ persisting attachments in a concurrency-safe way, which is especially useful
7
+ when using the `backgrounding` plugin. The database plugins (`activerecord`
8
+ and `sequel`) implement atomic promotion and atomic persistence on top of this
9
+ plugin.
9
10
 
10
11
  ```rb
11
12
  plugin :atomic_helpers
12
13
  ```
13
14
 
15
+ ## Problem Statement
16
+
17
+ What happens if two different processors (web workers, background jobs,
18
+ command-line executions, whatever) try to edit a shrine attachment
19
+ concurrently? The kinds of edits typically made include: "promoting a file",
20
+ moving it to a different storage and persisting that change in the model;
21
+ adding or changing a derivative; adding or changing a metadata element.
22
+
23
+ There are two main categories of "race condition":
24
+
25
+ 1. The file could be switched out from under you. If you were promoting a file,
26
+ but some other process has *changed* the attachment, you don't want to
27
+ overwrite it with the promomoted version of the *prior* attacchment. Likewise,
28
+ if you were adding metadata or a derivative, they would be corresponding to a
29
+ certain attachment, and you don't want to accidentally add them to a now changed
30
+ attacchment for which they are inappropriate.
31
+
32
+ 2. Overwriting each other's edits. Since all shrine (meta)data is stored in a
33
+ single JSON hash, standard implementations will write the entire JSON hash at
34
+ once to a rdbms column or other store. If two processes both read in the hash,
35
+ make a change to different keys in it, and then write it back out, the second
36
+ process to write will 'win' and overwrite changes made by the first.
37
+
38
+ The atomic helpers give you tools to avoid both of these sorts of race
39
+ conditions, under conditions of concurrent editing.
40
+
41
+ ## High-level ORM helpers
42
+
43
+ If you are using the `sequel` or `activerecord` plugins, they give you two
44
+ higher-level helpers: `atomic_persist` and `atomic_promote`. See the
45
+ [persistence] documentation for more.
46
+
47
+
14
48
  ## Retrieving
15
49
 
16
50
  The `Attacher.retrieve` method provided by the plugin instantiates an attacher
@@ -177,3 +211,7 @@ end
177
211
  ```
178
212
 
179
213
  [atomic_helpers]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/atomic_helpers.rb
214
+
215
+ [persistence]: https://shrinerb.com/docs/plugins/persistence
216
+
217
+ [backgrounding]: https://shrinerb.com/docs/plugins/backgrounding
@@ -187,12 +187,22 @@ and make the execution synchronous, you can override them on the attacher level
187
187
  and call the default behaviour:
188
188
 
189
189
  ```rb
190
- photo.image_attacher.promote_block(&:promote)
191
- photo.image_attacher.destroy_block(&:destroy)
190
+ photo.image_attacher.promote_block { promote } # promote synchronously
191
+ photo.image_attacher.destroy_block { destroy } # destroy synchronously
192
192
 
193
193
  # ... now promotion and deletion will be synchronous ...
194
194
  ```
195
195
 
196
+ You can also do this on the class level if you want to disable backgrounding
197
+ that was set up by a superclass:
198
+
199
+ ```rb
200
+ class MyUploader < Shrine
201
+ Attacher.promote_block { promote } # promote synchronously
202
+ Attacher.destroy_block { destroy } # destroy synchronously
203
+ end
204
+ ```
205
+
196
206
  [backgrounding]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/backgrounding.rb
197
207
  [derivatives]: https://shrinerb.com/docs/plugins/derivatives
198
208
  [atomic_helpers]: https://shrinerb.com/docs/plugins/atomic_helpers
@@ -66,23 +66,48 @@ If you want to load attachment from a Hash, use `Attacher.from_data` or
66
66
 
67
67
  ## Serializer
68
68
 
69
- By default the `JSON` standard library is used as the serializer, but you can
70
- use your own serializer. The serializer object needs to implement `#dump` and
71
- `#load` methods.
69
+ By default, the `JSON` standard library is used for serializing hash data. With
70
+ the [`model`][model] and [`entity`][entity] plugin, the data is serialized
71
+ before writing to and deserialized after reading from the data attribute.
72
+
73
+ You can also use your own serializer via the `:serializer` option. The
74
+ serializer object needs to implement `#dump` and `#load` methods:
75
+
76
+ ```rb
77
+ class MyDataSerializer
78
+ def self.dump(data)
79
+ data #=> { "id" => "...", "storage" => "...", "metadata" => { ... } }
80
+
81
+ JSON.generate(data) # serialize data, e.g. into JSON
82
+ end
83
+
84
+ def self.load(data)
85
+ data #=> '{"id":"...", "storage":"...", "metadata": {...}}'
86
+
87
+ JSON.parse(data) # deserialize data, e.g. from JSON
88
+ end
89
+ end
90
+
91
+ plugin :column, serializer: MyDataSerializer
92
+ ```
93
+
94
+ Some serialization libraries such as [Oj] and [MessagePack] already implement
95
+ this interface, which simplifies the configuration:
72
96
 
73
97
  ```rb
74
- require "oj"
98
+ require "oj" # https://github.com/ohler55/oj
75
99
 
76
- plugin :column, serializer: Oj # use custom serializer
100
+ plugin :column, serializer: Oj
77
101
  ```
78
102
 
79
- If you want to disable serialization, you can set serializer to `nil`.
103
+ If you want to disable serialization and work with hashes directly, you can set
104
+ `:serializer` to `nil`:
80
105
 
81
106
  ```rb
82
107
  plugin :column, serializer: nil # disable serialization
83
108
  ```
84
109
 
85
- You can also change the serializer on the attacher level:
110
+ The serializer can also be changed for a particular attacher instance:
86
111
 
87
112
  ```rb
88
113
  Shrine::Attacher.new(column_serializer: Oj) # use custom serializer
@@ -90,3 +115,7 @@ Shrine::Attacher.new(column_serializer: nil) # disable serialization
90
115
  ```
91
116
 
92
117
  [column]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/column.rb
118
+ [model]: https://shrinerb.com/docs/plugins/model
119
+ [entity]: https://shrinerb.com/docs/plugins/entity
120
+ [Oj]: https://github.com/ohler55/oj
121
+ [MessagePack]: https://github.com/msgpack/msgpack-ruby
@@ -8,7 +8,7 @@ returned when there is no attached file.
8
8
  ```rb
9
9
  plugin :default_url
10
10
 
11
- Attacher.default_url do |options|
11
+ Attacher.default_url do |**options|
12
12
  "/#{name}/missing.jpg"
13
13
  end
14
14
  ```
@@ -28,7 +28,7 @@ Any URL options passed will be available in the default URL block:
28
28
  attacher.url(foo: "bar")
29
29
  ```
30
30
  ```rb
31
- Attacher.default_url do |options|
31
+ Attacher.default_url do |**options|
32
32
  options #=> { foo: "bar" }
33
33
  end
34
34
  ```
@@ -37,12 +37,15 @@ The default URL block is evaluated in the context of an instance of
37
37
  `Shrine::Attacher`.
38
38
 
39
39
  ```rb
40
- Attacher.default_url do |options|
40
+ Attacher.default_url do |**options|
41
41
  self #=> #<Shrine::Attacher>
42
42
 
43
+ file #=> #<Shrine::UploadedFile>
43
44
  name #=> :avatar
44
45
  record #=> #<User>
45
46
  context #=> { ... }
47
+
48
+ # ...
46
49
  end
47
50
  ```
48
51
 
@@ -2,7 +2,7 @@
2
2
  title: Derivatives
3
3
  ---
4
4
 
5
- The derivatives plugin allows storing processed files ("derivatives") alongside
5
+ The [`derivatives`][derivatives] plugin allows storing processed files ("derivatives") alongside
6
6
  the main attached file. The processed file data will be saved together with the
7
7
  main attachment data in the same record attribute.
8
8
 
@@ -38,49 +38,43 @@ class ImageUploader < Shrine
38
38
  end
39
39
  ```
40
40
  ```rb
41
- class Photo < Model(:image_data)
42
- include ImageUploader::Attachment(:image)
43
- end
44
- ```
45
- ```rb
46
41
  photo = Photo.new(image: file)
47
- photo.image_derivatives! # calls derivatives processor and uploads results
42
+ photo.image_derivatives! # creates derivatives
48
43
  photo.save
49
44
  ```
50
45
 
51
- If you're allowing the attached file to be updated later on, in your update
52
- route make sure to create derivatives for new attachments:
46
+ You can then retrieve the URL of a processed derivative:
53
47
 
54
48
  ```rb
55
- photo.image_derivatives! if photo.image_changed?
56
- ```
57
-
58
- You can then retrieve created derivatives as follows:
59
-
60
- ```rb
61
- photo.image(:large) #=> #<Shrine::UploadedFile ...>
62
- photo.image(:large).url #=> "https://s3.amazonaws.com/path/to/large.jpg"
63
- photo.image(:large).size #=> 43843
64
- photo.image(:large).mime_type #=> "image/jpeg"
49
+ photo.image_url(:large) #=> "https://s3.amazonaws.com/path/to/large.jpg"
65
50
  ```
66
51
 
67
- The derivatives data is stored in the `#<name>_data` record attribute, alongside
68
- the main file data:
52
+ The derivatives data is stored in the `<attachment>_data` column alongside the
53
+ main file:
69
54
 
70
55
  ```rb
71
56
  photo.image_data #=>
72
57
  # {
73
- # "id": "original.jpg",
58
+ # "id": "path/to/original.jpg",
74
59
  # "store": "store",
75
60
  # "metadata": { ... },
76
61
  # "derivatives": {
77
- # "small": { "id": "small.jpg", "storage": "store", "metadata": { ... } },
78
- # "medium": { "id": "medium.jpg", "storage": "store", "metadata": { ... } },
79
- # "large": { "id": "large.jpg", "storage": "store", "metadata": { ... } },
62
+ # "small": { "id": "path/to/small.jpg", "storage": "store", "metadata": { ... } },
63
+ # "medium": { "id": "path/to/medium.jpg", "storage": "store", "metadata": { ... } },
64
+ # "large": { "id": "path/to/large.jpg", "storage": "store", "metadata": { ... } },
80
65
  # }
81
66
  # }
82
67
  ```
83
68
 
69
+ And they can be retrieved as `Shrine::UploadedFile` objects:
70
+
71
+ ```rb
72
+ photo.image(:large) #=> #<Shrine::UploadedFile id="path/to/large.jpg" storage=:store metadata={...}>
73
+ photo.image(:large).url #=> "https://s3.amazonaws.com/path/to/large.jpg"
74
+ photo.image(:large).size #=> 5825949
75
+ photo.image(:large).mime_type #=> "image/jpeg"
76
+ ```
77
+
84
78
  ## Retrieving derivatives
85
79
 
86
80
  The list of stored derivatives can be retrieved with `#<name>_derivatives`:
@@ -135,7 +129,7 @@ You can use the [`default_url`][default_url] plugin to set up URL fallbacks:
135
129
 
136
130
  ```rb
137
131
  Attacher.default_url do |derivative: nil, **|
138
- "https://my-app.com/fallbacks/#{derivative}.jpg" if derivative
132
+ "/fallbacks/#{derivative}.jpg" if derivative
139
133
  end
140
134
  ```
141
135
  ```rb
@@ -203,6 +197,19 @@ attacher.create_derivatives(different_source) # pass a different source file
203
197
  attacher.create_derivatives(foo: "bar") # pass custom options to the processor
204
198
  ```
205
199
 
200
+ ### Create on promote
201
+
202
+ You can also have derivatives created automatically on promotion:
203
+
204
+ ```rb
205
+ Shrine.plugin :derivatives, create_on_promote: true
206
+ ```
207
+ ```rb
208
+ attacher.assign(file)
209
+ attacher.finalize # creates derivatives on promotion
210
+ attacher.derivatives #=> { small: ..., medium: ..., large: ... }
211
+ ```
212
+
206
213
  ### Naming processors
207
214
 
208
215
  If you want to have multiple processors for an uploader, you can assign each
@@ -279,11 +286,14 @@ The storage block is evaluated in the context of a `Shrine::Attacher` instance:
279
286
 
280
287
  ```rb
281
288
  Attacher.derivatives_storage do |derivative|
282
- self #=> #<Shrine::Attacher>
289
+ self #=> #<Shrine::Attacher>
283
290
 
291
+ file #=> #<Shrine::UploadedFile>
284
292
  record #=> #<Photo>
285
293
  name #=> :image
286
294
  context #=> { ... }
295
+
296
+ # ...
287
297
  end
288
298
  ```
289
299
 
@@ -352,6 +362,7 @@ which allows you to change your processing logic based on the record data.
352
362
  Attacher.derivatives :my_processor do |original|
353
363
  self #=> #<Shrine::Attacher>
354
364
 
365
+ file #=> #<Shrine::UploadedFile>
355
366
  record #=> #<Photo>
356
367
  name #=> :image
357
368
  context #=> { ... }
@@ -390,7 +401,8 @@ attacher.process_derivatives(:my_processor) # downloads attached file and passes
390
401
 
391
402
  If you want to use a different source file, you can pass it in to the process
392
403
  call. Typically you'd pass a local file on disk. If you pass a
393
- `Shrine::UploadedFile` object, it will be automatically downloaded to disk.
404
+ `Shrine::UploadedFile` object or another IO-like object, it will be
405
+ automatically downloaded/copied to a local TempFile on disk.
394
406
 
395
407
  ```rb
396
408
  # named processor:
@@ -410,17 +422,31 @@ attacher.file.download do |original|
410
422
  end
411
423
  ```
412
424
 
425
+ If a processor might not always need a local source file, you avoid a
426
+ potentially expensive download/copy by registering the processor with
427
+ `download: false`, in which case the source file will be passed to the
428
+ processor as is.
429
+
430
+ ```rb
431
+ Attacher.derivatives :my_processor, download: false do |source|
432
+ source #=> Could be File, Shrine::UploadedFile, or other IO-like object
433
+ shrine_class.with_file(source) do |file|
434
+ # can force download/copy if necessary with `with_file`,
435
+ end
436
+ end
437
+ ```
438
+
413
439
  ## Adding derivatives
414
440
 
415
441
  If you already have processed files that you want to save, you can do that with
416
442
  `Attacher#add_derivatives`:
417
443
 
418
444
  ```rb
419
- attacher.add_derivatives(
445
+ attacher.add_derivatives({
420
446
  one: file_1,
421
447
  two: file_2,
422
448
  # ...
423
- )
449
+ })
424
450
 
425
451
  attacher.derivatives #=>
426
452
  # {
@@ -434,7 +460,7 @@ New derivatives will be merged with existing ones:
434
460
 
435
461
  ```rb
436
462
  attacher.derivatives #=> { one: #<Shrine::UploadedFile> }
437
- attacher.add_derivatives(two: two_file)
463
+ attacher.add_derivatives({ two: two_file })
438
464
  attacher.derivatives #=> { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> }
439
465
  ```
440
466
 
@@ -442,7 +468,7 @@ The merging is deep, so the following will work as well:
442
468
 
443
469
  ```rb
444
470
  attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile> } }
445
- attacher.add_derivatives(nested: { two: two_file })
471
+ attacher.add_derivatives({ nested: { two: two_file } })
446
472
  attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> } }
447
473
  ```
448
474
 
@@ -453,6 +479,11 @@ For adding a single derivative, you can also use the singular
453
479
  attacher.add_derivative(:thumb, thumbnail_file)
454
480
  ```
455
481
 
482
+ > Note that new derivatives will replace any existing derivatives living under
483
+ the same key, but won't delete them. If this is your case, make sure to save a
484
+ reference to the old derivatives before assigning new ones, and then delete
485
+ them after persisting the change.
486
+
456
487
  Any options passed to `Attacher#add_derivative(s)` will be forwarded to
457
488
  [`Attacher#upload_derivatives`](#uploading-derivatives).
458
489
 
@@ -469,11 +500,11 @@ If you want to upload processed files without setting them, you can use
469
500
  `Attacher#upload_derivatives`:
470
501
 
471
502
  ```rb
472
- derivatives = attacher.upload_derivatives(
503
+ derivatives = attacher.upload_derivatives({
473
504
  one: file_1,
474
505
  two: file_2,
475
506
  # ...
476
- )
507
+ })
477
508
 
478
509
  derivatives #=>
479
510
  # {
@@ -549,7 +580,7 @@ If you want to save already uploaded derivatives, you can use
549
580
 
550
581
  ```rb
551
582
  attacher.derivatives #=> { one: #<Shrine::UploadedFile> }
552
- attacher.merge_derivatives attacher.upload_derivatives(two: two_file)
583
+ attacher.merge_derivatives attacher.upload_derivatives({ two: two_file })
553
584
  attacher.derivatives #=> { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> }
554
585
  ```
555
586
 
@@ -557,10 +588,15 @@ This does a deep merge, so the following will work as well:
557
588
 
558
589
  ```rb
559
590
  attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile> } }
560
- attacher.merge_derivatives attacher.upload_derivatives(nested: { two: two_file })
591
+ attacher.merge_derivatives attacher.upload_derivatives({ nested: { two: two_file } })
561
592
  attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> } }
562
593
  ```
563
594
 
595
+ > Note that new derivatives will replace any existing derivatives living under
596
+ the same key, but won't delete them. If this is your case, make sure to save a
597
+ reference to the old derivatives before assigning new ones, and then delete
598
+ them after persisting the change.
599
+
564
600
  The `Attacher#merge_derivatives` method is thread-safe.
565
601
 
566
602
  ### Setting derivatives
@@ -570,7 +606,7 @@ If instead of adding you want to *override* existing derivatives, you can use
570
606
 
571
607
  ```rb
572
608
  attacher.derivatives #=> { one: #<Shrine::UploadedFile> }
573
- attacher.set_derivatives attacher.upload_derivatives(two: two_file)
609
+ attacher.set_derivatives attacher.upload_derivatives({ two: two_file })
574
610
  attacher.derivatives #=> { two: #<Shrine::UploadedFile> }
575
611
  ```
576
612
 
@@ -678,7 +714,7 @@ You can store derivatives even if there is no main attached file:
678
714
 
679
715
  ```rb
680
716
  attacher.file #=> nil
681
- attacher.add_derivatives(one: one_file, two: two_file)
717
+ attacher.add_derivatives({ one: one_file, two: two_file })
682
718
  attacher.data #=>
683
719
  # {
684
720
  # "derivatives" => {
@@ -756,11 +792,13 @@ plugin :derivatives
756
792
  Processing derivatives will trigger a `derivatives.shrine` event with the
757
793
  following payload:
758
794
 
759
- | Key | Description |
760
- | :-- | :---- |
761
- | `:processor` | Name of the derivatives processor |
762
- | `:processor_options` | Any options passed to the processor |
763
- | `:uploader` | The uploader class that sent the event |
795
+ | Key | Description |
796
+ | :-- | :---- |
797
+ | `:processor` | Name of the derivatives processor |
798
+ | `:processor_options` | Any options passed to the processor |
799
+ | `:io` | The source file passed to the processor |
800
+ | `:attacher` | The attacher instance doing the processing |
801
+ | `:uploader` | The uploader class that sent the event |
764
802
 
765
803
  A default log subscriber is added as well which logs these events:
766
804
 
@@ -776,7 +814,7 @@ plugin :derivatives, log_subscriber: -> (event) {
776
814
  }
777
815
  ```
778
816
  ```
779
- {"name":"derivatives","duration":2133,"processor":"thumbnails","processor_options":{},"uploader":"ImageUploader"}
817
+ {"name":"derivatives","duration":2133,"processor":"thumbnails","processor_options":{},"io":"#<File:...>","uploader":"ImageUploader"}
780
818
  ```
781
819
 
782
820
  Or disable logging altogether:
@@ -785,6 +823,7 @@ Or disable logging altogether:
785
823
  plugin :derivatives, log_subscriber: nil
786
824
  ```
787
825
 
826
+ [derivatives]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/derivatives.rb
788
827
  [default_url]: https://shrinerb.com/docs/plugins/default_url
789
828
  [entity]: https://shrinerb.com/docs/plugins/entity
790
829
  [model]: https://shrinerb.com/docs/plugins/model