shrine 2.14.0 → 2.15.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 (149) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +384 -374
  3. data/README.md +132 -63
  4. data/doc/advantages.md +191 -109
  5. data/doc/attacher.md +1 -1
  6. data/doc/carrierwave.md +4 -4
  7. data/doc/creating_storages.md +2 -2
  8. data/doc/design.md +2 -2
  9. data/doc/direct_s3.md +3 -3
  10. data/doc/metadata.md +1 -1
  11. data/doc/multiple_files.md +2 -2
  12. data/doc/paperclip.md +3 -3
  13. data/doc/plugins/activerecord.md +92 -0
  14. data/doc/plugins/add_metadata.md +93 -0
  15. data/doc/plugins/backgrounding.md +148 -0
  16. data/doc/plugins/backup.md +29 -0
  17. data/doc/plugins/cached_attachment_data.md +23 -0
  18. data/doc/plugins/copy.md +22 -0
  19. data/doc/plugins/data_uri.md +92 -0
  20. data/doc/plugins/default_storage.md +16 -0
  21. data/doc/plugins/default_url.md +33 -0
  22. data/doc/plugins/default_url_options.md +22 -0
  23. data/doc/plugins/delete_promoted.md +10 -0
  24. data/doc/plugins/delete_raw.md +16 -0
  25. data/doc/plugins/derivation_endpoint.md +747 -0
  26. data/doc/plugins/determine_mime_type.md +64 -0
  27. data/doc/plugins/direct_upload.md +170 -0
  28. data/doc/plugins/download_endpoint.md +83 -0
  29. data/doc/plugins/dynamic_storage.md +20 -0
  30. data/doc/plugins/hooks.md +56 -0
  31. data/doc/plugins/included.md +15 -0
  32. data/doc/plugins/infer_extension.md +57 -0
  33. data/doc/plugins/keep_files.md +20 -0
  34. data/doc/plugins/logging.md +39 -0
  35. data/doc/plugins/metadata_attribues.md +43 -0
  36. data/doc/plugins/migration_helpers.md +58 -0
  37. data/doc/plugins/module_include.md +40 -0
  38. data/doc/plugins/moving.md +17 -0
  39. data/doc/plugins/multi_delete.md +18 -0
  40. data/doc/plugins/parallelize.md +14 -0
  41. data/doc/plugins/parsed_json.md +9 -0
  42. data/doc/plugins/presign_endpoint.md +133 -0
  43. data/doc/plugins/pretty_location.md +29 -0
  44. data/doc/plugins/processing.md +68 -0
  45. data/doc/plugins/rack_file.md +49 -0
  46. data/doc/plugins/rack_response.md +96 -0
  47. data/doc/plugins/recache.md +27 -0
  48. data/doc/plugins/refresh_metadata.md +31 -0
  49. data/doc/plugins/remote_url.md +104 -0
  50. data/doc/plugins/remove_attachment.md +16 -0
  51. data/doc/plugins/remove_invalid.md +9 -0
  52. data/doc/plugins/restore_cached_data.md +14 -0
  53. data/doc/plugins/sequel.md +64 -0
  54. data/doc/plugins/signature.md +49 -0
  55. data/doc/plugins/store_dimensions.md +68 -0
  56. data/doc/plugins/tempfile.md +40 -0
  57. data/doc/plugins/upload_endpoint.md +123 -0
  58. data/doc/plugins/upload_options.md +28 -0
  59. data/doc/plugins/validation_helpers.md +129 -0
  60. data/doc/plugins/versions.md +179 -0
  61. data/doc/processing.md +217 -247
  62. data/doc/refile.md +3 -3
  63. data/doc/release_notes/1.0.0.md +143 -0
  64. data/doc/release_notes/1.1.0.md +184 -0
  65. data/doc/release_notes/1.2.0.md +37 -0
  66. data/doc/release_notes/1.3.0.md +90 -0
  67. data/doc/release_notes/1.4.0.md +167 -0
  68. data/doc/release_notes/1.4.1.md +9 -0
  69. data/doc/release_notes/1.4.2.md +20 -0
  70. data/doc/release_notes/2.0.0.md +173 -0
  71. data/doc/release_notes/2.0.1.md +12 -0
  72. data/doc/release_notes/2.1.0.md +59 -0
  73. data/doc/release_notes/2.1.1.md +8 -0
  74. data/doc/release_notes/2.10.0.md +52 -0
  75. data/doc/release_notes/2.10.1.md +6 -0
  76. data/doc/release_notes/2.11.0.md +69 -0
  77. data/doc/release_notes/2.12.0.md +65 -0
  78. data/doc/release_notes/2.13.0.md +146 -0
  79. data/doc/release_notes/2.14.0.md +278 -0
  80. data/doc/release_notes/2.15.0.md +82 -0
  81. data/doc/release_notes/2.2.0.md +98 -0
  82. data/doc/release_notes/2.3.0.md +50 -0
  83. data/doc/release_notes/2.3.1.md +10 -0
  84. data/doc/release_notes/2.4.0.md +87 -0
  85. data/doc/release_notes/2.4.1.md +29 -0
  86. data/doc/release_notes/2.5.0.md +130 -0
  87. data/doc/release_notes/2.6.0.md +254 -0
  88. data/doc/release_notes/2.6.1.md +14 -0
  89. data/doc/release_notes/2.7.0.md +180 -0
  90. data/doc/release_notes/2.8.0.md +95 -0
  91. data/doc/release_notes/2.9.0.md +82 -0
  92. data/doc/retrieving_uploads.md +1 -1
  93. data/doc/storage/file_system.md +96 -0
  94. data/doc/storage/s3.md +293 -0
  95. data/doc/validation.md +1 -1
  96. data/lib/shrine/plugins/_urlsafe_serialization.rb +33 -125
  97. data/lib/shrine/plugins/activerecord.rb +0 -78
  98. data/lib/shrine/plugins/add_metadata.rb +0 -80
  99. data/lib/shrine/plugins/backgrounding.rb +0 -134
  100. data/lib/shrine/plugins/backup.rb +0 -22
  101. data/lib/shrine/plugins/cached_attachment_data.rb +0 -15
  102. data/lib/shrine/plugins/copy.rb +0 -14
  103. data/lib/shrine/plugins/data_uri.rb +0 -73
  104. data/lib/shrine/plugins/default_storage.rb +0 -11
  105. data/lib/shrine/plugins/default_url.rb +0 -25
  106. data/lib/shrine/plugins/default_url_options.rb +0 -16
  107. data/lib/shrine/plugins/delete_promoted.rb +0 -6
  108. data/lib/shrine/plugins/delete_raw.rb +0 -10
  109. data/lib/shrine/plugins/derivation_endpoint.rb +652 -0
  110. data/lib/shrine/plugins/determine_mime_type.rb +1 -85
  111. data/lib/shrine/plugins/direct_upload.rb +0 -155
  112. data/lib/shrine/plugins/download_endpoint.rb +11 -73
  113. data/lib/shrine/plugins/dynamic_storage.rb +0 -17
  114. data/lib/shrine/plugins/hooks.rb +0 -48
  115. data/lib/shrine/plugins/included.rb +0 -12
  116. data/lib/shrine/plugins/infer_extension.rb +0 -49
  117. data/lib/shrine/plugins/keep_files.rb +0 -19
  118. data/lib/shrine/plugins/logging.rb +0 -39
  119. data/lib/shrine/plugins/metadata_attributes.rb +0 -35
  120. data/lib/shrine/plugins/migration_helpers.rb +0 -50
  121. data/lib/shrine/plugins/module_include.rb +0 -32
  122. data/lib/shrine/plugins/moving.rb +0 -12
  123. data/lib/shrine/plugins/multi_delete.rb +0 -13
  124. data/lib/shrine/plugins/parallelize.rb +0 -8
  125. data/lib/shrine/plugins/parsed_json.rb +0 -5
  126. data/lib/shrine/plugins/presign_endpoint.rb +2 -117
  127. data/lib/shrine/plugins/pretty_location.rb +0 -22
  128. data/lib/shrine/plugins/processing.rb +0 -55
  129. data/lib/shrine/plugins/rack_file.rb +0 -39
  130. data/lib/shrine/plugins/rack_response.rb +0 -81
  131. data/lib/shrine/plugins/recache.rb +0 -21
  132. data/lib/shrine/plugins/refresh_metadata.rb +0 -24
  133. data/lib/shrine/plugins/remote_url.rb +0 -85
  134. data/lib/shrine/plugins/remove_attachment.rb +0 -10
  135. data/lib/shrine/plugins/remove_invalid.rb +0 -6
  136. data/lib/shrine/plugins/restore_cached_data.rb +0 -10
  137. data/lib/shrine/plugins/sequel.rb +0 -54
  138. data/lib/shrine/plugins/signature.rb +0 -37
  139. data/lib/shrine/plugins/store_dimensions.rb +0 -63
  140. data/lib/shrine/plugins/tempfile.rb +4 -35
  141. data/lib/shrine/plugins/upload_endpoint.rb +2 -109
  142. data/lib/shrine/plugins/upload_options.rb +0 -20
  143. data/lib/shrine/plugins/validation_helpers.rb +0 -36
  144. data/lib/shrine/plugins/versions.rb +0 -156
  145. data/lib/shrine/storage/file_system.rb +0 -77
  146. data/lib/shrine/storage/s3.rb +0 -249
  147. data/lib/shrine/version.rb +1 -1
  148. data/shrine.gemspec +2 -2
  149. metadata +86 -6
data/README.md CHANGED
@@ -6,7 +6,7 @@ Shrine is a toolkit for file attachments in Ruby applications. Some highlights:
6
6
  * **Memory friendly** – streaming uploads and downloads make it work great with large files
7
7
  * **Cloud storage** – store files on [disk][FileSystem], [AWS S3][S3], [Google Cloud][GCS], [Cloudinary] and others
8
8
  * **ORM integrations** – works with [Sequel][sequel plugin], [ActiveRecord][activerecord plugin], [Hanami::Model][hanami plugin] and [Mongoid][mongoid plugin]
9
- * **Flexible processing** – generate thumbnails with [ImageMagick] or [libvips] using the [ImageProcessing][image_processing] gem
9
+ * **Flexible processing** – generate thumbnails [on upload] or [on-the-fly] using [ImageMagick][ImageProcessing::MiniMagick] or [libvips][ImageProcessing::Vips]
10
10
  * **Metadata validation** – [validate files][validation_helpers plugin] based on [extracted metadata][Extracting Metadata]
11
11
  * **Direct uploads** – upload asynchronously [to your app][upload_endpoint plugin] or [to the cloud][presign_endpoint plugin] using [Uppy]
12
12
  * **Resumable uploads** – make large file uploads [resumable][tus] by pointing [Uppy][uppy tus] to a [resumable endpoint][tus-ruby-server]
@@ -16,12 +16,14 @@ If you're curious how it compares to other file attachment libraries, see the [A
16
16
 
17
17
  ## Resources
18
18
 
19
- - Documentation: [shrinerb.com](https://shrinerb.com)
20
- - Demo code: [Roda][roda demo] / [Rails][rails demo]
21
- - Source: [github.com/shrinerb/shrine](https://github.com/shrinerb/shrine)
22
- - Wiki: [github.com/shrinerb/shrine/wiki](https://github.com/shrinerb/shrine/wiki)
23
- - Bugs: [github.com/shrinerb/shrine/issues](https://github.com/shrinerb/shrine/issues)
24
- - Help & Discussion: [groups.google.com/group/ruby-shrine](https://groups.google.com/forum/#!forum/ruby-shrine)
19
+ | Resource | URL |
20
+ | :------- | :--- |
21
+ | Website | [shrinerb.com](https://shrinerb.com) |
22
+ | Demo code | [Roda][roda demo] / [Rails][rails demo] |
23
+ | Source | [github.com/shrinerb/shrine](https://github.com/shrinerb/shrine) |
24
+ | Wiki | [github.com/shrinerb/shrine/wiki](https://github.com/shrinerb/shrine/wiki) |
25
+ | Bugs | [github.com/shrinerb/shrine/issues](https://github.com/shrinerb/shrine/issues) |
26
+ | Help & Discussion | [groups.google.com/group/ruby-shrine](https://groups.google.com/forum/#!forum/ruby-shrine) |
25
27
 
26
28
  ## Quick start
27
29
 
@@ -509,13 +511,15 @@ uploaded_file.metadata["foo"] #=> "bar"
509
511
 
510
512
  ## Processing
511
513
 
512
- Shrine's `processing` plugin allows you to intercept when the cached file is
513
- being uploaded to permanent storage, and do any file processing your might want.
514
+ Shrine allows you to processing attached files either "on upload" or
515
+ "on-the-fly". For example, if your app is accepting image uploads, you can
516
+ generate a pre-defined set of of thumbnails as soon as the image is attached to
517
+ a record ("on upload"), or you can generate necessary thumbnails dynamically as
518
+ they're needed ("on-the-fly").
514
519
 
515
- If you're uploading images, it's common to want to generate various thumbnails.
516
- It's recommended to use the **[ImageProcessing][image_processing]** gem for
517
- this, which provides a convenient API over [ImageMagick] and [libvips]. You
518
- also need to load the `versions` plugin to be able to save multiple files.
520
+ In both cases, for image processing you can use the **[ImageProcessing]** gem.
521
+ It provides a convenient unified API for processing with [ImageMagick] or
522
+ [libvips]. Here is an example of generating a thumbnail with ImageProcessing:
519
523
 
520
524
  ```sh
521
525
  $ brew install imagemagick
@@ -524,6 +528,28 @@ $ brew install imagemagick
524
528
  # Gemfile
525
529
  gem "image_processing", "~> 1.0"
526
530
  ```
531
+ ```rb
532
+ require "image/mini_magick"
533
+
534
+ thumbnail = ImageProcessing::MiniMagick
535
+ .source(original_image)
536
+ .resize_to_limit!(600, 400)
537
+
538
+ thumbnail #=> #<Tempfile:...> (thumbnail limited to 600x400)
539
+ ```
540
+
541
+ ### On upload
542
+
543
+ Shrine allows you intercept when a cached file is being uploaded to permanent
544
+ storage, and perform any file processing you might want. The result of
545
+ processing can also be multiple files, such as thumbnails of various
546
+ dimensions. This processing can additionaly be delayed into a [background
547
+ job](#backgrounding).
548
+
549
+ The promotion hook is provided by the `processing` plugin, while the ability
550
+ to save multiple files is provided by the `versions` plugin. Let's set up our
551
+ uploader to generate some thumbnails from the attached image:
552
+
527
553
  ```rb
528
554
  require "image_processing/mini_magick"
529
555
 
@@ -553,6 +579,8 @@ After these files have been uploaded, their data will all be saved to the
553
579
  of `Shrine::UploadedFile` objects.
554
580
 
555
581
  ```rb
582
+ photo = Photo.create(image: image)
583
+
556
584
  photo.image_data #=>
557
585
  # '{
558
586
  # "original": {"id":"9sd84.jpg", "storage":"store", "metadata":{...}},
@@ -581,8 +609,53 @@ The `versions` plugin also expands `#<attachment>_url` to accept version names:
581
609
  photo.image_url(:large) #=> "https://..."
582
610
  ```
583
611
 
584
- For more details, including examples of how to do custom and on-the-fly
585
- processing, see the [File Processing] guide.
612
+ For more details see the [File Processing] guide.
613
+
614
+ ### On-the-fly
615
+
616
+ On-the-fly processing is provided by the
617
+ [`derivation_endpoint`][derivation_endpoint plugin] plugin. It provides a
618
+ mountable Rack app, which on request will call the processing we defined.
619
+
620
+ We start by loading the plugin with a secret key and a path prefix to where
621
+ we'll mount the Rack app, and defining a "derivation" we want the app to call:
622
+
623
+ ```rb
624
+ require "image_processing/mini_magick"
625
+
626
+ class ImageUploader < Shrine
627
+ plugin :derivation_endpoint,
628
+ secret_key: "<YOUR SECRET KEY>",
629
+ prefix: "derivations/image"
630
+
631
+ derivation :thumbnail do |file, width, height|
632
+ ImageProcessing::MiniMagick
633
+ .source(file)
634
+ .resize_to_limit!(width.to_i, height.to_i)
635
+ end
636
+ end
637
+ ```
638
+
639
+ Then we can mount the Rack app into our router (for web frameworks other than
640
+ Rails see [Mounting Endpoints] wiki):
641
+
642
+ ```rb
643
+ # config/routes.rb (Rails)
644
+ Rails.application.routes.draw do
645
+ mount ImageUploader.derivation_endpoint => "derivations/image"
646
+ end
647
+ ```
648
+
649
+ Now we can generate URLs from attached files that on request will call the
650
+ processing we defined:
651
+
652
+ ```rb
653
+ photo.image.derivation_url(:thumbnail, "600", "400")
654
+ #=> "/derivations/image/thumbnail/600/400/eyJpZCI6ImZvbyIsInN0b3JhZ2UiOiJzdG9yZSJ9?signature=..."
655
+ ```
656
+
657
+ The `derivation_endpoint` plugin is highly customizable, for more details see
658
+ its [documentation][derivation_endpoint plugin].
586
659
 
587
660
  ## Context
588
661
 
@@ -638,8 +711,8 @@ user.valid? #=> false
638
711
  user.errors.to_hash #=> {:cv=>["is too large (max is 5 MB)"]}
639
712
  ```
640
713
 
641
- See the [File Validation] guide and `validation_helpers` plugin documentation
642
- for more details.
714
+ See the [File Validation] guide and [`validation_helpers`][validation_helpers
715
+ plugin] plugin documentation for more details.
643
716
 
644
717
  ## Location
645
718
 
@@ -699,8 +772,8 @@ server and attached to a record, just like with raw files. The only difference
699
772
  is that they won't be additionally uploaded to temporary storage on assignment,
700
773
  as they were already uploaded on the client side. Note that by default **Shrine
701
774
  won't extract metadata from directly uploaded files**, instead it will just copy
702
- metadata that was extacted on the client side; see [this section][metadata direct uploads]
703
- for the rationale and instructions on how to opt in.
775
+ metadata that was extacted on the client side; see [this section][metadata
776
+ direct uploads] for the rationale and instructions on how to opt in.
704
777
 
705
778
  For handling client side uploads it's recommended to use **[Uppy]**. Uppy is a
706
779
  very flexible modern JavaScript file upload library, which happens to integrate
@@ -711,22 +784,16 @@ nicely with Shrine.
711
784
  The simplest approach is creating an upload endpoint in your app that will
712
785
  receive uploads and forward them to the specified storage. You can use the
713
786
  `upload_endpoint` Shrine plugin to create a Rack app that handles uploads,
714
- and mount it inside your application.
787
+ and mount it inside your application (for web frameworks other than Rails
788
+ see [Mounting Endpoints] wiki).
715
789
 
716
790
  ```rb
717
791
  Shrine.plugin :upload_endpoint
718
792
  ```
719
793
  ```rb
720
- # config.ru (Rack)
721
- map "/images/upload" do
722
- run ImageUploader.upload_endpoint(:cache)
723
- end
724
-
725
- # OR
726
-
727
794
  # config/routes.rb (Rails)
728
795
  Rails.application.routes.draw do
729
- mount ImageUploader.upload_endpoint(:cache) => "/images/upload"
796
+ mount ImageUploader.upload_endpoint(:cache) => "images/upload"
730
797
  end
731
798
  ```
732
799
 
@@ -759,24 +826,20 @@ end
759
826
  If you want to free your app from receiving file uploads, you can also upload
760
827
  files directly to the cloud (AWS S3, Google Cloud etc). In this flow the client
761
828
  is required to first fetch upload parameters from the server, and then use these
762
- parameters to make the upload. The `presign_endpoint` Shrine plugin can be used
763
- to create a Rack app that generates these upload parameters (provided that the
764
- underlying storage implements `#presign`):
829
+ parameters to make the upload.
830
+
831
+ You can use the `presign_endpoint` Shrine plugin to create a Rack app that
832
+ generates these upload parameters (provided that the underlying storage
833
+ implements `#presign`), and mount it inside your application (for web
834
+ frameworks other than Rails see [Mounting Endpoints] wiki):
765
835
 
766
836
  ```rb
767
837
  Shrine.plugin :presign_endpoint
768
838
  ```
769
839
  ```rb
770
- # config.ru (Rack)
771
- map "/s3/params" do
772
- run Shrine.presign_endpoint(:cache)
773
- end
774
-
775
- # OR
776
-
777
840
  # config/routes.rb (Rails)
778
841
  Rails.application.routes.draw do
779
- mount Shrine.presign_endpoint(:cache) => "/s3/params"
842
+ mount Shrine.presign_endpoint(:cache) => "s3/params"
780
843
  end
781
844
  ```
782
845
 
@@ -969,54 +1032,60 @@ The gem is available as open source under the terms of the [MIT License].
969
1032
 
970
1033
  [Shrine]: https://shrinerb.com
971
1034
  [plugin system]: https://twin.github.io/the-plugin-system-of-sequel-and-roda/
972
- [FileSystem]: https://shrinerb.com/rdoc/classes/Shrine/Storage/FileSystem.html
973
- [S3]: https://shrinerb.com/rdoc/classes/Shrine/Storage/S3.html
1035
+ [FileSystem]: /doc/storage/file_system.md#readme
1036
+ [S3]: /doc/storage/s3.md#readme
974
1037
  [GCS]: https://github.com/renchap/shrine-google_cloud_storage
975
1038
  [Cloudinary]: https://github.com/shrinerb/shrine-cloudinary
976
1039
  [Transloadit]: https://github.com/shrinerb/shrine-transloadit
977
- [activerecord plugin]: https://shrinerb.com/rdoc/classes/Shrine/Plugins/Activerecord.html
978
- [sequel plugin]: https://shrinerb.com/rdoc/classes/Shrine/Plugins/Sequel.html
1040
+ [activerecord plugin]: /doc/plugins/activerecord.md#readme
1041
+ [sequel plugin]: /doc/plugins/sequel.md#readme
979
1042
  [hanami plugin]: https://github.com/katafrakt/hanami-shrine
980
1043
  [mongoid plugin]: https://github.com/shrinerb/shrine-mongoid
981
- [image_processing]: https://github.com/janko-m/image_processing
982
- [ImageMagick]: https://www.imagemagick.org/script/index.php
1044
+ [ImageProcessing]: https://github.com/janko/image_processing
1045
+ [on upload]: #on-upload
1046
+ [on-the-fly]: #on-the-fly
1047
+ [ImageProcessing::MiniMagick]: https://github.com/janko/image_processing/blob/master/doc/minimagick.md#readme
1048
+ [ImageProcessing::Vips]: https://github.com/janko/image_processing/blob/master/doc/vips.md#readme
1049
+ [ImageMagick]: https://imagemagick.org/
983
1050
  [libvips]: http://libvips.github.io/libvips/
984
- [validation_helpers plugin]: https://shrinerb.com/rdoc/classes/Shrine/Plugins/ValidationHelpers.html
985
- [upload_endpoint plugin]: https://shrinerb.com/rdoc/classes/Shrine/Plugins/UploadEndpoint.html
986
- [presign_endpoint plugin]: https://shrinerb.com/rdoc/classes/Shrine/Plugins/PresignEndpoint.html
1051
+ [derivation_endpoint plugin]: /doc/plugins/derivation_endpoint.md#readme
1052
+ [validation_helpers plugin]: /doc/plugins/validation_helpers.md#readme
1053
+ [upload_endpoint plugin]: /doc/plugins/upload_endpoint.md#readme
1054
+ [presign_endpoint plugin]: /doc/plugins/presign_endpoint.md#readme
987
1055
  [Uppy]: https://uppy.io
988
1056
  [tus]: https://tus.io
989
1057
  [uppy tus]: https://uppy.io/docs/tus/
990
- [tus-ruby-server]: https://github.com/janko-m/tus-ruby-server
991
- [backgrounding plugin]: https://shrinerb.com/rdoc/classes/Shrine/Plugins/Backgrounding.html
992
- [Advantages of Shrine]: https://shrinerb.com/rdoc/files/doc/advantages_md.html
1058
+ [tus-ruby-server]: https://github.com/janko/tus-ruby-server
1059
+ [backgrounding plugin]: /doc/plugins/backgrounding.md#readme
1060
+ [Advantages of Shrine]: /doc/advantages.md#readme
993
1061
  [external storages]: https://shrinerb.com/#external
994
- [creating storage]: https://shrinerb.com/rdoc/files/doc/creating_storages_md.html
995
- [creating plugin]: https://shrinerb.com/rdoc/files/doc/creating_plugins_md.html
996
- [Retrieving Uploads]: https://shrinerb.com/rdoc/files/doc/retrieving_uploads_md.html
997
- [Using Attacher]: https://shrinerb.com/rdoc/files/doc/attacher_md.html
1062
+ [creating storage]: /doc/creating_storages.md#readme
1063
+ [creating plugin]: /doc/creating_plugins.md#readme
1064
+ [Retrieving Uploads]: /doc/retrieving_uploads.md#readme
1065
+ [Using Attacher]: /doc/attacher.md#readme
998
1066
  [plugins]: https://shrinerb.com/#plugins
999
1067
  [`file`]: http://linux.die.net/man/1/file
1000
- [Extracting Metadata]: https://shrinerb.com/rdoc/files/doc/metadata_md.html
1001
- [File Processing]: https://shrinerb.com/rdoc/files/doc/processing_md.html
1002
- [File Validation]: https://shrinerb.com/rdoc/files/doc/validation_md.html
1003
- [metadata direct uploads]: https://github.com/shrinerb/shrine/blob/master/doc/metadata.md#direct-uploads
1068
+ [Extracting Metadata]: /doc/metadata.md#readme
1069
+ [File Processing]: /doc/processing.md#readme
1070
+ [File Validation]: /doc/validation.md#readme
1071
+ [metadata direct uploads]: /doc/metadata.md#direct-uploads
1004
1072
  [uppy xhr upload]: https://uppy.io/docs/xhr-upload/
1005
1073
  [direct uploads walkthrough]: https://github.com/shrinerb/shrine/wiki/Adding-Direct-App-Uploads
1006
1074
  [uppy aws s3]: https://uppy.io/docs/aws-s3/
1007
1075
  [direct S3 uploads walkthrough]: https://github.com/shrinerb/shrine/wiki/Adding-Direct-S3-Uploads<Paste>
1008
- [direct S3 uploads guide]: https://shrinerb.com/rdoc/files/doc/direct_s3_md.html
1076
+ [direct S3 uploads guide]: /doc/direct_s3.md#readme
1009
1077
  [roda demo]: https://github.com/shrinerb/shrine/tree/master/demo
1010
1078
  [rails demo]: https://github.com/erikdahlstrand/shrine-rails-example
1011
1079
  [shrine-tus]: https://github.com/shrinerb/shrine-tus
1012
1080
  [uppy aws s3 multipart]: https://uppy.io/docs/aws-s3-multipart/
1013
- [uppy-s3_multipart]: https://github.com/janko-m/uppy-s3_multipart
1081
+ [uppy-s3_multipart]: https://github.com/janko/uppy-s3_multipart
1014
1082
  [resumable uploads walkthrough]: https://github.com/shrinerb/shrine/wiki/Adding-Resumable-Uploads
1015
1083
  [resumable demo]: https://github.com/shrinerb/shrine-tus-demo
1016
- [backgrounding libraries]: https://github.com/shrinerb/shrine/wiki/Backgrounding-libraries
1084
+ [backgrounding libraries]: https://github.com/shrinerb/shrine/wiki/Backgrounding-Libraries
1085
+ [Mounting Endpoints]: https://github.com/shrinerb/shrine/wiki/Mounting-Endpoints
1017
1086
  [S3 lifecycle Console]: http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html
1018
1087
  [S3 lifecycle API]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#put_bucket_lifecycle_configuration-instance_method
1019
1088
  [Roda]: https://github.com/jeremyevans/roda
1020
1089
  [Refile]: https://github.com/refile/refile
1021
- [CoC]: https://github.com/shrinerb/shrine/blob/master/CODE_OF_CONDUCT.md
1090
+ [CoC]: CODE_OF_CONDUCT.md
1022
1091
  [MIT License]: http://opensource.org/licenses/MIT
@@ -26,6 +26,7 @@ provided as plugins.
26
26
  Shrine.plugin :upload_endpoint
27
27
  Shrine.plugin :presign_endpoint
28
28
  Shrine.plugin :download_endpoint
29
+ Shrine.plugin :derivation_endpoint
29
30
  Shrine.plugin :rack_response
30
31
  Shrine.plugin :rack_file
31
32
 
@@ -42,7 +43,7 @@ Shrine was designed with simplicity in mind. Where other solutions favour
42
43
  complex class-level DSLs, Shrine chooses simple instance-level interfaces where
43
44
  you can write regular Ruby code.
44
45
 
45
- There are no `CarrierWave::Uploader::Base` and `Paperclip::Attachment` [God
46
+ There are no `CarrierWave::Uploader::Base` and `Paperclip::Attachment` [god
46
47
  objects], Shrine has several core classes each with clear responsibilities:
47
48
 
48
49
  * Storage classes encapsulate file operations for the underlying service
@@ -64,31 +65,53 @@ minimal amount of code.
64
65
  ## Modularity
65
66
 
66
67
  Shrine uses a [plugin system] that allows you to pick and choose the features
67
- that you want, which makes it very flexible. Moreover, you're only loading the
68
- code for features that you use, which means that Shrine will generally load
69
- very fast.
68
+ that you want. Moreover, you're only loading the code for features that you
69
+ use, which means that Shrine will generally load very fast.
70
70
 
71
71
  ```rb
72
- Shrine.plugin :logging # loads the logging feature
72
+ Shrine.plugin :logging
73
+
74
+ # translates to
75
+
76
+ require "shrine/plugins/logging"
77
+ Shrine.plugin Shrine::Plugins::Logging
73
78
  ```
74
79
 
75
80
  Shrine comes with a complete attachment functionality, but it also exposes many
76
81
  low level APIs that can be used for building your own customized attachment
77
- flow.
82
+ flow. For example, if you prefer the `Attachment`/`Blob` architecture Active
83
+ Storage provides, you can ditch the Shrine's attachment implementation and use
84
+ uploaders and uploaded files that are decoupled from attachment:
85
+
86
+ ```rb
87
+ uploader = ImageUploader.new(:store)
88
+ uploaded_file = uploader.upload(image) # metadata extraction, upload location generation
89
+
90
+ uploaded_file.id #=> "44ccafc10ce6a4ff22829e8f579ee6b9.jpg"
91
+ uplaoded_file.metadata #=> { ... extracted metadata ... }
92
+
93
+ data = uploaded_file.to_json # serialization
94
+ # ...
95
+ uploaded_file = ImageUploader.uploaded_file(data) # deserialization
96
+
97
+ uploaded_file.url #=> "https://..."
98
+ uploaded_file.download { |tempfile| ... } # streaming download
99
+ uploaded_file.delete
100
+ ```
78
101
 
79
102
  ### Dependencies
80
103
 
81
- Shrine is very diligent when it comes to dependencies. It has only one
82
- mandatory dependency - [Down], a gem for streaming downloads from a URL. Some
83
- Shrine plugins require additional dependencies, but you only need to load them
84
- if you're using those plugins.
104
+ Shrine is very diligent when it comes to dependencies. It has two mandatory
105
+ dependencies [Down] and [ContentDisposition] which are loaded only by
106
+ components that need them. Some Shrine plugins require additional dependencies,
107
+ but you only need to load them if you're using those plugins.
85
108
 
86
- Moreover, Shrine often let you choose between multiple alternative dependencies
87
- for doing the same task. For example, the `determine_mime_type` plugin allows
88
- you to choose between the [`file`] command, [FileMagic], [FastImage],
89
- [MimeMagic], or [Marcel] gem for determining the MIME type, while the
90
- `store_dimensions` plugin can extract dimensions using [FastImage],
91
- [MiniMagick], or [ruby-vips] gem.
109
+ Moreover, Shrine often gives you the ability choose between multiple
110
+ alternative dependencies for doing the same task. For example, the
111
+ `determine_mime_type` plugin allows you to choose between the [`file`] command,
112
+ [FileMagic], [FastImage], [MimeMagic], or [Marcel] gem for determining the MIME
113
+ type, while the `store_dimensions` plugin can extract dimensions using
114
+ [FastImage], [MiniMagick], or [ruby-vips] gem.
92
115
 
93
116
  ```rb
94
117
  Shrine.plugin :determine_mime_type, analyzer: :marcel
@@ -104,9 +127,9 @@ Shrine is designed to handle any types of files. If you're accepting uploads of
104
127
  multiple types of files, such as videos and images, chances are that the logic
105
128
  for handling them will be very different:
106
129
 
107
- * images can be processed on-the-fly, while videos should be transcoded on upload
108
- * you might want to store images on one service and videos on another
109
- * tools for extracting image metadata are different than ones for video metadata
130
+ * small images can be processed on-the-fly, but large files should be processed in a background job
131
+ * which storage service is most suitable might depend on the filetype (images, documents, audios, videos)
132
+ * different filetypes have different metadata to extract which require different tools
110
133
 
111
134
  With Shrine you can create isolated uploaders for each type of file. Plugins
112
135
  that you want to be applied to both uploaders can be applied globally, while
@@ -129,12 +152,63 @@ end
129
152
 
130
153
  ## Processing
131
154
 
132
- Instead of having yet another vendored solution for generating image
133
- thumbnails, Shrine chose to adopt a generic **[ImageProcessing]** gem. The
134
- ImageProcessing gem was created for Shrine, but it can be used in any other
135
- file upload library. It has a very flexible API and takes care of many details
136
- for you, such as [auto orienting] the input image and [sharpening] the
137
- thumbnails after they are resized.
155
+ Most file attachment libraries give you the ability to process files either "on
156
+ upload" (Paperclip, CarrierWave) or "on-the-fly" (Dragonfly, Refile, Active
157
+ Storage). Having only one option is not ideal, because some type of files
158
+ it's more suitable to process on-the-fly (image thumbnails, document previews),
159
+ while other types of files should be processed in a background job (video
160
+ transcoding, raw images)
161
+
162
+ Shrine is the first file attachment library that has support for both
163
+ processing on upload and on-the-fly. So, if you're handling image uploads, you
164
+ can choose to either generate a set of pre-defined image thumbnails in a
165
+ background job:
166
+
167
+ ```rb
168
+ class ImageUploader < Shrine
169
+ process(:store) do |io|
170
+ versions = { original: io }
171
+
172
+ io.download do |original|
173
+ pipeline = ImageProcessing::MiniMagick.source(original)
174
+
175
+ versions[:large] = pipeline.resize_to_limit!(800, 800)
176
+ versions[:medium] = pipeline.resize_to_limit!(500, 500)
177
+ versions[:small] = pipeline.resize_to_limit!(300, 300)
178
+ end
179
+
180
+ versions
181
+ end
182
+ end
183
+ ```
184
+
185
+ or generate thumbnails on-demand:
186
+
187
+ ```rb
188
+ class ImageUploader < Shrine
189
+ derivation :thumbnail do |file, width, height|
190
+ ImageProcessing::MiniMagick
191
+ .source(file)
192
+ .resize_to_limit!(width.to_i, height.to_i)
193
+ end
194
+ end
195
+ ```
196
+ ```rb
197
+ photo.image.derivation_url(:thumbnail, "600", "400")
198
+ #=> ".../thumbnail/600/400/eyJpZCI6ImZvbyIsInN0b3JhZ2UiOiJzdG9yZSJ9?signature=..."
199
+ ```
200
+
201
+ ### Image processing
202
+
203
+ Many file attachment libraries, such as CarrierWave, Paperclip, Dragonfly and
204
+ Refile, implement their own image processing macros. Instead of creating
205
+ yet another in-house implementation, the **[ImageProcessing]** gem was created.
206
+
207
+ Even though the ImageProcessing gem was created for Shrine, it's completely
208
+ generic and can be used standalone, or in any other file upload library (e.g.
209
+ Active Storage uses it now as well). It takes care of many details for you,
210
+ such as [auto orienting] the input image and [sharpening] the thumbnails after
211
+ they are resized.
138
212
 
139
213
  ```rb
140
214
  require "image_processing"
@@ -147,14 +221,15 @@ thumbnail = ImageProcessing::MiniMagick
147
221
  thumbnail #=> #<Tempfile:/var/folders/.../image_processing20180316-18446-1j247h6.png>
148
222
  ```
149
223
 
150
- ### libvips
224
+ #### libvips
151
225
 
152
226
  Probably the biggest ImageProcessing feature is the support for **[libvips]**.
153
- libvips is also a full-featured image processing library, which can process
154
- images very rapidly – often multiple times faster than ImageMagick – with low
155
- memory usage (see [Why is libvips quick]). The `ImageProcessing::Vips` backend
156
- implements the same API as `ImageProcessing::MiniMagick`, so you can easily
157
- swap one for the other.
227
+ libvips is a full-featured image processing library like ImageMagick, with
228
+ impressive performance characteristicsit's often **multiple times faster**
229
+ than ImageMagick and has low memory usage (see [Why is libvips quick]).
230
+
231
+ The `ImageProcessing::Vips` backend implements the same API as
232
+ `ImageProcessing::MiniMagick`, so you can easily swap one for the other.
158
233
 
159
234
  ```rb
160
235
  require "image_processing/mini_magick"
@@ -172,28 +247,35 @@ ImageProcessing::Vips.resize_to_fit(800, 800).call(original)
172
247
 
173
248
  ### Other processors
174
249
 
175
- Shrine's processing block simply executes the Ruby code inside it, so you can
176
- call there any other processor your want. The only thing that Shrine requires
177
- is that processed files are returned as the block result.
250
+ Both processing "on upload" and "on-the-fly" work in a way that you define a
251
+ Ruby block, which accepts a source file and is expected to return a processed
252
+ file. How you're going to do the processing is entirely up to you.
253
+
254
+ This allows you to use any tool you want. For example, you could use the
255
+ [image_optim] gem to perform additional image optimizations:
178
256
 
179
257
  ```rb
180
258
  class VideoUploader < Shrine
181
- plugin :processing
259
+ derivation :thumbnail do |file, width, height|
260
+ thumbnail = ImageProcessing::MiniMagick
261
+ .source(file)
262
+ .resize_to_limit!(width, height)
263
+
264
+ image_optim = ImageOptim.new
265
+ image_optim.optimize_image!(thumbnail.path)
182
266
 
183
- process(:store) do |io, context|
184
- # define your processing
267
+ thumbnail
185
268
  end
186
269
  end
187
270
  ```
188
271
 
189
- ## Metadata
272
+ ## Metadata & Validation
190
273
 
191
274
  Shrine automatically [extracts metadata][metadata] from each uploaded file,
192
- including derivates like image thumbnails, and saves them into the database
275
+ including derivatives like image thumbnails, and saves them into the database
193
276
  column. In addition to filename, filesize, and MIME type that are extracted by
194
277
  default, you can also extract [image dimensions][store_dimensions], or your own
195
- [custom metadata][add_metadata]. This metadata can additionally be
196
- [validated][validation].
278
+ [custom metadata][add_metadata].
197
279
 
198
280
  ```rb
199
281
  photo.image.metadata #=>
@@ -207,61 +289,61 @@ photo.image.metadata #=>
207
289
  # }
208
290
  ```
209
291
 
210
- ## Direct Uploads
292
+ For common metadata there are already [validation macros][validation_helpers],
293
+ but you can also [validate any custom metadata][custom validations].
211
294
 
212
- Instead of submitting selected files synchronously via the form, it's generally
213
- better to start uploading files asynchronously as soon as they're selected.
214
- Shrine streamlines this workflow, allowing you to upload directly [to your
215
- app][upload_endpoint] or [to the cloud][presign_endpoint].
216
-
217
- [Refile] and [Active Storage] provide this functionality as well, and they also
218
- ship with a custom plug-and-play JavaScript solution for integrating these
219
- endpoints. In contrast, Shrine doesn't ship with any custom JavaScript, but
220
- instead recommends using **[Uppy]**. Uppy is a flexible JavaScript file upload
221
- library that allows uploading to a [custom endpoint][XHRUpload], to [AWS
222
- S3][AwsS3], or even to a [resumable endpoint][Tus]. It comes with a set of UI
223
- components, ranging from a simple [status bar][StatusBar] to a full-featured
224
- [dashboard][Dashboard]. Since Uppy is maintained by the whole JavaScript
225
- community, it will generally be better than any homegrown solution.
295
+ ```rb
296
+ class DocumentUploader < Shrine
297
+ Attacher.validate do
298
+ # validation macros
299
+ validate_max_size 10*1024*1024
300
+ validate_mime_type_inclusion %W[application/pdf]
301
+
302
+ # custom validations
303
+ if get.metadata["page_count"] > 30
304
+ errors << "has too many pages (max is 30)"
305
+ end
306
+ end
307
+ end
308
+ ```
226
309
 
227
310
  ## Backgrounding
228
311
 
229
312
  In most file upload solutions background processing was an afterthought, which
230
313
  resulted in complex implementations. Shrine was designed with backgrounding
231
- feature in mind from day one. It is supported via the `backgrounding` plugin
232
- and can be used with [any backgrounding library][backgrounding libraries].
233
-
234
- ## Large Files
314
+ feature in mind from day one. It is supported via the
315
+ [`backgrounding`][backgrounding] plugin and can be used with [any backgrounding
316
+ library][backgrounding libraries].
235
317
 
236
- If your application needs to handle large files (such as videos), Shrine will
237
- go out of the way to make this as resilient and performant as possible.
238
-
239
- ### Streaming
318
+ ## Direct Uploads
240
319
 
241
- Shrine uses and encourages streaming uploads and downloads, where only a small
242
- part of the file is loaded into memory at any given time. This means that
243
- Shrine will use very little memory regardless of the size of the files.
320
+ Shrine doesn't come with a plug-and-play JavaScript solution for client-side
321
+ uploads like Refile and Active Storage, but instead it adopts **[Uppy]**. Uppy
322
+ is a modern JavaScript file upload library, which offers support for uploading
323
+ to [AWS S3][Uppy AwsS3], to a [custom endpoint][Uppy XHRUpload], or even to a
324
+ [resumable endpoint][Uppy Tus]. It comes with a set of UI components, ranging
325
+ from a simple [status bar][Uppy StatusBar] to a full-featured [dashboard][Uppy
326
+ Dashboard]. Since Uppy is maintained by the wide JavaScript community, it's
327
+ generally a better choice than any homegrown solution.
244
328
 
245
- Shrine storages also automatically support [partial downloads][Down streaming]
246
- (provided by the [Down] gem), which allows you to read only a portion of the
247
- file. This can be useful for extracting metadata, because common information such
248
- as MIME type or image dimensions are typically written in the beginning of the
249
- file, so it's enough to download just the first few kilobytes of the file.
329
+ Shrine provides Rack components for uploads that integrate nicely with Uppy.
330
+ So, whether you want Uppy to upload directly [to your app][upload_endpoint], or
331
+ you want to authorize direct uploads [to the cloud][presign_endpoint], Shrine
332
+ has it streamlined.
250
333
 
251
334
  ### Resumable uploads
252
335
 
253
- Another challenge with large files is that it can be difficult for your users
254
- to upload them to your app, especially on flaky internet connections. Since by
255
- default an upload is made in a single long HTTP request, any connection
256
- failures will cause the upload to fail and have to be restarted from the
257
- beginning.
336
+ If your users are uploading large files, flaky internet connections can cause
337
+ uploads to fail halfway, which can be a frustrating user experience. To fix
338
+ this problem, [Transloadit] company has created an open HTTP-based protocol for
339
+ resumable uploads **[tus]**. There are already countless client and server
340
+ [implementations][tus implementations] of the protocol in various languages.
258
341
 
259
- To fix this problem, [Transloadit] company has created an open HTTP-based
260
- protocol for resumable uploads **[tus]**. To use it, you can choose from
261
- numerous client and server [implementations][tus implementations] of the
262
- protocol. In a typical app you would have a [JavaScript client][tus-js-client]
263
- (via [Uppy][uppy tus]) upload to a [Ruby server][tus-ruby-server], and then
264
- attach uploaded files using the handy [Shrine integration][shrine-tus].
342
+ So, if you're expecting large file uploads, you can use Uppy as a [JavaScript
343
+ client][Uppy Tus] and have it upload to [Ruby server][tus-ruby-server], then
344
+ attach uploaded files using the handy [Shrine integration][shrine-tus]. Shrine
345
+ handles uploads and downloads in a streaming fashion, so you can expect low
346
+ memory usage.
265
347
 
266
348
  Alternatively, you can have [resumable multipart uploads directly to
267
349
  S3][uppy-s3_multipart].
@@ -282,11 +364,8 @@ than relying on the `Content-Type` request header), preventing exploits like
282
364
 
283
365
  The `remote_url` plugin requires specifying a `:max_size` option, which limits
284
366
  the maximum allowed size of the remote file. The [Down] gem which the
285
- `remote_url` plugin uses will immediately terminate the download if it reads
286
- from the `Content-Length` response header that the file will be too large. For
287
- chunked responses (where `Content-Length` header is absent) the download will
288
- will be terminated as soon as the received content surpasses the specified
289
- limit.
367
+ `remote_url` plugin uses will [terminate the download early][Down max size]
368
+ when it realizes it's too large.
290
369
 
291
370
  [Paperclip]: https://github.com/thoughtbot/paperclip
292
371
  [CarrierWave]: https://github.com/carrierwaveuploader/carrierwave
@@ -303,7 +382,8 @@ limit.
303
382
  [ROM]: http://rom-rb.org
304
383
  [Hanami::Model]: https://github.com/hanami/model
305
384
  [plugin system]: https://twin.github.io/the-plugin-system-of-sequel-and-roda/
306
- [Down]: https://github.com/janko-m/down
385
+ [Down]: https://github.com/janko/down
386
+ [ContentDisposition]: https://github.com/shrinerb/content_disposition
307
387
  [`file`]: http://linux.die.net/man/1/file
308
388
  [FileMagic]: https://github.com/blackwinter/ruby-filemagic
309
389
  [FastImage]: https://github.com/sdsykes/fastimage
@@ -313,36 +393,38 @@ limit.
313
393
  [mini_mime]: https://github.com/discourse/mini_mime
314
394
  [MiniMagick]: https://github.com/minimagick/minimagick
315
395
  [ruby-vips]: https://github.com/libvips/ruby-vips
316
- [God objects]: https://en.wikipedia.org/wiki/God_object
396
+ [god objects]: https://en.wikipedia.org/wiki/God_object
317
397
  [ImageMagick]: https://www.imagemagick.org
318
398
  [refile-mini_magick]: https://github.com/refile/refile-mini_magick
319
- [ImageProcessing]: https://github.com/janko-m/image_processing
399
+ [ImageProcessing]: https://github.com/janko/image_processing
320
400
  [auto orienting]: https://www.imagemagick.org/script/command-line-options.php#auto-orient
321
401
  [sharpening]: https://photography.tutsplus.com/tutorials/what-is-image-sharpening--cms-26627
322
402
  [libvips]: http://libvips.github.io/libvips/
323
403
  [Why is libvips quick]: https://github.com/libvips/libvips/wiki/Why-is-libvips-quick
324
- [metadata]: https://shrinerb.com/rdoc/files/doc/metadata_md.html
325
- [store_dimensions]: https://shrinerb.com/rdoc/classes/Shrine/Plugins/StoreDimensions.html
326
- [add_metadata]: https://shrinerb.com/rdoc/classes/Shrine/Plugins/AddMetadata.html
327
- [validation]: https://shrinerb.com/rdoc/files/doc/validation_md.html
328
- [upload_endpoint]: https://shrinerb.com/rdoc/classes/Shrine/Plugins/UploadEndpoint.html
329
- [presign_endpoint]: https://shrinerb.com/rdoc/classes/Shrine/Plugins/PresignEndpoint.html
404
+ [metadata]: /doc/metadata.md#readme
405
+ [store_dimensions]: /doc/plugins/store_dimensions.md#readme
406
+ [add_metadata]: /doc/plugins/add_metadata.md#readme
407
+ [validation]: /doc/validation.md#readme
408
+ [upload_endpoint]: /doc/plugins/upload_endpoint.md#readme
409
+ [presign_endpoint]: /doc/plugins/presign_endpoint.md#readme
330
410
  [Uppy]: https://uppy.io
331
- [XHRUpload]: https://uppy.io/docs/xhrupload/
332
- [AwsS3]: https://uppy.io/docs/aws-s3/
333
- [Tus]: https://uppy.io/docs/tus/
334
- [StatusBar]: https://uppy.io/examples/statusbar/
335
- [Dashboard]: https://uppy.io/examples/dashboard/
336
- [background job]: http://shrinerb.com/rdoc/classes/Shrine/Plugins/Backgrounding.html
337
- [backgrounding libraries]: https://github.com/shrinerb/shrine/wiki/Backgrounding-libraries
338
- [Down streaming]: https://github.com/janko-m/down#streaming
411
+ [Uppy XHRUpload]: https://uppy.io/docs/xhrupload/
412
+ [Uppy AwsS3]: https://uppy.io/docs/aws-s3/
413
+ [Uppy Tus]: https://uppy.io/docs/tus/
414
+ [Uppy StatusBar]: https://uppy.io/examples/statusbar/
415
+ [Uppy Dashboard]: https://uppy.io/examples/dashboard/
416
+ [backgrounding]: /doc/plugins/backgrounding.md#readme
417
+ [backgrounding libraries]: https://github.com/shrinerb/shrine/wiki/Backgrounding-Libraries
418
+ [Down streaming]: https://github.com/janko/down#streaming
339
419
  [Transloadit]: https://transloadit.com
340
420
  [tus]: https://tus.io
341
421
  [tus implementations]: https://tus.io/implementations.html
342
- [tus-js-client]: https://github.com/tus/tus-js-client
343
- [uppy tus]: https://uppy.io/docs/tus/
344
- [tus-ruby-server]: https://github.com/janko-m/tus-ruby-server
422
+ [tus-ruby-server]: https://github.com/janko/tus-ruby-server
345
423
  [shrine-tus]: https://github.com/shrinerb/shrine-tus
346
424
  [ImageTragick]: https://imagetragick.com
347
- [uppy-s3_multipart]: https://github.com/janko-m/uppy-s3_multipart
425
+ [uppy-s3_multipart]: https://github.com/janko/uppy-s3_multipart
348
426
  [OWASP]: https://www.owasp.org/index.php/Unrestricted_File_Upload
427
+ [image_optim]: https://github.com/toy/image_optim
428
+ [validation_helpers]: /doc/plugins/validation_helpers.md#readme
429
+ [custom validations]: /doc/validation.md#custom-validations
430
+ [Down max size]: https://github.com/janko/down#maximum-size