shrine 3.0.1 → 3.1.0

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +7 -2
  5. data/doc/advantages.md +29 -12
  6. data/doc/carrierwave.md +54 -22
  7. data/doc/changing_derivatives.md +39 -39
  8. data/doc/getting_started.md +63 -58
  9. data/doc/multiple_files.md +5 -3
  10. data/doc/paperclip.md +92 -33
  11. data/doc/plugins/activerecord.md +1 -1
  12. data/doc/plugins/data_uri.md +2 -2
  13. data/doc/plugins/derivation_endpoint.md +26 -28
  14. data/doc/plugins/derivatives.md +170 -142
  15. data/doc/plugins/determine_mime_type.md +2 -2
  16. data/doc/plugins/infer_extension.md +2 -2
  17. data/doc/plugins/instrumentation.md +1 -1
  18. data/doc/plugins/metadata_attributes.md +21 -10
  19. data/doc/plugins/persistence.md +1 -0
  20. data/doc/plugins/refresh_metadata.md +5 -4
  21. data/doc/plugins/remote_url.md +2 -2
  22. data/doc/plugins/signature.md +11 -2
  23. data/doc/plugins/store_dimensions.md +2 -2
  24. data/doc/plugins/upload_endpoint.md +7 -11
  25. data/doc/plugins/validation_helpers.md +3 -3
  26. data/doc/processing.md +5 -5
  27. data/doc/refile.md +30 -9
  28. data/doc/release_notes/2.19.0.md +1 -1
  29. data/doc/release_notes/3.0.1.md +4 -0
  30. data/doc/release_notes/3.1.0.md +73 -0
  31. data/doc/securing_uploads.md +1 -1
  32. data/doc/storage/file_system.md +1 -1
  33. data/doc/storage/s3.md +1 -5
  34. data/doc/upgrading_to_3.md +4 -2
  35. data/doc/validation.md +3 -2
  36. data/lib/shrine.rb +1 -2
  37. data/lib/shrine/attacher.rb +4 -4
  38. data/lib/shrine/attachment.rb +3 -3
  39. data/lib/shrine/plugins/add_metadata.rb +1 -5
  40. data/lib/shrine/plugins/default_storage.rb +6 -6
  41. data/lib/shrine/plugins/derivatives.rb +4 -3
  42. data/lib/shrine/plugins/signature.rb +7 -6
  43. data/lib/shrine/plugins/store_dimensions.rb +18 -9
  44. data/lib/shrine/uploaded_file.rb +0 -1
  45. data/lib/shrine/version.rb +2 -2
  46. metadata +3 -2
@@ -14,7 +14,7 @@ relationship with the main table, and files will be attached on the records in
14
14
  the new table. That way each record from the main table can implicitly have
15
15
  multiple attachments through the associated records.
16
16
 
17
- ```plaintext
17
+ ```
18
18
  album1
19
19
  photo1
20
20
  - attachment1
@@ -74,13 +74,15 @@ Sequel.migration do
74
74
  change do
75
75
  create_table :albums do
76
76
  primary_key :id
77
- column :title, :text
77
+
78
+ String :title
78
79
  end
79
80
 
80
81
  create_table :photos do
81
82
  primary_key :id
82
83
  foreign_key :album_id, :albums
83
- column :image_data, :text
84
+
85
+ String :image_data
84
86
  end
85
87
  end
86
88
  end
@@ -6,7 +6,7 @@ This guide is aimed at helping Paperclip users transition to Shrine, and it
6
6
  consists of three parts:
7
7
 
8
8
  1. Explanation of the key differences in design between Paperclip and Shrine
9
- 2. Instructions how to migrate and existing app that uses Paperclip to Shrine
9
+ 2. Instructions how to migrate an existing app that uses Paperclip to Shrine
10
10
  3. Extensive reference of Paperclip's interface with Shrine equivalents
11
11
 
12
12
  ## Overview
@@ -182,7 +182,7 @@ require "image_processing/mini_magick"
182
182
  class ImageUploader < Shrine
183
183
  plugin :derivatives
184
184
 
185
- Attacher.derivatives_processor do |original|
185
+ Attacher.derivatives do |original|
186
186
  magick = ImageProcessing::MiniMagick.source(original)
187
187
 
188
188
  {
@@ -297,16 +297,21 @@ file.mime_type #=> "application/x-php"
297
297
  ## Migrating from Paperclip
298
298
 
299
299
  You have an existing app using Paperclip and you want to transfer it to Shrine.
300
- First we need to make new uploads write to the `<attachment>_data` column.
301
- Let's assume we have a `Photo` model with the "image" attachment:
300
+ Let's assume we have a `Photo` model with the "image" attachment.
301
+
302
+ ### 1. Add Shrine column
303
+
304
+ First we need to create the `image_data` column for Shrine:
302
305
 
303
306
  ```rb
304
307
  add_column :photos, :image_data, :text
305
308
  ```
306
309
 
307
- Afterwards we need to make new uploads write to the `image_data` column. This
308
- can be done by including the below module to all models that have Paperclip
309
- attachments:
310
+ ### 2. Dual write
311
+
312
+ Next, we need to make new Paperclip attachments write to the `image_data`
313
+ column. This can be done by including the below module to all models that have
314
+ Paperclip attachments:
310
315
 
311
316
  ```rb
312
317
  require "shrine"
@@ -318,8 +323,7 @@ Shrine.storages = {
318
323
 
319
324
  Shrine.plugin :model
320
325
  Shrine.plugin :derivatives
321
- ```
322
- ```rb
326
+
323
327
  module PaperclipShrineSynchronization
324
328
  def self.included(model)
325
329
  model.before_save do
@@ -336,8 +340,8 @@ module PaperclipShrineSynchronization
336
340
  if attachment.size.present?
337
341
  attacher.set shrine_file(attachment)
338
342
 
339
- attachment.styles.each do |name, style|
340
- attacher.merge_derivatives(name => shrine_file(style))
343
+ attachment.styles.each do |style_name, style|
344
+ attacher.merge_derivatives(style_name => shrine_file(style))
341
345
  end
342
346
  else
343
347
  attacher.set nil
@@ -372,7 +376,7 @@ module PaperclipShrineSynchronization
372
376
  # If you'll be using a `:prefix` on your Shrine storage, or you're storing
373
377
  # files on the filesystem, make sure to subtract the appropriate part
374
378
  # from the path assigned to `:id`.
375
- def style_to_shrine_data(style)
379
+ def shrine_style_file(style)
376
380
  Shrine.uploaded_file(
377
381
  storage: :store,
378
382
  id: style.attachment.path(style.name),
@@ -389,34 +393,35 @@ end
389
393
  ```
390
394
 
391
395
  After you deploy this code, the `image_data` column should now be successfully
392
- synchronized with new attachments. Next step is to run a script which writes
393
- all existing Paperclip attachments to `image_data`:
396
+ synchronized with new attachments.
397
+
398
+ ### 3. Data migration
399
+
400
+ Next step is to run a script which writes all existing Paperclip attachments to
401
+ `image_data`:
394
402
 
395
403
  ```rb
396
404
  Photo.find_each do |photo|
397
- Paperclip::AttachmentRegistry.each_definition do |klass, name, options|
398
- photo.write_shrine_data(name) if klass == Photo
399
- end
405
+ photo.write_shrine_data(:image)
400
406
  photo.save!
401
407
  end
402
408
  ```
403
409
 
410
+ ### 4. Rewrite code
411
+
404
412
  Now you should be able to rewrite your application so that it uses Shrine
405
- instead of Paperclip, using equivalent Shrine storages. For help with
406
- translating the code from Paperclip to Shrine, you can consult the reference
407
- below.
413
+ instead of Paperclip (you can consult the reference in the next section). You
414
+ can remove the `PaperclipShrineSynchronization` module as well.
408
415
 
409
- You'll notice that Shrine metadata will be absent from the migrated files' data
410
- (specifically versions). You can run a script that will fill in any missing
411
- metadata defined in your Shrine uploader:
416
+ ### 5. Remove Paperclip columns
412
417
 
413
- ```rb
414
- Shrine.plugin :refresh_metadata
418
+ If everything is looking good, we can remove Paperclip columns:
415
419
 
416
- Photo.find_each do |photo|
417
- photo.image_attacher.refresh_metadata!
418
- photo.save
419
- end
420
+ ```rb
421
+ remove_column :photos, :image_file_name
422
+ remove_column :photos, :image_file_size
423
+ remove_column :photos, :image_content_type
424
+ remove_column :photos, :image_updated_at
420
425
  ```
421
426
 
422
427
  ## Paperclip to Shrine direct mapping
@@ -457,8 +462,8 @@ Processing is defined by using the `derivatives` plugin:
457
462
  class ImageUploader < Shrine
458
463
  plugin :derivatives
459
464
 
460
- Attacher.derivatives_processor do |original|
461
- magick = ImageProcessing::MiniMagick.source(image)
465
+ Attacher.derivatives do |original|
466
+ magick = ImageProcessing::MiniMagick.source(original)
462
467
 
463
468
  {
464
469
  large: magick.resize_to_limit!(800, 800),
@@ -477,8 +482,8 @@ For default URLs you can use the `default_url` plugin:
477
482
  class ImageUploader < Shrine
478
483
  plugin :default_url
479
484
 
480
- Attacher.default_url do |options|
481
- "/attachments/#{name}/default.jpg"
485
+ Attacher.default_url do |derivative: nil, **|
486
+ "/images/placeholders/#{derivative || "original"}.jpg"
482
487
  end
483
488
  end
484
489
  ```
@@ -516,6 +521,60 @@ end
516
521
 
517
522
  Shrine has this functionality in the `determine_mime_type` plugin.
518
523
 
524
+ ### `validates_attachment`
525
+
526
+ #### `:presence`
527
+
528
+ For presence validation you can use your ORM's presence validator:
529
+
530
+ ```rb
531
+ class Photo < ActiveRecord::Base
532
+ include ImageUploader::Attachment(:image)
533
+ validates_presence_of :image
534
+ end
535
+ ```
536
+
537
+ #### `:content_type`
538
+
539
+ You can do MIME type validation with Shrine's `validation_helpers` plugin:
540
+
541
+ ```rb
542
+ class ImageUploader < Shrine
543
+ plugin :validation_helpers
544
+
545
+ Attacher.validate do
546
+ validate_mime_type %w[image/jpeg image/png image/webp]
547
+ end
548
+ end
549
+ ```
550
+
551
+ Make sure to also load the `determine_mime_type` plugin to detect MIME type
552
+ from file content.
553
+
554
+ ```rb
555
+ # Gemfile
556
+ gem "mimemagic"
557
+ ```
558
+ ```rb
559
+ Shrine.plugin :determine_mime_type, analyzer: -> (io, analyzers) do
560
+ analyzers[:mimemagic].call(io) || analyzers[:file].call(io)
561
+ end
562
+ ```
563
+
564
+ #### `:size`
565
+
566
+ You can do filesize validation with Shrine's `validation_helpers` plugin:
567
+
568
+ ```rb
569
+ class ImageUploader < Shrine
570
+ plugin :validation_helpers
571
+
572
+ Attacher.validate do
573
+ validate_max_size 10*1024*1024
574
+ end
575
+ end
576
+ ```
577
+
519
578
  ### `Paperclip::Attachment`
520
579
 
521
580
  This section explains the equivalent of Paperclip attachment's methods, in
@@ -158,7 +158,7 @@ end
158
158
  ```
159
159
  ```yml
160
160
  en:
161
- activerecord
161
+ activerecord:
162
162
  errors:
163
163
  models:
164
164
  photo:
@@ -123,7 +123,7 @@ payload:
123
123
 
124
124
  A default log subscriber is added as well which logs these events:
125
125
 
126
- ```plaintext
126
+ ```
127
127
  Data URI (5ms) – {:uploader=>Shrine}
128
128
  ```
129
129
 
@@ -134,7 +134,7 @@ plugin :data_uri, log_subscriber: -> (event) {
134
134
  Shrine.logger.info JSON.generate(name: event.name, duration: event.duration, uploader: event[:uploader])
135
135
  }
136
136
  ```
137
- ```plaintext
137
+ ```
138
138
  {"name":"data_uri","duration":5,"uploader":"Shrine"}
139
139
  ```
140
140
 
@@ -68,7 +68,7 @@ generates an URL consisting of the configured [path prefix](#prefix),
68
68
  derivation name and arguments, serialized uploaded file, and an URL signature
69
69
  generated using the configured secret key:
70
70
 
71
- ```plaintext
71
+ ```
72
72
  / derivations/image / thumbnail / 600/400 / eyJmZvbyIb3JhZ2UiOiJzdG9yZSJ9 ? signature=...
73
73
  └──── prefix ─────┘ └── name ──┘ └─ args ─┘ └─── serialized source file ───┘
74
74
  ```
@@ -197,7 +197,8 @@ Rails.application.routes.draw do
197
197
  end
198
198
  end
199
199
  end
200
-
200
+ ```
201
+ ```rb
201
202
  # app/controllers/photos_controller.rb
202
203
  class PhotosController < ApplicationController
203
204
  def thumbnail
@@ -483,32 +484,27 @@ such as AWS S3 or Google Cloud Storage.
483
484
  ### Deleting derivatives
484
485
 
485
486
  When the original attachment is deleted, its uploaded derivatives will not be
486
- automatically deleted, you will need to do the deletion manually. To ensure
487
- this gets called both on destroying and replacing, you can add that code to
488
- `Attacher#destroy`.
487
+ automatically deleted, you will need to do the deletion manually. If you're
488
+ using [backgrounding], you can do this in your `DestroyJob`.
489
489
 
490
- The easiest way is to delete the directory containing your derivatives:
490
+ If your storage implements `#delete_prefixed`, and you're using the default
491
+ [`:upload_location`](#upload-location), you can delete the directory containing
492
+ derivatives:
491
493
 
492
494
  ```rb
493
- class ImageUploader < Shrine
494
- class Attacher
495
- def destroy(*args)
496
- super
495
+ class DestroyJob < ActiveJob::Base
496
+ def perform(attacher_class, data)
497
+ # ... destroy attached file ...
497
498
 
498
- derivatives_directory = file.id
499
- storage = store.storage
499
+ derivatives_directory = attacher.file.id + "/"
500
+ storage = attacher.store.storage
500
501
 
501
- storage.delete_prefixed(derivatives_directory)
502
- end
502
+ storage.delete_prefixed(derivatives_directory)
503
503
  end
504
504
  end
505
505
  ```
506
506
 
507
- This is under the assumption that your storage implements `#delete_prefixed`
508
- and that you're using default [`:upload_location`](#upload-location).
509
-
510
- Alternatively, you can delete each derivative individually using
511
- `Derivation#delete`:
507
+ Alternatively, you can delete each derivative individually:
512
508
 
513
509
  ```rb
514
510
  class ImageUploader < Shrine
@@ -518,14 +514,15 @@ class ImageUploader < Shrine
518
514
  [:thumbnail, 400, 300],
519
515
  ...
520
516
  ]
517
+ end
518
+ ```
519
+ ```rb
520
+ class DestroyJob < ActiveJob::Base
521
+ def perform(attacher_class, data)
522
+ # ... destroy attached file ...
521
523
 
522
- class Attacher
523
- def destroy(*args)
524
- super
525
-
526
- DERIVATIONS.each do |args|
527
- file.derivation(*args).delete
528
- end
524
+ attacher.shrine_class::DERIVATIONS.each do |args|
525
+ attacher.file.derivation(*args).delete
529
526
  end
530
527
  end
531
528
  end
@@ -830,7 +827,7 @@ following payload:
830
827
 
831
828
  A default log subscriber is added as well which logs these events:
832
829
 
833
- ```plaintext
830
+ ```
834
831
  Derivation (492ms) – {:name=>:thumbnail, :args=>[600, 600], :uploader=>Shrine}
835
832
  ```
836
833
 
@@ -846,7 +843,7 @@ plugin :derivation_endpoint, log_subscriber: -> (event) {
846
843
  )
847
844
  }
848
845
  ```
849
- ```plaintext
846
+ ```
850
847
  {"name":"derivation","duration":492,"name":"thumbnail","args":[600,600],"uploader":"Shrine"}
851
848
  ```
852
849
 
@@ -861,3 +858,4 @@ plugin :derivation_endpoint, log_subscriber: nil
861
858
  [`Content-Type`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
862
859
  [`Content-Disposition`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition
863
860
  [`Cache-Control`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
861
+ [backgrounding]: https://shrinerb.com/docs/plugins/backgrounding
@@ -10,12 +10,11 @@ main attachment data in the same record attribute.
10
10
  Shrine.plugin :derivatives
11
11
  ```
12
12
 
13
- ## Creating derivatives
13
+ ## Quick start
14
14
 
15
- When you have a file attached, you can generate derivatives from it and save
16
- them alongside the attached file. The simplest way to do this is to define a
17
- processor which returns the processed files, and then trigger it when you want
18
- to create derivatives.
15
+ You'll usually want to create derivatives from an attached file. The simplest
16
+ way to do this is to define a processor which returns the processed files, and
17
+ then trigger it when you want to create derivatives.
19
18
 
20
19
  Here is an example of generating image thumbnails:
21
20
 
@@ -27,7 +26,7 @@ gem "image_processing", "~> 1.8"
27
26
  require "image_processing/mini_magick"
28
27
 
29
28
  class ImageUploader < Shrine
30
- Attacher.derivatives_processor do |original|
29
+ Attacher.derivatives do |original|
31
30
  magick = ImageProcessing::MiniMagick.source(original)
32
31
 
33
32
  {
@@ -56,8 +55,17 @@ route make sure to create derivatives for new attachments:
56
55
  photo.image_derivatives! if photo.image_changed?
57
56
  ```
58
57
 
59
- Once derivatives have been created, their data is stored in the `#<name>_data`
60
- record attribute alongside the main file data:
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"
65
+ ```
66
+
67
+ The derivatives data is stored in the `#<name>_data` record attribute, alongside
68
+ the main file data:
61
69
 
62
70
  ```rb
63
71
  photo.image_data #=>
@@ -73,22 +81,112 @@ photo.image_data #=>
73
81
  # }
74
82
  ```
75
83
 
76
- You can then retrieve derivatives as follows:
84
+ ## Retrieving derivatives
85
+
86
+ The list of stored derivatives can be retrieved with `#<name>_derivatives`:
77
87
 
78
88
  ```rb
79
- photo.image(:large) #=> #<Shrine::UploadedFile>
80
- photo.image(:large).url #=> "https://s3.amazonaws.com/path/to/large.jpg"
81
- photo.image(:large).size #=> 43843
82
- photo.image(:large).mime_type #=> "image/jpeg"
89
+ photo.image_derivatives #=>
90
+ # {
91
+ # small: #<Shrine::UploadedFile ...>,
92
+ # medium: #<Shrine::UploadedFile ...>,
93
+ # large: #<Shrine::UploadedFile ...>,
94
+ # }
95
+ ```
96
+
97
+ A specific derivative can be retrieved in any of the following ways:
98
+
99
+ ```rb
100
+ photo.image_derivatives[:small] #=> #<Shrine::UploadedFile ...>
101
+ photo.image_derivatives(:small) #=> #<Shrine::UploadedFile ...>
102
+ photo.image(:small) #=> #<Shrine::UploadedFile ...>
103
+ ```
104
+
105
+ Or with nested derivatives:
106
+
107
+ ```rb
108
+ photo.image_derivatives #=> { thumbnail: { small: ..., medium: ..., large: ... } }
109
+
110
+ photo.image_derivatives.dig(:thumbnail, :small) #=> #<Shrine::UploadedFile ...>
111
+ photo.image_derivatives(:thumbnail, :small) #=> #<Shrine::UploadedFile ...>
112
+ photo.image(:thumbnails, :small) #=> #<Shrine::UploadedFile ...>
113
+ ```
114
+
115
+ ### Derivative URL
116
+
117
+ You can retrieve the URL of a derivative URL with `#<name>_url`:
118
+
119
+ ```rb
120
+ photo.image_url(:small) #=> "https://example.com/small.jpg"
121
+ photo.image_url(:medium) #=> "https://example.com/medium.jpg"
122
+ photo.image_url(:large) #=> "https://example.com/large.jpg"
123
+ ```
124
+
125
+ For nested derivatives you can pass multiple keys:
126
+
127
+ ```rb
128
+ photo.image_derivatives #=> { thumbnail: { small: ..., medium: ..., large: ... } }
129
+
130
+ photo.image_url(:thumbnail, :medium) #=> "https://example.com/medium.jpg"
131
+ ```
132
+
133
+ By default, `#<name>_url` method will return `nil` if derivative is not found.
134
+ You can use the [`default_url`][default_url] plugin to set up URL fallbacks:
135
+
136
+ ```rb
137
+ Attacher.default_url do |derivative: nil, **|
138
+ "https://my-app.com/fallbacks/#{derivative}.jpg" if derivative
139
+ end
140
+ ```
141
+ ```rb
142
+ photo.image_url(:medium) #=> "https://example.com/fallbacks.com/medium.jpg"
83
143
  ```
84
144
 
85
- The `#<name>_derivatives!` model method delegates to
86
- `Attacher#create_derivatives`, which you can use if you're using
87
- `Shrine::Attacher` directly:
145
+ Any additional URL options passed to `#<name>_url` will be forwarded to the
146
+ storage:
147
+
148
+ ```rb
149
+ photo.image_url(:small, response_content_disposition: "attachment")
150
+ ```
151
+
152
+ You can also retrieve the derivative URL via `UploadedFile#url`:
153
+
154
+ ```rb
155
+ photo.image_derivatives[:large].url
156
+ ```
157
+
158
+ ## Attacher API
159
+
160
+ The derivatives API is primarily defined on the `Shrine::Attacher` class, with
161
+ some important methods also being exposed through the `Shrine::Attachment`
162
+ module.
163
+
164
+ Here is a model example with equivalent attacher code:
165
+
166
+ ```rb
167
+ photo.image_derivatives!(:thumbnails)
168
+ photo.image_derivatives #=> { ... }
169
+
170
+ photo.image_url(:large) #=> "https://..."
171
+ photo.image(:large) #=> #<Shrine::UploadedFile ...>
172
+ ```
173
+ ```rb
174
+ attacher.create_derivatives(:thumbnails)
175
+ attacher.get_derivatives #=> { ... }
176
+
177
+ attacher.url(:large) #=> "https://..."
178
+ attacher.get(:large) #=> "#<Shrine::UploadedFile>"
179
+ ```
180
+
181
+ ## Creating derivatives
182
+
183
+ By default, the `Attacher#create_derivatives` method downloads the attached
184
+ file, calls the processor, uploads results to attacher's permanent storage, and
185
+ saves uploaded files on the attacher.
88
186
 
89
187
  ```rb
90
188
  attacher.file #=> #<Shrine::UploadedFile id="original.jpg" storage=:store ...>
91
- attacher.create_derivatives # calls registered processor and uploads results
189
+ attacher.create_derivatives # calls default processor and uploads results
92
190
  attacher.derivatives #=>
93
191
  # {
94
192
  # small: #<Shrine::UploadedFile id="small.jpg" storage=:store ...>,
@@ -97,9 +195,7 @@ attacher.derivatives #=>
97
195
  # }
98
196
  ```
99
197
 
100
- By default, the `Attacher#create_derivatives` method downloads the attached
101
- file, calls the processor, uploads results to attacher's permanent storage, and
102
- saves uploaded files on the attacher. Any additional arguments are forwarded to
198
+ Any additional arguments are forwarded to
103
199
  [`Attacher#process_derivatives`](#processing-derivatives):
104
200
 
105
201
  ```rb
@@ -110,32 +206,43 @@ attacher.create_derivatives(foo: "bar") # pass custom options to the proce
110
206
  ### Naming processors
111
207
 
112
208
  If you want to have multiple processors for an uploader, you can assign each
113
- processor a name. Then when creating derivatives you can specify the name of
114
- the desired processor.
209
+ processor a name:
115
210
 
116
211
  ```rb
117
212
  class ImageUploader < Shrine
118
- Attacher.derivatives_processor :thumbnails do |original|
119
- # ...
213
+ Attacher.derivatives :thumbnails do |original|
214
+ { large: ..., medium: ..., small: ... }
120
215
  end
121
216
 
122
- Attacher.derivatives_processor :crop do |original|
123
- # ...
217
+ Attacher.derivatives :crop do |original|
218
+ { cropped: ... }
124
219
  end
125
-
126
- # ...
127
220
  end
128
221
  ```
222
+
223
+ Then when creating derivatives you can specify the name of the desired
224
+ processor. New derivatives will be merged with any existing ones.
225
+
129
226
  ```rb
130
- photo.image_derivatives!(:thumbnails)
131
- # or
132
227
  attacher.create_derivatives(:thumbnails)
228
+ attacher.derivatives #=> { large: ..., medium: ..., small: ... }
229
+
230
+ attacher.create_derivatives(:crop)
231
+ attacher.derivatives #=> { large: ..., medium: ..., small: ..., cropped: ... }
133
232
  ```
134
233
 
135
234
  ### Derivatives storage
136
235
 
137
236
  By default, derivatives are uploaded to the permanent storage of the attacher.
138
- You can change the default destination storage with the `:storage` plugin
237
+ You can change the destination storage by passing `:storage` to the creation
238
+ call:
239
+
240
+ ```rb
241
+ attacher.create_derivatives(storage: :cache) # will be promoted together with main file
242
+ attacher.create_derivatives(storage: :other_store)
243
+ ```
244
+
245
+ You can also change the default destination storage with the `:storage` plugin
139
246
  option:
140
247
 
141
248
  ```rb
@@ -186,7 +293,7 @@ Derivatives can be nested to any level, using both hashes and arrays, but the
186
293
  top-level object must be a hash.
187
294
 
188
295
  ```rb
189
- Attacher.derivatives_processor :tiff do |original|
296
+ Attacher.derivatives :tiff do |original|
190
297
  {
191
298
  thumbnail: {
192
299
  small: small,
@@ -201,114 +308,29 @@ Attacher.derivatives_processor :tiff do |original|
201
308
  }
202
309
  end
203
310
  ```
204
-
205
- ## Retrieving derivatives
206
-
207
- If you're using the `Shrine::Attachment` module, you can retrieve stored
208
- derivatives by calling `#<name>_derivatives` on your model/entity.
209
-
210
- ```rb
211
- class Photo < Model(:image_data)
212
- include ImageUploader::Attachment(:image)
213
- end
214
- ```
215
- ```rb
216
- photo.image_derivatives #=>
217
- # {
218
- # small: #<Shrine::UploadedFile>,
219
- # medium: #<Shrine::UploadedFile>,
220
- # large: #<Shrine::UploadedFile>,
221
- # }
222
- ```
223
-
224
- A specific derivative can be retrieved in any of the following ways:
225
-
226
- ```rb
227
- photo.image_derivatives[:small] #=> #<Shrine::UploadedFile>
228
- photo.image_derivatives(:small) #=> #<Shrine::UploadedFile>
229
- photo.image(:small) #=> #<Shrine::UploadedFile>
230
- ```
231
-
232
- And with nested derivatives:
233
-
234
- ```rb
235
- photo.image_derivatives #=> { thumbnail: { small: ..., medium: ..., large: ... } }
236
-
237
- photo.image_derivatives.dig(:thumbnail, :small) #=> #<Shrine::UploadedFile>
238
- photo.image_derivatives(:thumbnail, :small) #=> #<Shrine::UploadedFile>
239
- photo.image(:thumbnails, :small) #=> #<Shrine::UploadedFile>
240
- ```
241
-
242
- When using `Shrine::Attacher` directly, you can retrieve derivatives using
243
- `Attacher#derivatives`:
244
-
245
311
  ```rb
246
312
  attacher.derivatives #=>
247
313
  # {
248
- # small: #<Shrine::UploadedFile>,
249
- # medium: #<Shrine::UploadedFile>,
250
- # large: #<Shrine::UploadedFile>,
314
+ # thumbnail: {
315
+ # small: #<Shrine::UploadedFile ...>,
316
+ # medium: #<Shrine::UploadedFile ...>,
317
+ # large: #<Shrine::UploadedFile ...>,
318
+ # },
319
+ # layers: [
320
+ # #<Shrine::UploadedFile ...>,
321
+ # #<Shrine::UploadedFile ...>,
322
+ # # ...
323
+ # ]
251
324
  # }
252
325
  ```
253
326
 
254
- ## Derivative URL
255
-
256
- If you're using the `Shrine::Attachment` module, you can use the `#<name>_url`
257
- method to retrieve the URL of a derivative.
258
-
259
- ```rb
260
- class Photo < Model(:image_data)
261
- include ImageUploader::Attachment(:image)
262
- end
263
- ```
264
- ```rb
265
- photo.image_url(:small) #=> "https://example.com/small.jpg"
266
- photo.image_url(:medium) #=> "https://example.com/medium.jpg"
267
- photo.image_url(:large) #=> "https://example.com/large.jpg"
268
- ```
269
-
270
- For nested derivatives you can pass multiple keys:
271
-
272
- ```rb
273
- photo.image_derivatives #=> { thumbnail: { small: ..., medium: ..., large: ... } }
274
-
275
- photo.image_url(:thumbnail, :medium) #=> "https://example.com/medium.jpg"
276
- ```
277
-
278
- By default, `#<name>_url` method will return `nil` if derivative is not found.
279
- You can use the [`default_url`][default_url] plugin to set up URL fallbacks:
280
-
281
- ```rb
282
- Attacher.default_url do |derivative: nil, **|
283
- "https://my-app.com/fallbacks/#{derivative}.jpg" if derivative
284
- end
285
- ```
286
- ```rb
287
- photo.image_url(:medium) #=> "https://example.com/fallbacks.com/medium.jpg"
288
- ```
289
-
290
- Any additional URL options passed to `#<name>_url` will be forwarded to the
291
- storage:
292
-
293
- ```rb
294
- photo.image_url(:small, response_content_disposition: "attachment")
295
- ```
296
-
297
- You can also retrieve the derivative URL via `UploadedFile#url`:
298
-
299
- ```rb
300
- photo.image_derivatives[:large].url
301
- # or
302
- attacher.derivatives[:large].url
303
- ```
304
-
305
327
  ## Processing derivatives
306
328
 
307
329
  A derivatives processor block takes the original file, and is expected to
308
330
  return a hash of processed files (it can be [nested](#nesting-derivatives)).
309
331
 
310
332
  ```rb
311
- Attacher.derivatives_processor :my_processor do |original|
333
+ Attacher.derivatives :my_processor do |original|
312
334
  # return a hash of processed files
313
335
  end
314
336
  ```
@@ -327,7 +349,7 @@ The processor block is evaluated in context of the `Shrine::Attacher` instance,
327
349
  which allows you to change your processing logic based on the record data.
328
350
 
329
351
  ```rb
330
- Attacher.derivatives_processor :my_processor do |original|
352
+ Attacher.derivatives :my_processor do |original|
331
353
  self #=> #<Shrine::Attacher>
332
354
 
333
355
  record #=> #<Photo>
@@ -345,7 +367,7 @@ forwarded to the processor:
345
367
  attacher.process_derivatives(:my_processor, foo: "bar")
346
368
  ```
347
369
  ```rb
348
- Attacher.derivatives_processor :my_processor do |original, **options|
370
+ Attacher.derivatives :my_processor do |original, **options|
349
371
  options #=> { :foo => "bar" }
350
372
  # ...
351
373
  end
@@ -357,7 +379,7 @@ By default, the `Attacher#process_derivatives` method will download the
357
379
  attached file and pass it to the processor:
358
380
 
359
381
  ```rb
360
- Attacher.derivatives_processor :my_processor do |original|
382
+ Attacher.derivatives :my_processor do |original|
361
383
  original #=> #<File:...>
362
384
  # ...
363
385
  end
@@ -366,12 +388,22 @@ end
366
388
  attacher.process_derivatives(:my_processor) # downloads attached file and passes it to the processor
367
389
  ```
368
390
 
369
- If you want to use a different source file, or if you're calling multiple
370
- processors in a row and want to avoid re-downloading the same source file each
371
- time, you can pass the source file as the second argument:
391
+ If you want to use a different source file, you can pass it in to the process
392
+ 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.
394
+
395
+ ```rb
396
+ # named processor:
397
+ attacher.process_derivatives(:my_processor, source_file)
398
+
399
+ # default processor:
400
+ attacher.process_derivatives(source_file)
401
+ ```
402
+
403
+ If you want to call multiple processors in a row with the same source file, you
404
+ can use this to avoid re-downloading the same source file each time:
372
405
 
373
406
  ```rb
374
- # this way the source file is downloaded only once
375
407
  attacher.file.download do |original|
376
408
  attacher.process_derivatives(:thumbnails, original)
377
409
  attacher.process_derivatives(:colors, original)
@@ -479,9 +511,7 @@ attacher.upload_derivative :thumb, thumbnail_file,
479
511
  location: "path/to/derivative"
480
512
  ```
481
513
 
482
- A `:derivative` option is automatically passed to the uploader and holds the
483
- name of the derivative, which you can use when extracting metadata, generating
484
- location or generating upload options:
514
+ The `:derivative` name is automatically passed to the uploader:
485
515
 
486
516
  ```rb
487
517
  class MyUploader < Shrine
@@ -510,8 +540,6 @@ If you want to disable this behaviour, pass `delete: false`:
510
540
 
511
541
  ```rb
512
542
  attacher.upload_derivative(:thumb, thumbnail_file, delete: false)
513
-
514
- File.exist?(thumbnail_file.path) #=> true
515
543
  ```
516
544
 
517
545
  ## Merging derivatives
@@ -736,7 +764,7 @@ following payload:
736
764
 
737
765
  A default log subscriber is added as well which logs these events:
738
766
 
739
- ```plaintext
767
+ ```
740
768
  Derivatives (2133ms) – {:processor=>:thumbnails, :processor_options=>{}, :uploader=>ImageUploader}
741
769
  ```
742
770
 
@@ -747,7 +775,7 @@ plugin :derivatives, log_subscriber: -> (event) {
747
775
  Shrine.logger.info JSON.generate(name: event.name, duration: event.duration, **event.payload)
748
776
  }
749
777
  ```
750
- ```plaintext
778
+ ```
751
779
  {"name":"derivatives","duration":2133,"processor":"thumbnails","processor_options":{},"uploader":"ImageUploader"}
752
780
  ```
753
781