shrine 3.2.2 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
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