shrine 3.2.2 → 3.3.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -0
  3. data/README.md +5 -0
  4. data/doc/advantages.md +1 -1
  5. data/doc/carrierwave.md +7 -4
  6. data/doc/direct_s3.md +1 -0
  7. data/doc/external/articles.md +6 -8
  8. data/doc/external/extensions.md +3 -0
  9. data/doc/external/misc.md +23 -8
  10. data/doc/getting_started.md +4 -8
  11. data/doc/multiple_files.md +1 -1
  12. data/doc/paperclip.md +14 -5
  13. data/doc/plugins/add_metadata.md +20 -0
  14. data/doc/plugins/atomic_helpers.md +41 -3
  15. data/doc/plugins/derivatives.md +43 -12
  16. data/doc/plugins/download_endpoint.md +5 -5
  17. data/doc/plugins/dynamic_storage.md +1 -1
  18. data/doc/plugins/infer_extension.md +9 -0
  19. data/doc/plugins/metadata_attributes.md +1 -0
  20. data/doc/plugins/mirroring.md +1 -1
  21. data/doc/plugins/persistence.md +10 -1
  22. data/doc/plugins/store_dimensions.md +10 -0
  23. data/doc/plugins/url_options.md +2 -2
  24. data/doc/processing.md +7 -8
  25. data/doc/release_notes/2.8.0.md +1 -1
  26. data/doc/release_notes/3.2.1.md +2 -3
  27. data/doc/release_notes/3.3.0.md +105 -0
  28. data/doc/storage/s3.md +9 -5
  29. data/doc/upgrading_to_3.md +11 -27
  30. data/lib/shrine/attacher.rb +6 -1
  31. data/lib/shrine/plugins/activerecord.rb +1 -1
  32. data/lib/shrine/plugins/add_metadata.rb +6 -4
  33. data/lib/shrine/plugins/backgrounding.rb +2 -2
  34. data/lib/shrine/plugins/derivation_endpoint.rb +2 -1
  35. data/lib/shrine/plugins/derivatives.rb +45 -15
  36. data/lib/shrine/plugins/mirroring.rb +8 -8
  37. data/lib/shrine/plugins/presign_endpoint.rb +14 -2
  38. data/lib/shrine/plugins/remove_attachment.rb +5 -0
  39. data/lib/shrine/plugins/sequel.rb +1 -1
  40. data/lib/shrine/plugins/store_dimensions.rb +4 -2
  41. data/lib/shrine/plugins/upload_endpoint.rb +7 -2
  42. data/lib/shrine/storage/memory.rb +5 -3
  43. data/lib/shrine/storage/s3.rb +61 -6
  44. data/lib/shrine/version.rb +2 -2
  45. data/shrine.gemspec +1 -1
  46. metadata +6 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26dd77c39f9d5b7cc4240eacea23823453959aad469acc1d86571f129c24c347
4
- data.tar.gz: 9c25f9de3f8c8be835e096da01b47c0636e03158b423a306b89895de05ca4294
3
+ metadata.gz: 8cfa59ed1f6143ee9298ede17eeda07dba85f0f9c3309a5b7a9e7f6c41399f29
4
+ data.tar.gz: 67a64a3a4f40c34797ca04279af73829acf4071eb118f78addefdbaa15e048da
5
5
  SHA512:
6
- metadata.gz: 4ae941f20c889d15300bb0a3bb522f8ad9f0cbd9bb80578246bc4d5ed37b1177ecc911172a405872919210bdaa05b1e67f23d77fc97d9c827ef85b01898dd336
7
- data.tar.gz: 16fbafcc5d29e70f6d0d4a36fc2123a4a8f705345021b00e87c863826d73970a7bd8116aa19ad5c3a2a03763b0e5e435398ae15a21428d0b932c52a7bb648f8e
6
+ metadata.gz: e87a3dbb80a304f8af559b9c3d4e8badf8e47e4377afd74fe08d570042d65358d44878c4a0366fa3cded98fe166083275e86c61f3b51d28d35e070e0233dea35
7
+ data.tar.gz: 83c93e76bf5513ab317ccabbdda86ce90f760e7b898dc9f40036af3e7ec915f48e55bd6f8efcf8661dacea672ce6b24b450db217815b062881169c8c90fd0cd3
@@ -1,3 +1,39 @@
1
+ ## 3.3.0 (2020-10-04)
2
+
3
+ * `s3` - Support new `Aws::S3::EncryptionV2::Client` for client-side encryption (@janko)
4
+
5
+ * `derivation_endpoint` – Reduce possibility of timing attacks when comparing signatures (@esparta)
6
+
7
+ * `derivatives` – Avoid downloading the attached file when calling default no-op processor (@janko)
8
+
9
+ * `derivatives` – Add `:download` processor setting for skipping downloading source file (@jrochkind, @janko)
10
+
11
+ * `derivatives` – Copy non-file source IO objects into local file before passing them to the processor (@jrochkind)
12
+
13
+ * `sequel` – Call `Attacher#reload` in `Sequel::Model#reload`, which keeps rest of attacher state (@janko, @jrochkind)
14
+
15
+ * `activerecord` – Call `Attacher#reload` in `ActiveRecord::Base#reload`, which keeps rest of attacher state (@janko, @jrochkind)
16
+
17
+ * `add_metadata` – Add `:skip_nil` option for excluding metadata keys whose values are nil (@renchap)
18
+
19
+ * `store_dimensions` – Add `:auto_extraction` option for disabling automatically extracting dimensions on upload (@renchap)
20
+
21
+ * `mirroring` – Forward original upload options when mirroring upload (@corneverbruggen)
22
+
23
+ * `derivation_endpoint` – Apply `version` URL option in derivation endpoint (@janko)
24
+
25
+ * `remove_attachment` – Delete removed file if a new file was attached right after removal (@janko)
26
+
27
+ * `upload_endpoint` – Fix `Shrine.upload_response` not working in a Rails controller (@pldavid2)
28
+
29
+ * `presign_endpoint` – Add `OPTIONS` route that newer versions of Uppy check (@janko)
30
+
31
+ * `derivatives` – Add `:create_on_promote` option for auto-creating derivatives on promotion (@janko)
32
+
33
+ * `s3` – Add back support for client-side encryption (@janko)
34
+
35
+ * `memory` – Ensure `Memory#open` returns content in original encoding (@jrochkind)
36
+
1
37
  ## 3.2.2 (2020-08-05)
2
38
 
3
39
  * `s3` – Fix `S3#open` not working on aws-sdk-core 3.104 and above (@janko)
data/README.md CHANGED
@@ -127,6 +127,10 @@ system.
127
127
  * Refile
128
128
  * Active Storage
129
129
 
130
+ ## Contributing
131
+
132
+ Please refer to the [contributing page][Contributing].
133
+
130
134
  ## Code of Conduct
131
135
 
132
136
  Everyone interacting in the Shrine project’s codebases, issue trackers, and
@@ -170,3 +174,4 @@ The gem is available as open source under the terms of the [MIT License].
170
174
  [Roda]: https://github.com/jeremyevans/roda
171
175
  [CoC]: /CODE_OF_CONDUCT.md
172
176
  [MIT License]: /LICENSE.txt
177
+ [Contributing]: https://github.com/shrinerb/shrine/blob/master/CONTRIBUTING.md
@@ -93,7 +93,7 @@ low-level abstractions that give you the flexibility to build your own flow.
93
93
  ```rb
94
94
  uploaded_file = ImageUploader.upload(image, :store) # metadata extraction, upload location generation
95
95
  uploaded_file.id #=> "44ccafc10ce6a4ff22829e8f579ee6b9.jpg"
96
- uplaoded_file.metadata #=> { ... extracted metadata ... }
96
+ uploaded_file.metadata #=> { ... extracted metadata ... }
97
97
 
98
98
  data = uploaded_file.to_json # serialization
99
99
  # ...
@@ -322,19 +322,22 @@ module CarrierwaveShrineSynchronization
322
322
 
323
323
  private
324
324
 
325
- # If you'll be using `:prefix` on your Shrine storage, make sure to
326
- # subtract it from the path assigned as `:id`.
327
325
  def shrine_file(uploader)
328
326
  name = uploader.mounted_as
329
327
  filename = read_attribute(name)
330
- path = uploader.store_path(filename)
328
+ location = uploader.store_path(filename)
329
+ location = location.sub(%r{^#{storage.prefix}/}, "") if storage.prefix
331
330
 
332
331
  Shrine.uploaded_file(
333
332
  storage: :store,
334
- id: path,
333
+ id: location,
335
334
  metadata: { "filename" => filename },
336
335
  )
337
336
  end
337
+
338
+ def storage
339
+ Shrine.storages[:store]
340
+ end
338
341
  end
339
342
  ```
340
343
  ```rb
@@ -76,6 +76,7 @@ If you're using [Uppy], this is the recommended CORS configuration for the
76
76
  <AllowedHeader>x-amz-content-sha256</AllowedHeader>
77
77
  <AllowedHeader>content-type</AllowedHeader>
78
78
  <AllowedHeader>content-disposition</AllowedHeader>
79
+ <ExposeHeader>ETag</ExposeHeader>
79
80
  </CORSRule>
80
81
  <CORSRule>
81
82
  <AllowedOrigin>*</AllowedOrigin>
@@ -21,7 +21,7 @@ title: Articles
21
21
  | [Asynchronous File Uploads](http://twin.github.io/file-uploads-asynchronous-world) | 18&nbsp;Jan&nbsp;2016 |
22
22
  | [Introducing Shrine](http://twin.github.io/introducing-shrine) | 04&nbsp;Oct&nbsp;2015 |
23
23
 
24
- ## Other Articles
24
+ ## Community articles
25
25
 
26
26
  | Article | Published |
27
27
  | :------ | --------: |
@@ -32,9 +32,9 @@ title: Articles
32
32
  | [Happy users uploading files with Rails 5, Shrine, and Vue.js](https://itnext.io/happy-users-uploading-files-with-rails-5-shrine-and-vue-js-bbcc470a327f) | 04&nbsp;Sep&nbsp;2018 |
33
33
  | [Rails 5 + Shrine + DropzoneJS](https://stephencodes.com/rails-5-shrine-dropzonejs/) | 20&nbsp;Oct&nbsp;2018 |
34
34
  | [How to use Trix and Shrine for WYSIWYG Editing with Drag-and-Drop Image Uploading](http://headway.io/blog/how-to-use-trix-and-shrine-for-wysiwyg-editing-with-drag-and-drop-image-uploading/) | 26&nbsp;Jan&nbsp;2018 |
35
- | [Store Your Files on S3: Uploading from a URL](https://www.ironin.it/blog/store-your-files-on-s3-using-the-ruby-shrine-gem-part-3.html) | 28&nbsp;Dec&nbsp;2017 |
36
- | [Store Your Files on S3: Direct Uploads](https://www.ironin.it/blog/store-your-files-on-s3-using-the-ruby-shrine-gem-part-2.html) | 15&nbsp;Dec&nbsp;2017 |
37
- | [Store Your Files on S3: Setup & Configuration](https://www.ironin.it/blog/store-your-files-on-s3-using-the-ruby-shrine-gem-part-1.html) | 27&nbsp;Nov&nbsp;2017 |
35
+ | [Store Your Files on S3: Part 3 – Uploading from a URL](https://www.ironin.it/blog/store-your-files-on-s3-using-the-ruby-shrine-gem-part-3.html) | 28&nbsp;Dec&nbsp;2017 |
36
+ | [Store Your Files on S3: Part 2 – Direct Uploads](https://www.ironin.it/blog/store-your-files-on-s3-using-the-ruby-shrine-gem-part-2.html) | 15&nbsp;Dec&nbsp;2017 |
37
+ | [Store Your Files on S3: Part 1 – Setup & Configuration](https://www.ironin.it/blog/store-your-files-on-s3-using-the-ruby-shrine-gem-part-1.html) | 27&nbsp;Nov&nbsp;2017 |
38
38
  | [Creating Streaming Radio With Rails and Icecast](https://scotch.io/tutorials/creating-online-streaming-radio-with-rails-and-icecast) | 06&nbsp;Nov&nbsp;2017 |
39
39
  | [Multiple Photo Upload using Shrine](https://github.com/pyksoft/multi-photo-upload#multiple-photo-upload-using-shrine) | 02&nbsp;Nov&nbsp;2017 |
40
40
  | [Uploading Files with Rails and ActionCable](https://scotch.io/tutorials/uploading-files-with-rails-and-actioncable) | 19&nbsp;Oct&nbsp;2017 |
@@ -48,14 +48,12 @@ title: Articles
48
48
 
49
49
  | Screencast | Source | Published |
50
50
  | :---- | :------ | --------: |
51
+ | [Testing File Uploads in Rails with Shrine](https://gorails.com/episodes/testing-file-uploads-with-shrine?autoplay=1) | GoRails | 23&nbsp;Mar&nbsp;2020 |
52
+ | [File Uploads in Rails with Shrine](https://gorails.com/episodes/rails-file-uploads-with-shrine?autoplay=1) | GoRails | 16&nbsp;Mar&nbsp;2020 |
51
53
  | [Uploading Files to DigitalOcean Spaces](https://gorails.com/episodes/digital-ocean-spaces-with-rails?autoplay=1) | GoRails | 31&nbsp;Oct&nbsp;2017 |
52
54
  | [Trix WYSIWYG Editor And File Uploads](https://gorails.com/episodes/trix-editor?autoplay=1) | GoRails | 03&nbsp;Oct&nbsp;2017 |
53
55
  | [Multiple File Uploads with Shrine](https://gorails.com/episodes/multiple-file-uploads-with-shrine?autoplay=1) | GoRails | 14&nbsp;Dec&nbsp;2016 |
54
56
  | [Backgrounding and Video Transcoding](https://gorails.com/episodes/shrine-background-and-video-transcoding?autoplay=1) | GoRails | 03&nbsp;Nov&nbsp;2016 |
55
- | [Direct File Uploads to S3: Part 3](https://gorails.com/episodes/direct-file-uploads-to-s3-part-3?autoplay=1) | GoRails | 05&nbsp;Oct&nbsp;2016 |
56
- | [Direct File Uploads to S3: Part 2](https://gorails.com/episodes/direct-file-uploads-to-s3-part-2?autoplay=1) | GoRails | 05&nbsp;Oct&nbsp;2016 |
57
- | [Direct File Uploads to S3: Part 1](https://gorails.com/episodes/direct-file-uploads-to-s3-part-1?autoplay=1) | GoRails | 05&nbsp;Oct&nbsp;2016 |
58
- | [File Uploads in Rails with Shrine](https://gorails.com/episodes/file-uploading-with-shrine?autoplay=1) | GoRails | 23&nbsp;Sep&nbsp;2016 |
59
57
 
60
58
  ## Talks
61
59
 
@@ -28,6 +28,8 @@ title: Extensions
28
28
  | :---- | :-------- |
29
29
  | [administrate-field-shrine](https://github.com/catsky/administrate-field-shrine) | Plugin for [Administrate](https://github.com/thoughtbot/administrate) |
30
30
  | [rails_admin_shrine](https://github.com/iquest/rails_admin_shrine) | Plugin for [RailsAdmin](https://github.com/sferik/rails_admin) |
31
+ | [shrine-blurhash](https://github.com/renchap/shrine-blurhash) | Plugin for computing [Blurhash](https://blurha.sh/) on images |
32
+ | [shrine-cloudimage](https://github.com/janklimo/shrine-cloudimage) | Plugin for [Cloudimage](https://www.cloudimage.io/) |
31
33
  | [shrine-color](https://github.com/jnylen/shrine-color) | Plugin for finding dominant color in an image |
32
34
  | [shrine-configurable_storage](https://github.com/SleeplessByte/shrine-configurable_storage) | Plugin for lazy storage registration |
33
35
  | [shrine-content_addressable](https://github.com/SleeplessByte/shrine-content_addressable) | Plugin for generating content addressable locations |
@@ -45,6 +47,7 @@ title: Extensions
45
47
  | Gem | Description |
46
48
  | :----- | :------- |
47
49
  | [ckeditor](https://github.com/galetahub/ckeditor) | Integration for [CKEditor](https://ckeditor.com/ckeditor-4/) |
50
+ | [faster_s3_url](https://github.com/jrochkind/faster_s3_url) | Optimized generation of public and presigned AWS S3 GET URLs |
48
51
  | [imgproxy](https://github.com/imgproxy/imgproxy.rb) | Integration for [imgproxy](https://github.com/imgproxy/imgproxy) |
49
52
  | [rails_admin](https://github.com/sferik/rails_admin) | Integration for [RailsAdmin](https://github.com/sferik/rails_admin) |
50
53
  | [uppy-s3_multipart](https://github.com/janko/uppy-s3_multipart) | Integration for [Uppy AWS S3 Multipart](https://uppy.io/docs/aws-s3-multipart/) |
@@ -4,14 +4,29 @@ title: Miscellaneous
4
4
 
5
5
  ## Demos
6
6
 
7
- * [Dropzone demo](https://github.com/codyeatworld/example-shrine-dropzone)
8
- * [Hanami demo](https://github.com/katafrakt/hanami-shrine-example)
9
- * [Rails demo](https://github.com/erikdahlstrand/shrine-rails-example)
10
- * [Resumable uploads demo](https://github.com/shrinerb/shrine-tus-demo)
11
- * [Roda demo (official)](https://github.com/shrinerb/shrine/tree/master/demo)
12
- * [ROM & dry-rb demo](https://github.com/shrinerb/shrine-rom/tree/master/demo)
13
- * [Transloadit demo](https://github.com/shrinerb/shrine-transloadit/tree/master/demo)
7
+ | Demo | Description |
8
+ | :--- | :---------- |
9
+ | [Dropzone demo](https://github.com/codyeatworld/example-shrine-dropzone) | Shows direct upload using [Dropzone.js] |
10
+ | [Hanami demo](https://github.com/katafrakt/hanami-shrine-example) | Shows file attachment in [Hanami] |
11
+ | [Crop demo](https://github.com/shrinerb/shrine-crop-example) | Shows image cropping using [Cropper.js] |
12
+ | [Rails demo](https://github.com/erikdahlstrand/shrine-rails-example) | Shows direct upload in [Rails] |
13
+ | [Resumable uploads demo](https://github.com/shrinerb/shrine-tus-demo) | Shows resumable direct upload on [tus] |
14
+ | [Roda demo (official)](https://github.com/shrinerb/shrine/tree/master/demo) | Shows direct upload in [Roda] |
15
+ | [rom-rb & dry-rb demo](https://github.com/shrinerb/shrine-rom/tree/master/demo) | Shows file attachment with [rom-rb] and [dry-rb] |
16
+ | [Transloadit demo](https://github.com/shrinerb/shrine-transloadit/tree/master/demo) | Shows file processing using [Transloadit] |
14
17
 
15
18
  ## Projects
16
19
 
17
- * [Cortex CMS](https://docs.cortexcms.org)
20
+ | Project | Description |
21
+ | :------ | :---------- |
22
+ | [CortexCMS](https://docs.cortexcms.org) | An open source, enterprise content management and distribution platform |
23
+
24
+ [Dropzone.js]: https://www.dropzonejs.com/
25
+ [Hanami]: https://hanamirb.org/
26
+ [Cropper.js]: https://github.com/fengyuanchen/cropperjs
27
+ [Rails]: https://rubyonrails.org/
28
+ [tus]: https://tus.io/
29
+ [Roda]: https://roda.jeremyevans.net/
30
+ [rom-rb]: https://rom-rb.org/
31
+ [dry-rb]: https://dry-rb.org/
32
+ [Transloadit]: https://transloadit.com/
@@ -554,7 +554,7 @@ creation:
554
554
  gem "image_processing", "~> 1.8"
555
555
  ```
556
556
  ```rb
557
- Shrine.plugin :derivatives
557
+ Shrine.plugin :derivatives, create_on_promote: true
558
558
  ```
559
559
  ```rb
560
560
  require "image_processing/mini_magick"
@@ -573,11 +573,7 @@ end
573
573
  ```
574
574
  ```rb
575
575
  photo = Photo.new(image: file)
576
-
577
- if photo.valid?
578
- photo.image_derivatives! if photo.image_changed? # create derivatives
579
- photo.save
580
- end
576
+ photo.save # automatically creates derivatives on promotion
581
577
  ```
582
578
 
583
579
  You can then retrieve the URL of a processed derivative:
@@ -711,7 +707,7 @@ photo/
711
707
  ...
712
708
  ```
713
709
 
714
- Buy you can also override `Shrine#generate_location` with a custom
710
+ But you can also override `Shrine#generate_location` with a custom
715
711
  implementation, for example:
716
712
 
717
713
  ```rb
@@ -903,7 +899,7 @@ resumable uploads from scratch, it includes a complete JavaScript example
903
899
 
904
900
  ## Backgrounding
905
901
 
906
- The [`backgrounding`][backgrounding plugin] allows you to move file promotion
902
+ The [`backgrounding`][backgrounding plugin] plugin allows you to move file promotion
907
903
  and deletion into a background job, using the backgrounding library [of your
908
904
  choice][Backgrounding Libraries]:
909
905
 
@@ -228,7 +228,7 @@ photos_attributes = album_params[:photos_attributes].to_h.merge(new_photos_attri
228
228
  album_attributes = album_params.merge(photos_attributes: photos_attributes)
229
229
 
230
230
  # create the album with photos
231
- Album.create(album_params)
231
+ Album.create(album_attributes)
232
232
  ```
233
233
 
234
234
  In this case you need to make sure your form tag has
@@ -358,13 +358,14 @@ module PaperclipShrineSynchronization
358
358
  end
359
359
  end
360
360
 
361
- # If you'll be using a `:prefix` on your Shrine storage, or you're storing
362
- # files on the filesystem, make sure to subtract the appropriate part
363
- # from the path assigned to `:id`.
364
361
  def shrine_attachment_file(attachment)
362
+ location = attachment.path
363
+ # if you're storing files on disk, make sure to subtract the absolute path
364
+ location = location.sub(%r{^#{storage.prefix}/}, "") if storage.prefix
365
+
365
366
  Shrine.uploaded_file(
366
367
  storage: :store,
367
- id: attachment.path,
368
+ id: location,
368
369
  metadata: {
369
370
  "size" => attachment.size,
370
371
  "filename" => attachment.original_filename,
@@ -377,12 +378,20 @@ module PaperclipShrineSynchronization
377
378
  # files on the filesystem, make sure to subtract the appropriate part
378
379
  # from the path assigned to `:id`.
379
380
  def shrine_style_file(style)
381
+ location = style.attachment.path(style.name)
382
+ # if you're storing files on disk, make sure to subtract the absolute path
383
+ location = location.sub(%r{^#{storage.prefix}/}, "") if storage.prefix
384
+
380
385
  Shrine.uploaded_file(
381
386
  storage: :store,
382
- id: style.attachment.path(style.name),
387
+ id: location,
383
388
  metadata: {},
384
389
  )
385
390
  end
391
+
392
+ def storage
393
+ Shrine.storages[:store]
394
+ end
386
395
  end
387
396
  ```
388
397
  ```rb
@@ -34,6 +34,26 @@ uploaded_file.metadata["page_count"] #=> 30
34
34
  uploaded_file.page_count #=> 30
35
35
  ```
36
36
 
37
+ ### Skipping nil values
38
+
39
+ By default, if your block returns `nil` then the `nil` value will be stored into
40
+ metadata. If you do not want to store anything when your block returns nil, you
41
+ can use the `skip_nil: true` option:
42
+
43
+ ```rb
44
+ class PdfUploader < Shrine
45
+ add_metadata :pages, skip_nil: true do |io|
46
+ if is_pdf?(io)
47
+ reader = PDF::Reader.new(io)
48
+ reader.page_count
49
+ else
50
+ # If this is not a PDF, then the pages metadata will not be stored
51
+ nil
52
+ end
53
+ end
54
+ end
55
+ ```
56
+
37
57
  ### Multiple values
38
58
 
39
59
  You can also extract multiple metadata values at once, by using `add_metadata`
@@ -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
@@ -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,17 +38,9 @@ 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
-
48
- if photo.valid?
49
- photo.image_derivatives! if photo.image_changed? # create derivatives
50
- photo.save
51
- end
42
+ photo.image_derivatives! # creates derivatives
43
+ photo.save
52
44
  ```
53
45
 
54
46
  You can then retrieve the URL of a processed derivative:
@@ -205,6 +197,19 @@ attacher.create_derivatives(different_source) # pass a different source file
205
197
  attacher.create_derivatives(foo: "bar") # pass custom options to the processor
206
198
  ```
207
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
+
208
213
  ### Naming processors
209
214
 
210
215
  If you want to have multiple processors for an uploader, you can assign each
@@ -396,7 +401,8 @@ attacher.process_derivatives(:my_processor) # downloads attached file and passes
396
401
 
397
402
  If you want to use a different source file, you can pass it in to the process
398
403
  call. Typically you'd pass a local file on disk. If you pass a
399
- `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.
400
406
 
401
407
  ```rb
402
408
  # named processor:
@@ -416,6 +422,20 @@ attacher.file.download do |original|
416
422
  end
417
423
  ```
418
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
+
419
439
  ## Adding derivatives
420
440
 
421
441
  If you already have processed files that you want to save, you can do that with
@@ -459,6 +479,11 @@ For adding a single derivative, you can also use the singular
459
479
  attacher.add_derivative(:thumb, thumbnail_file)
460
480
  ```
461
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
+
462
487
  Any options passed to `Attacher#add_derivative(s)` will be forwarded to
463
488
  [`Attacher#upload_derivatives`](#uploading-derivatives).
464
489
 
@@ -567,6 +592,11 @@ attacher.merge_derivatives attacher.upload_derivatives({ nested: { two: two_file
567
592
  attacher.derivatives #=> { nested: { one: #<Shrine::UploadedFile>, two: #<Shrine::UploadedFile> } }
568
593
  ```
569
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
+
570
600
  The `Attacher#merge_derivatives` method is thread-safe.
571
601
 
572
602
  ### Setting derivatives
@@ -793,6 +823,7 @@ Or disable logging altogether:
793
823
  plugin :derivatives, log_subscriber: nil
794
824
  ```
795
825
 
826
+ [derivatives]: https://github.com/shrinerb/shrine/blob/master/lib/shrine/plugins/derivatives.rb
796
827
  [default_url]: https://shrinerb.com/docs/plugins/default_url
797
828
  [entity]: https://shrinerb.com/docs/plugins/entity
798
829
  [model]: https://shrinerb.com/docs/plugins/model