shrine 2.19.3 → 3.6.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 (211) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +523 -41
  3. data/LICENSE.txt +1 -1
  4. data/README.md +83 -979
  5. data/doc/advantages.md +231 -204
  6. data/doc/attacher.md +304 -153
  7. data/doc/carrierwave.md +297 -226
  8. data/doc/changing_derivatives.md +308 -0
  9. data/doc/changing_location.md +103 -21
  10. data/doc/changing_storage.md +110 -0
  11. data/doc/creating_persistence_plugins.md +132 -0
  12. data/doc/creating_plugins.md +43 -23
  13. data/doc/creating_storages.md +19 -5
  14. data/doc/design.md +147 -97
  15. data/doc/direct_s3.md +38 -28
  16. data/doc/external/articles.md +63 -0
  17. data/doc/external/extensions.md +53 -0
  18. data/doc/external/misc.md +32 -0
  19. data/doc/getting_started.md +1156 -0
  20. data/doc/metadata.md +190 -109
  21. data/doc/multiple_files.md +93 -30
  22. data/doc/paperclip.md +384 -262
  23. data/doc/plugins/activerecord.md +177 -46
  24. data/doc/plugins/add_metadata.md +139 -38
  25. data/doc/plugins/atomic_helpers.md +217 -0
  26. data/doc/plugins/backgrounding.md +156 -98
  27. data/doc/plugins/cached_attachment_data.md +7 -5
  28. data/doc/plugins/column.md +121 -0
  29. data/doc/plugins/data_uri.md +23 -22
  30. data/doc/plugins/default_storage.md +36 -10
  31. data/doc/plugins/default_url.md +30 -13
  32. data/doc/plugins/delete_raw.md +4 -2
  33. data/doc/plugins/derivation_endpoint.md +186 -101
  34. data/doc/plugins/derivatives.md +839 -0
  35. data/doc/plugins/determine_mime_type.md +4 -2
  36. data/doc/plugins/download_endpoint.md +64 -8
  37. data/doc/plugins/dynamic_storage.md +5 -3
  38. data/doc/plugins/entity.md +263 -0
  39. data/doc/plugins/form_assign.md +55 -0
  40. data/doc/plugins/included.md +31 -8
  41. data/doc/plugins/infer_extension.md +21 -10
  42. data/doc/plugins/instrumentation.md +38 -16
  43. data/doc/plugins/keep_files.md +16 -17
  44. data/doc/plugins/metadata_attributes.md +42 -13
  45. data/doc/plugins/mirroring.md +118 -0
  46. data/doc/plugins/model.md +210 -0
  47. data/doc/plugins/module_include.md +4 -2
  48. data/doc/plugins/multi_cache.md +24 -0
  49. data/doc/plugins/persistence.md +101 -0
  50. data/doc/plugins/presign_endpoint.md +9 -4
  51. data/doc/plugins/pretty_location.md +16 -3
  52. data/doc/plugins/processing.md +4 -2
  53. data/doc/plugins/rack_file.md +8 -2
  54. data/doc/plugins/rack_response.md +6 -2
  55. data/doc/plugins/recache.md +4 -2
  56. data/doc/plugins/refresh_metadata.md +49 -9
  57. data/doc/plugins/remote_url.md +84 -47
  58. data/doc/plugins/remove_attachment.md +27 -6
  59. data/doc/plugins/remove_invalid.md +21 -6
  60. data/doc/plugins/restore_cached_data.md +11 -3
  61. data/doc/plugins/sequel.md +159 -35
  62. data/doc/plugins/signature.md +16 -5
  63. data/doc/plugins/store_dimensions.md +14 -2
  64. data/doc/plugins/tempfile.md +4 -2
  65. data/doc/plugins/type_predicates.md +96 -0
  66. data/doc/plugins/upload_endpoint.md +13 -13
  67. data/doc/plugins/upload_options.md +6 -4
  68. data/doc/plugins/{default_url_options.md → url_options.md} +9 -7
  69. data/doc/plugins/validation.md +97 -0
  70. data/doc/plugins/validation_helpers.md +16 -13
  71. data/doc/plugins/versions.md +15 -19
  72. data/doc/processing.md +438 -221
  73. data/doc/refile.md +188 -170
  74. data/doc/release_notes/1.0.0.md +4 -0
  75. data/doc/release_notes/1.1.0.md +6 -2
  76. data/doc/release_notes/1.2.0.md +4 -0
  77. data/doc/release_notes/1.3.0.md +4 -0
  78. data/doc/release_notes/1.4.0.md +4 -0
  79. data/doc/release_notes/1.4.1.md +4 -0
  80. data/doc/release_notes/1.4.2.md +4 -0
  81. data/doc/release_notes/2.0.0.md +4 -0
  82. data/doc/release_notes/2.0.1.md +4 -0
  83. data/doc/release_notes/2.1.0.md +5 -1
  84. data/doc/release_notes/2.1.1.md +4 -0
  85. data/doc/release_notes/2.10.0.md +4 -0
  86. data/doc/release_notes/2.10.1.md +4 -0
  87. data/doc/release_notes/2.11.0.md +4 -0
  88. data/doc/release_notes/2.12.0.md +4 -0
  89. data/doc/release_notes/2.13.0.md +4 -0
  90. data/doc/release_notes/2.14.0.md +5 -1
  91. data/doc/release_notes/2.15.0.md +11 -7
  92. data/doc/release_notes/2.16.0.md +4 -0
  93. data/doc/release_notes/2.17.0.md +4 -0
  94. data/doc/release_notes/2.18.0.md +4 -0
  95. data/doc/release_notes/2.19.0.md +6 -3
  96. data/doc/release_notes/2.2.0.md +4 -0
  97. data/doc/release_notes/2.3.0.md +4 -0
  98. data/doc/release_notes/2.3.1.md +4 -0
  99. data/doc/release_notes/2.4.0.md +4 -0
  100. data/doc/release_notes/2.4.1.md +4 -0
  101. data/doc/release_notes/2.5.0.md +4 -0
  102. data/doc/release_notes/2.6.0.md +4 -0
  103. data/doc/release_notes/2.6.1.md +4 -0
  104. data/doc/release_notes/2.7.0.md +4 -0
  105. data/doc/release_notes/2.8.0.md +4 -0
  106. data/doc/release_notes/2.9.0.md +4 -0
  107. data/doc/release_notes/3.0.0.md +981 -0
  108. data/doc/release_notes/3.0.1.md +22 -0
  109. data/doc/release_notes/3.1.0.md +73 -0
  110. data/doc/release_notes/3.2.0.md +96 -0
  111. data/doc/release_notes/3.2.1.md +31 -0
  112. data/doc/release_notes/3.2.2.md +14 -0
  113. data/doc/release_notes/3.3.0.md +105 -0
  114. data/doc/release_notes/3.4.0.md +35 -0
  115. data/doc/release_notes/3.5.0.md +63 -0
  116. data/doc/release_notes/3.6.0.md +23 -0
  117. data/doc/retrieving_uploads.md +5 -2
  118. data/doc/securing_uploads.md +60 -37
  119. data/doc/storage/file_system.md +20 -3
  120. data/doc/storage/memory.md +19 -0
  121. data/doc/storage/s3.md +122 -78
  122. data/doc/testing.md +141 -133
  123. data/doc/upgrading_to_3.md +708 -0
  124. data/doc/validation.md +54 -90
  125. data/lib/shrine/attacher.rb +292 -169
  126. data/lib/shrine/attachment.rb +13 -46
  127. data/lib/shrine/plugins/_persistence.rb +93 -0
  128. data/lib/shrine/plugins/activerecord.rb +77 -34
  129. data/lib/shrine/plugins/add_metadata.rb +25 -17
  130. data/lib/shrine/plugins/atomic_helpers.rb +119 -0
  131. data/lib/shrine/plugins/backgrounding.rb +77 -113
  132. data/lib/shrine/plugins/cached_attachment_data.rb +6 -15
  133. data/lib/shrine/plugins/column.rb +102 -0
  134. data/lib/shrine/plugins/data_uri.rb +38 -36
  135. data/lib/shrine/plugins/default_storage.rb +45 -15
  136. data/lib/shrine/plugins/default_url.rb +12 -24
  137. data/lib/shrine/plugins/default_url_options.rb +3 -30
  138. data/lib/shrine/plugins/delete_raw.rb +10 -16
  139. data/lib/shrine/plugins/derivation_endpoint.rb +130 -171
  140. data/lib/shrine/plugins/derivatives.rb +645 -0
  141. data/lib/shrine/plugins/determine_mime_type.rb +9 -21
  142. data/lib/shrine/plugins/download_endpoint.rb +118 -133
  143. data/lib/shrine/plugins/dynamic_storage.rb +5 -11
  144. data/lib/shrine/plugins/entity.rb +158 -0
  145. data/lib/shrine/plugins/form_assign.rb +108 -0
  146. data/lib/shrine/plugins/included.rb +6 -6
  147. data/lib/shrine/plugins/infer_extension.rb +17 -20
  148. data/lib/shrine/plugins/instrumentation.rb +59 -43
  149. data/lib/shrine/plugins/keep_files.rb +3 -15
  150. data/lib/shrine/plugins/metadata_attributes.rb +28 -19
  151. data/lib/shrine/plugins/mirroring.rb +142 -0
  152. data/lib/shrine/plugins/model.rb +160 -0
  153. data/lib/shrine/plugins/module_include.rb +3 -3
  154. data/lib/shrine/plugins/multi_cache.rb +27 -0
  155. data/lib/shrine/plugins/presign_endpoint.rb +27 -28
  156. data/lib/shrine/plugins/pretty_location.rb +15 -9
  157. data/lib/shrine/plugins/processing.rb +22 -9
  158. data/lib/shrine/plugins/rack_file.rb +2 -42
  159. data/lib/shrine/plugins/rack_response.rb +21 -10
  160. data/lib/shrine/plugins/recache.rb +6 -5
  161. data/lib/shrine/plugins/refresh_metadata.rb +13 -11
  162. data/lib/shrine/plugins/remote_url.rb +49 -49
  163. data/lib/shrine/plugins/remove_attachment.rb +12 -6
  164. data/lib/shrine/plugins/remove_invalid.rb +19 -8
  165. data/lib/shrine/plugins/restore_cached_data.rb +13 -7
  166. data/lib/shrine/plugins/sequel.rb +86 -36
  167. data/lib/shrine/plugins/signature.rb +10 -16
  168. data/lib/shrine/plugins/store_dimensions.rb +35 -40
  169. data/lib/shrine/plugins/tempfile.rb +1 -3
  170. data/lib/shrine/plugins/type_predicates.rb +113 -0
  171. data/lib/shrine/plugins/upload_endpoint.rb +28 -24
  172. data/lib/shrine/plugins/upload_options.rb +14 -15
  173. data/lib/shrine/plugins/url_options.rb +31 -0
  174. data/lib/shrine/plugins/validation.rb +80 -0
  175. data/lib/shrine/plugins/validation_helpers.rb +35 -58
  176. data/lib/shrine/plugins/versions.rb +107 -87
  177. data/lib/shrine/plugins.rb +22 -0
  178. data/lib/shrine/storage/file_system.rb +46 -64
  179. data/lib/shrine/storage/linter.rb +42 -7
  180. data/lib/shrine/storage/memory.rb +49 -0
  181. data/lib/shrine/storage/s3.rb +173 -160
  182. data/lib/shrine/uploaded_file.rb +32 -32
  183. data/lib/shrine/version.rb +3 -3
  184. data/lib/shrine.rb +87 -150
  185. data/shrine.gemspec +11 -12
  186. metadata +92 -82
  187. data/doc/migrating_storage.md +0 -76
  188. data/doc/plugins/backup.md +0 -31
  189. data/doc/plugins/copy.md +0 -24
  190. data/doc/plugins/delete_promoted.md +0 -12
  191. data/doc/plugins/direct_upload.md +0 -172
  192. data/doc/plugins/hooks.md +0 -58
  193. data/doc/plugins/logging.md +0 -42
  194. data/doc/plugins/migration_helpers.md +0 -60
  195. data/doc/plugins/moving.md +0 -19
  196. data/doc/plugins/multi_delete.md +0 -20
  197. data/doc/plugins/parallelize.md +0 -16
  198. data/doc/plugins/parsed_json.md +0 -23
  199. data/doc/regenerating_versions.md +0 -143
  200. data/lib/shrine/plugins/background_helpers.rb +0 -5
  201. data/lib/shrine/plugins/backup.rb +0 -90
  202. data/lib/shrine/plugins/copy.rb +0 -50
  203. data/lib/shrine/plugins/delete_promoted.rb +0 -20
  204. data/lib/shrine/plugins/direct_upload.rb +0 -217
  205. data/lib/shrine/plugins/hooks.rb +0 -90
  206. data/lib/shrine/plugins/logging.rb +0 -142
  207. data/lib/shrine/plugins/migration_helpers.rb +0 -70
  208. data/lib/shrine/plugins/moving.rb +0 -57
  209. data/lib/shrine/plugins/multi_delete.rb +0 -32
  210. data/lib/shrine/plugins/parallelize.rb +0 -78
  211. data/lib/shrine/plugins/parsed_json.rb +0 -29
data/doc/advantages.md CHANGED
@@ -1,9 +1,13 @@
1
- # Advantages of Shrine
1
+ ---
2
+ title: Advantages of Shrine
3
+ ---
2
4
 
3
- There are many existing file upload solutions for Ruby out there [Paperclip],
4
- [CarrierWave], [Dragonfly], [Refile], and [Active Storage], to name the most
5
- popular ones. This guide will attempt to cover some of the main advantages that
6
- Shrine offers compared to these alternatives.
5
+ There are many existing file upload solutions for Ruby out there. This guide
6
+ will attempt to cover some of the main advantages that Shrine offers compared
7
+ to these alternatives.
8
+
9
+ For a more direct comparison with specific file attachment libraries, there are
10
+ more specialized guides for [CarrierWave], [Paperclip], and [Refile] users.
7
11
 
8
12
  ## Generality
9
13
 
@@ -11,15 +15,15 @@ Many alternative file upload solutions are coupled to either Rails (Active
11
15
  Storage) or Active Record itself (Paperclip, Dragonfly). This is not ideal, as
12
16
  Rails-specific solutions fragment the Ruby community between developers that
13
17
  use Rails and developers that don't. There are many great web frameworks
14
- ([Sinatra], [Roda], [Cuba], [Hanami], [Grape]) and database libraries
15
- ([Sequel], [ROM], [Hanami::Model]) out there that people use instead of
16
- Rails and Active Record.
18
+ ([Sinatra], [Roda], [Cuba], [Hanami], [Grape]) and persistence libraries
19
+ ([Sequel], [ROM], [Hanami::Model]) out there that people use instead of Rails
20
+ and Active Record.
17
21
 
18
22
  Shrine, on the other hand, doesn't make any assumptions about which web
19
- framework or ORM you're using. Any web-specific functionality is implemented
20
- on top of [Rack], the Ruby web server interface that powers all the popular
21
- Ruby web frameworks (including Rails). The integrations for specific ORMs are
22
- provided as plugins.
23
+ framework or persistence library you're using. Any web-specific functionality
24
+ is implemented on top of [Rack], the Ruby web server interface that powers all
25
+ the popular Ruby web frameworks (including Rails). The integrations for
26
+ specific ORMs are provided as plugins.
23
27
 
24
28
  ```rb
25
29
  # Rack-based plugins
@@ -34,59 +38,62 @@ Shrine.plugin :rack_file
34
38
  Shrine.plugin :activerecord
35
39
  Shrine.plugin :sequel
36
40
  Shrine.plugin :mongoid # https://github.com/shrinerb/shrine-mongoid
41
+ Shrine.plugin :rom # https://github.com/shrinerb/shrine-rom
37
42
  Shrine.plugin :hanami # https://github.com/katafrakt/hanami-shrine
38
43
  ```
39
44
 
40
45
  ## Simplicity
41
46
 
42
- Shrine was designed with simplicity in mind. Where other solutions favour
43
- complex class-level DSLs, Shrine chooses simple instance-level interfaces where
44
- you can write regular Ruby code.
45
-
46
- There are no `CarrierWave::Uploader::Base` and `Paperclip::Attachment` [god
47
- objects], Shrine has several core classes each with clear responsibilities:
47
+ Where some popular file attachment libraries have [god objects]
48
+ (`CarrierWave::Uploader::Base` and `Paperclip::Attachment`), Shrine distributes
49
+ responsibilities across multiple core classes:
48
50
 
49
- * Storage classes encapsulate file operations for the underlying service
50
- * `Shrine` handles uploads and manages plugins
51
- * `Shrine::UploadedFile` repesents a file that was uploaded to a storage
52
- * `Shrine::Attacher` handles attaching files to records
53
- * `Shrine::Attachment` adds convenience methods to model instances
51
+ | Class | Description |
52
+ | :---- | :----------- |
53
+ | `Shrine::Storage::*` | Encapsulate file operations for the underlying service |
54
+ | `Shrine` | Wraps uploads and handles loading plugins |
55
+ | `Shrine::UploadedFile` | Represents a file that was uploaded to a storage |
56
+ | `Shrine::Attacher` | Handles attaching files to records |
57
+ | `Shrine::Attachment` | Adds convenience attachment methods to model instances |
54
58
 
55
59
  ```rb
56
- photo.image #=> #<Shrine::UploadedFile>
57
- photo.image.storage #=> #<Shrine::Storage::S3>
58
- photo.image.uploader #=> #<Shrine>
59
- photo.image_attacher #=> #<Shrine::Attacher>
60
+ photo.image #=> #<Shrine::UploadedFile>
61
+ photo.image.storage #=> #<Shrine::Storage::S3>
62
+ photo.image.uploader #=> #<Shrine>
63
+ photo.image_attacher #=> #<Shrine::Attacher>
64
+ photo.class.ancestors #=> [..., #<Shrine::Attachment(:image)>, ...]
60
65
  ```
61
66
 
62
- Special care was taken to make integrating new storages and ORMs possible with
63
- minimal amount of code.
67
+ The attachment functionality is decoupled from persistence and storage, which
68
+ makes it much easier to reason about. Also, special care was taken to make
69
+ integrating new storages and persistence libraries as easy as possible.
64
70
 
65
71
  ## Modularity
66
72
 
67
73
  Shrine uses a [plugin system] that allows you to pick and choose the features
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.
74
+ that you want. Moreover, you'll only be loading code for the features you've
75
+ selected, which means that Shrine will generally load much faster than the
76
+ alternatives.
70
77
 
71
78
  ```rb
72
79
  Shrine.plugin :instrumentation
73
80
 
74
- # translates to
81
+ # which translates to
75
82
 
76
83
  require "shrine/plugins/instrumentation"
77
84
  Shrine.plugin Shrine::Plugins::Instrumentation
78
85
  ```
86
+ ```rb
87
+ Shrine.method(:instrument).owner #=> Shrine::Plugins::Instrumentation::ClassMethods
88
+ ```
79
89
 
80
- Shrine comes with a complete attachment functionality, but it also exposes many
81
- low level APIs that can be used for building your own customized attachment
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:
90
+ Shrine recommends a certain type of attachment flow, but it still offers good
91
+ low-level abstractions that give you the flexibility to build your own flow.
85
92
 
86
93
  ```rb
87
94
  uploaded_file = ImageUploader.upload(image, :store) # metadata extraction, upload location generation
88
95
  uploaded_file.id #=> "44ccafc10ce6a4ff22829e8f579ee6b9.jpg"
89
- uplaoded_file.metadata #=> { ... extracted metadata ... }
96
+ uploaded_file.metadata #=> { ... extracted metadata ... }
90
97
 
91
98
  data = uploaded_file.to_json # serialization
92
99
  # ...
@@ -101,8 +108,8 @@ uploaded_file.delete
101
108
 
102
109
  Shrine is very diligent when it comes to dependencies. It has two mandatory
103
110
  dependencies – [Down] and [ContentDisposition] – which are loaded only by
104
- components that need them. Some Shrine plugins require additional dependencies,
105
- but you only need to load them if you're using those plugins.
111
+ components that need them. Some Shrine plugins also require additional
112
+ dependencies, but you only need to load them if you're using those plugins.
106
113
 
107
114
  Moreover, Shrine often gives you the ability choose between multiple
108
115
  alternative dependencies for doing the same task. For example, the
@@ -116,72 +123,68 @@ Shrine.plugin :determine_mime_type, analyzer: :marcel
116
123
  Shrine.plugin :store_dimensions, analyzer: :mini_magick
117
124
  ```
118
125
 
119
- This approach gives you control over your dependencies by allowing you to
120
- choose the combination that best suit your needs.
121
-
122
126
  ## Inheritance
123
127
 
124
128
  Shrine is designed to handle any types of files. If you're accepting uploads of
125
129
  multiple types of files, such as videos and images, chances are that the logic
126
- for handling them will be very different:
130
+ for handling them will differ:
127
131
 
128
132
  * small images can be processed on-the-fly, but large files should be processed in a background job
129
- * which storage service is most suitable might depend on the filetype (images, documents, audios, videos)
130
- * different filetypes have different metadata to extract which require different tools
133
+ * you might want to store different files to different storage services (images, documents, audios, videos)
134
+ * extracting metadata might require different tools depending on the filetype
131
135
 
132
- With Shrine you can create isolated uploaders for each type of file. Plugins
133
- that you want to be applied to both uploaders can be applied globally, while
134
- other plugins would be loaded only for a specific uploader.
136
+ With Shrine you can create isolated uploaders for each type of file. For
137
+ features you want all uploaders to share, their plugins can be loaded globally,
138
+ while other plugins you can load only for selected uploaders.
135
139
 
136
140
  ```rb
141
+ # loaded for all plugins
137
142
  Shrine.plugin :activerecord
138
143
  Shrine.plugin :instrumentation
139
144
  ```
140
145
  ```rb
141
146
  class ImageUploader < Shrine
147
+ # loaded only for ImageUploader
142
148
  plugin :store_dimensions
143
149
  end
144
150
  ```
145
151
  ```rb
146
152
  class VideoUploader < Shrine
153
+ # loaded only for VideoUploader
147
154
  plugin :default_storage, store: :vimeo
148
155
  end
149
156
  ```
150
157
 
151
158
  ## Processing
152
159
 
153
- Most file attachment libraries give you the ability to process files either "on
154
- upload" (Paperclip, CarrierWave) or "on-the-fly" (Dragonfly, Refile, Active
155
- Storage). Having only one option is not ideal, because some type of files
156
- it's more suitable to process on-the-fly (image thumbnails, document previews),
157
- while other types of files should be processed in a background job (video
158
- transcoding, raw images)
160
+ Most file attachment libraries allow you to process files either "eagerly"
161
+ (Paperclip, CarrierWave) or "on-the-fly" (Dragonfly, Refile, Active Storage).
162
+ However, each approach is suitable for different requirements. For instance,
163
+ while on-the-fly processing is suitable for fast processing (image thumbnails,
164
+ document previews), longer running processing (video transcoding, raw images)
165
+ should be moved into a background job.
159
166
 
160
- Shrine is the first file attachment library that has support for both
161
- processing on upload and on-the-fly. So, if you're handling image uploads, you
162
- can choose to either generate a set of pre-defined image thumbnails in a
163
- background job:
167
+ That's why Shrine supports both [eager][derivatives] and
168
+ [on-the-fly][derivation_endpoint] processing. For example, if you're handling
169
+ image uploads, you can choose to either generate a set of pre-defined
170
+ thumbnails during attachment:
164
171
 
165
172
  ```rb
166
173
  class ImageUploader < Shrine
167
- process(:store) do |io|
168
- versions = { original: io }
169
-
170
- io.download do |original|
171
- pipeline = ImageProcessing::MiniMagick.source(original)
172
-
173
- versions[:large] = pipeline.resize_to_limit!(800, 800)
174
- versions[:medium] = pipeline.resize_to_limit!(500, 500)
175
- versions[:small] = pipeline.resize_to_limit!(300, 300)
176
- end
177
-
178
- versions
174
+ Attacher.derivatives do |original|
175
+ magick = ImageProcessing::MiniMagick.source(original)
176
+
177
+ {
178
+ large: magick.resize_to_limit!(800, 800),
179
+ medium: magick.resize_to_limit!(500, 500),
180
+ small: magick.resize_to_limit!(300, 300),
181
+ }
179
182
  end
180
183
  end
181
184
  ```
182
185
  ```rb
183
- photo.image_url(:large)
184
- #=> "https://s3.amazonaws.com/path/to/large.jpg"
186
+ photo.image_derivatives! # creates thumbnails
187
+ photo.image_url(:large) #=> "https://s3.amazonaws.com/path/to/large.jpg"
185
188
  ```
186
189
 
187
190
  or generate thumbnails on-demand:
@@ -196,82 +199,89 @@ class ImageUploader < Shrine
196
199
  end
197
200
  ```
198
201
  ```rb
199
- photo.image.derivation_url(:thumbnail, "600", "400")
202
+ photo.image.derivation_url(:thumbnail, 600, 400)
200
203
  #=> ".../thumbnail/600/400/eyJpZCI6ImZvbyIsInN0b3JhZ2UiOiJzdG9yZSJ9?signature=..."
201
204
  ```
202
205
 
203
- ### Image processing
206
+ ### ImageMagick
204
207
 
205
208
  Many file attachment libraries, such as CarrierWave, Paperclip, Dragonfly and
206
- Refile, implement their own image processing macros. Instead of creating
207
- yet another in-house implementation, the **[ImageProcessing]** gem was created.
208
-
209
- Even though the ImageProcessing gem was created for Shrine, it's completely
210
- generic and can be used standalone, or in any other file upload library (e.g.
211
- Active Storage uses it now as well). It takes care of many details for you,
212
- such as [auto orienting] the input image and [sharpening] the thumbnails after
213
- they are resized.
209
+ Refile, implement their own image processing macros. Rather than building yet
210
+ another in-house implementation, a general purpose **[ImageProcessing]** gem
211
+ was created instead, which works great with Shrine.
214
212
 
215
213
  ```rb
216
- require "image_processing"
214
+ require "image_processing/mini_magick"
217
215
 
218
216
  thumbnail = ImageProcessing::MiniMagick
219
217
  .source(image)
220
218
  .resize_to_limit(400, 400)
221
219
  .call # convert input.jpg -auto-orient -resize 400x400> -sharpen 0x1 output.jpg
222
220
 
223
- thumbnail #=> #<Tempfile:/var/folders/.../image_processing20180316-18446-1j247h6.png>
221
+ thumbnail #=> #<Tempfile:/var/folders/.../image_processing20180316-18446-1j247h6.jpg>
224
222
  ```
225
223
 
226
- #### libvips
224
+ It takes care of many details for you, such as [auto orienting] the input image
225
+ and applying [sharpening] to resized images. It also has support for
226
+ [libvips](#libvips).
227
227
 
228
- Probably the biggest ImageProcessing feature is the support for **[libvips]**.
229
- libvips is a full-featured image processing library like ImageMagick, with
230
- impressive performance characteristics – it's often **multiple times faster**
231
- than ImageMagick and has low memory usage (see [Why is libvips quick]).
228
+ ### libvips
232
229
 
233
- The `ImageProcessing::Vips` backend implements the same API as
234
- `ImageProcessing::MiniMagick`, so you can easily swap one for the other.
230
+ **[libvips]** is a full-featured image processing library like ImageMagick,
231
+ with [great performance characteristics][libvips performance]. It's often
232
+ **multiple times faster** than ImageMagick, and also has lower memory usage.
233
+ For more details, see [Why is libvips quick].
234
+
235
+ The ImageProcessing gem provides libvips support as an alternative
236
+ `ImageProcessing::Vips` backend, sharing the same API as the
237
+ `ImageProcessing::MiniMagick` backend.
235
238
 
236
239
  ```rb
237
- require "image_processing/mini_magick"
238
240
  require "image_processing/vips"
239
- require "open-uri"
240
-
241
- original = open("https://upload.wikimedia.org/wikipedia/commons/3/36/Hopetoun_falls.jpg")
242
241
 
243
- ImageProcessing::MiniMagick.resize_to_fit(800, 800).call(original)
244
- #=> 1.0s
245
-
246
- ImageProcessing::Vips.resize_to_fit(800, 800).call(original)
247
- #=> 0.2s (5x faster)
242
+ # this now generates the thumbnail using libvips
243
+ ImageProcessing::Vips
244
+ .source(image)
245
+ .resize_to_limit!(400, 400)
248
246
  ```
249
247
 
250
248
  ### Other processors
251
249
 
252
- Both processing "on upload" and "on-the-fly" work in a way that you define a
253
- Ruby block, which accepts a source file and is expected to return a processed
254
- file. How you're going to do the processing is entirely up to you.
250
+ In contrast to most file attachment libraries, file processing in Shrine is
251
+ just a functional transformation, where you receive the source file on the
252
+ input and return processed files on the output. This makes it easier to use
253
+ custom processing tools and encourages building generic processors that can be
254
+ reused outside of Shrine.
255
255
 
256
- This allows you to use any tool you want. For example, you could use the
257
- [image_optim] gem to perform additional image optimizations:
256
+ Here is an example of transcoding videos using the [streamio-ffmpeg] gem:
258
257
 
258
+ ```rb
259
+ # Gemfile
260
+ gem "streamio-ffmpeg"
261
+ ```
259
262
  ```rb
260
263
  class VideoUploader < Shrine
261
- derivation :thumbnail do |file, width, height|
262
- thumbnail = ImageProcessing::MiniMagick
263
- .source(file)
264
- .resize_to_limit!(width, height)
264
+ Attacher.derivatives do |original|
265
+ transcoded = Tempfile.new ["transcoded", ".mp4"]
266
+ screenshot = Tempfile.new ["screenshot", ".jpg"]
265
267
 
266
- image_optim = ImageOptim.new
267
- image_optim.optimize_image!(thumbnail.path)
268
+ movie = FFMPEG::Movie.new(original.path)
269
+ movie.transcode(transcoded.path)
270
+ movie.screenshot(screenshot.path)
268
271
 
269
- thumbnail
272
+ { transcoded: transcoded, screenshot: screenshot }
270
273
  end
271
274
  end
272
275
  ```
276
+ ```rb
277
+ movie.video_derivatives! # create derivatives
278
+
279
+ movie.video #=> #<Shrine::UploadedFile id="5a5cd0.mov" ...>
280
+ movie.video(:transcoded) #=> #<Shrine::UploadedFile id="7481d6.mp4" ...>
281
+ movie.video(:screenshot) #=> #<Shrine::UploadedFile id="8f3136.jpg" ...>
282
+ ```
273
283
 
274
- ## Metadata & Validation
284
+ ## Metadata
275
285
 
276
286
  Shrine automatically [extracts metadata][metadata] from each uploaded file,
277
287
  including derivatives like image thumbnails, and saves them into the database
@@ -279,6 +289,17 @@ column. In addition to filename, filesize, and MIME type that are extracted by
279
289
  default, you can also extract [image dimensions][store_dimensions], or your own
280
290
  [custom metadata][add_metadata].
281
291
 
292
+ ```rb
293
+ class ImageUploader < Shrine
294
+ plugin :determine_mime_type # mime_type
295
+ plugin :store_dimensions # width & height
296
+
297
+ add_metadata :resolution do |io|
298
+ image = MiniMagick::Image.new(io.path)
299
+ image.resolution
300
+ end
301
+ end
302
+ ```
282
303
  ```rb
283
304
  photo.image.metadata #=>
284
305
  # {
@@ -287,23 +308,30 @@ photo.image.metadata #=>
287
308
  # "mime_type" => "image/jpeg",
288
309
  # "width" => 600,
289
310
  # "height" => 400,
311
+ # "resolution" => [72, 72],
290
312
  # ...
291
313
  # }
292
314
  ```
293
315
 
294
- For common metadata there are already [validation macros][validation_helpers],
295
- but you can also [validate any custom metadata][custom validations].
316
+ ## Validation
317
+
318
+ For file validations there are [built-in validators][validation_helpers], but
319
+ you can also just use plain Ruby code:
296
320
 
297
321
  ```rb
298
- class DocumentUploader < Shrine
322
+ class ImageUploader < Shrine
323
+ plugin :validation_helpers
324
+
299
325
  Attacher.validate do
300
- # validation macros
301
326
  validate_max_size 10*1024*1024
302
- validate_mime_type_inclusion %W[application/pdf]
327
+ validate_extension %w[jpg jpeg png webp]
303
328
 
304
- # custom validations
305
- if get.metadata["page_count"] > 30
306
- errors << "has too many pages (max is 30)"
329
+ if validate_mime_type %W[image/jpeg image/png image/webp]
330
+ validate_max_dimensions [5000, 5000]
331
+
332
+ unless ImageProcessing::MiniMagick.valid_image?(file.download.path)
333
+ error << "seems to be corrupted"
334
+ end
307
335
  end
308
336
  end
309
337
  end
@@ -311,69 +339,70 @@ end
311
339
 
312
340
  ## Backgrounding
313
341
 
314
- In most file upload solutions background processing was an afterthought, which
315
- resulted in complex implementations. Shrine was designed with backgrounding
316
- feature in mind from day one. It is supported via the
317
- [`backgrounding`][backgrounding] plugin and can be used with [any backgrounding
318
- library][backgrounding libraries].
342
+ In most file upload solutions, support for background processing was an
343
+ afterthought, which resulted in complex and unreliable implementations. Shrine
344
+ was designed with backgrounding feature in mind from day one. It is supported
345
+ via the [`backgrounding`][backgrounding] plugin and can be used with [any
346
+ backgrounding library][backgrounding libraries].
347
+
348
+ ```rb
349
+ Shrine.plugin :backgrounding
350
+ Shrine::Attacher.promote_block do
351
+ PromoteJob.perform_async(self.class.name, record.class.name, record.id, name, file_data)
352
+ end
353
+ ```
354
+ ```rb
355
+ class PromoteJob
356
+ include Sidekiq::Worker
357
+
358
+ def perform(attacher_class, record_class, record_id, name, file_data)
359
+ attacher_class = Object.const_get(attacher_class)
360
+ record = Object.const_get(record_class).find(record_id) # if using Active Record
361
+
362
+ attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
363
+ attacher.create_derivatives # perform processing
364
+ attacher.atomic_promote
365
+ rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
366
+ # attachment changes are detected for concurrency safety
367
+ end
368
+ end
369
+ ```
370
+
371
+ With Shrine, there is no need for a separate boolean column that indicates the
372
+ processing status. Processed file data is stored into the attachment database
373
+ column, which allows you to easily check whether a file has been processed.
374
+
375
+ ```rb
376
+ photo = Photo.create(image: file) # background job is kicked off
377
+
378
+ photo.image(:large) #=> nil (thumbnails are still being processed)
379
+ # ... sometime later ...
380
+ photo.image(:large) #=> #<Shrine::UploadedFile> (processing has finished)
381
+ ```
319
382
 
320
383
  ## Direct Uploads
321
384
 
322
- Shrine doesn't come with a plug-and-play JavaScript solution for client-side
323
- uploads like Refile and Active Storage, but instead it adopts **[Uppy]**. Uppy
324
- is a modern JavaScript file upload library, which offers support for uploading
325
- to [AWS S3][Uppy AwsS3], to a [custom endpoint][Uppy XHRUpload], or even to a
326
- [resumable endpoint][Uppy Tus]. It comes with a set of UI components, ranging
327
- from a simple [status bar][Uppy StatusBar] to a full-featured [dashboard][Uppy
328
- Dashboard]. Since Uppy is maintained by the wide JavaScript community, it's
329
- generally a better choice than any homegrown solution.
330
-
331
- Shrine provides Rack components for uploads that integrate nicely with Uppy.
332
- So, whether you want Uppy to upload directly [to your app][upload_endpoint], or
333
- you want to authorize direct uploads [to the cloud][presign_endpoint], Shrine
334
- has it streamlined.
335
-
336
- ### Resumable uploads
337
-
338
- If your users are uploading large files, flaky internet connections can cause
339
- uploads to fail halfway, which can be a frustrating user experience. To fix
340
- this problem, [Transloadit] company has created an open HTTP-based protocol for
341
- resumable uploads **[tus]**. There are already countless client and server
342
- [implementations][tus implementations] of the protocol in various languages.
343
-
344
- So, if you're expecting large file uploads, you can use Uppy as a [JavaScript
345
- client][Uppy Tus] and have it upload to [Ruby server][tus-ruby-server], then
346
- attach uploaded files using the handy [Shrine integration][shrine-tus]. Shrine
347
- handles uploads and downloads in a streaming fashion, so you can expect low
348
- memory usage.
349
-
350
- Alternatively, you can have [resumable multipart uploads directly to
351
- S3][uppy-s3_multipart].
352
-
353
- ## Security
354
-
355
- It's [important][OWASP] to care about security when handling file uploads, and
356
- Shrine bakes in many good practices. For starters, it uses a separate
357
- "temporary" storage for direct uploads, making it easy to periodically clear
358
- uploads that didn't end up being attached and difficult for the attacker to
359
- flood the main storage.
360
-
361
- File processing and upload to permanent storage is done outside of a database
362
- transaction, and only after the file has been successfully validated. The
363
- `determine_mime_type` plugin determines MIME type from the file content (rather
364
- than relying on the `Content-Type` request header), preventing exploits like
365
- [ImageTragick].
366
-
367
- The `remote_url` plugin requires specifying a `:max_size` option, which limits
368
- the maximum allowed size of the remote file. The [Down] gem which the
369
- `remote_url` plugin uses will [terminate the download early][Down max size]
370
- when it realizes it's too large.
371
-
372
- [Paperclip]: https://github.com/thoughtbot/paperclip
373
- [CarrierWave]: https://github.com/carrierwaveuploader/carrierwave
374
- [Dragonfly]: http://markevans.github.io/dragonfly/
375
- [Refile]: https://github.com/refile/refile
376
- [Active Storage]: https://github.com/rails/rails/tree/master/activestorage#active-storage
385
+ For client side uploads, Shrine adopts **[Uppy]**, a modern JavaScript file
386
+ upload library. This gives the developer a lot more power in customizing the
387
+ user experience compared to a custom JavaScript solution implemented by Refile
388
+ and Active Storage.
389
+
390
+ Uppy supports direct uploads to [AWS S3][Uppy AwsS3] or to a [custom
391
+ endpoint][Uppy XHRUpload]. It also supports **resumable** uploads, either
392
+ [directly to S3][Uppy AwsS3Multipart] or via the [tus protocol][tus]. For the
393
+ UI you can choose from various components, ranging from a simple [status
394
+ bar][Uppy StatusBar] to a full-featured [dashboard][Uppy Dashboard].
395
+
396
+ Shrine provides server side components for each type of upload. They are built
397
+ on top of Rack, so that they can be used with any Ruby web framework.
398
+
399
+ | Uppy | Shrine |
400
+ | :--- | :----- |
401
+ | [XHRUpload][Uppy XHRUpload] | [`upload_endpoint`][upload_endpoint] |
402
+ | [AwsS3][Uppy AwsS3] | [`presign_endpoint`][presign_endpoint] |
403
+ | [AwsS3Multipart][Uppy AwsS3Multipart] | [`uppy-s3_multipart`][uppy-s3_multipart] |
404
+ | [Tus][Uppy Tus] | [`tus-ruby-server`][tus-ruby-server] |
405
+
377
406
  [Rack]: https://rack.github.io
378
407
  [Sinatra]: http://sinatrarb.com
379
408
  [Roda]: http://roda.jeremyevans.net
@@ -396,37 +425,35 @@ when it realizes it's too large.
396
425
  [MiniMagick]: https://github.com/minimagick/minimagick
397
426
  [ruby-vips]: https://github.com/libvips/ruby-vips
398
427
  [god objects]: https://en.wikipedia.org/wiki/God_object
399
- [ImageMagick]: https://www.imagemagick.org
400
- [refile-mini_magick]: https://github.com/refile/refile-mini_magick
401
428
  [ImageProcessing]: https://github.com/janko/image_processing
402
429
  [auto orienting]: https://www.imagemagick.org/script/command-line-options.php#auto-orient
403
430
  [sharpening]: https://photography.tutsplus.com/tutorials/what-is-image-sharpening--cms-26627
404
431
  [libvips]: http://libvips.github.io/libvips/
405
432
  [Why is libvips quick]: https://github.com/libvips/libvips/wiki/Why-is-libvips-quick
406
- [metadata]: /doc/metadata.md#readme
407
- [store_dimensions]: /doc/plugins/store_dimensions.md#readme
408
- [add_metadata]: /doc/plugins/add_metadata.md#readme
409
- [validation]: /doc/validation.md#readme
410
- [upload_endpoint]: /doc/plugins/upload_endpoint.md#readme
411
- [presign_endpoint]: /doc/plugins/presign_endpoint.md#readme
433
+ [metadata]: https://shrinerb.com/docs/metadata
434
+ [store_dimensions]: https://shrinerb.com/docs/plugins/store_dimensions
435
+ [add_metadata]: https://shrinerb.com/docs/plugins/add_metadata
436
+ [validation]: https://shrinerb.com/docs/validation
437
+ [upload_endpoint]: https://shrinerb.com/docs/plugins/upload_endpoint
438
+ [presign_endpoint]: https://shrinerb.com/docs/plugins/presign_endpoint
412
439
  [Uppy]: https://uppy.io
413
- [Uppy XHRUpload]: https://uppy.io/docs/xhrupload/
440
+ [Uppy XHRUpload]: https://uppy.io/docs/xhr-upload/
414
441
  [Uppy AwsS3]: https://uppy.io/docs/aws-s3/
415
442
  [Uppy Tus]: https://uppy.io/docs/tus/
443
+ [Uppy AwsS3Multipart]: https://uppy.io/docs/aws-s3-multipart/
444
+ [tus]: https://tus.io
416
445
  [Uppy StatusBar]: https://uppy.io/examples/statusbar/
417
446
  [Uppy Dashboard]: https://uppy.io/examples/dashboard/
418
- [backgrounding]: /doc/plugins/backgrounding.md#readme
419
- [backgrounding libraries]: https://github.com/shrinerb/shrine/wiki/Backgrounding-Libraries
420
- [Down streaming]: https://github.com/janko/down#streaming
421
- [Transloadit]: https://transloadit.com
422
- [tus]: https://tus.io
423
- [tus implementations]: https://tus.io/implementations.html
424
447
  [tus-ruby-server]: https://github.com/janko/tus-ruby-server
425
- [shrine-tus]: https://github.com/shrinerb/shrine-tus
426
- [ImageTragick]: https://imagetragick.com
427
448
  [uppy-s3_multipart]: https://github.com/janko/uppy-s3_multipart
428
- [OWASP]: https://www.owasp.org/index.php/Unrestricted_File_Upload
429
- [image_optim]: https://github.com/toy/image_optim
430
- [validation_helpers]: /doc/plugins/validation_helpers.md#readme
431
- [custom validations]: /doc/validation.md#custom-validations
432
- [Down max size]: https://github.com/janko/down#maximum-size
449
+ [backgrounding]: https://shrinerb.com/docs/plugins/backgrounding
450
+ [backgrounding libraries]: https://github.com/shrinerb/shrine/wiki/Backgrounding-Libraries
451
+ [Down streaming]: https://github.com/janko/down#streaming
452
+ [validation_helpers]: https://shrinerb.com/docs/plugins/validation_helpers
453
+ [derivatives]: https://shrinerb.com/docs/plugins/derivatives
454
+ [derivation_endpoint]: https://shrinerb.com/docs/plugins/derivation_endpoint
455
+ [libvips performance]: https://github.com/libvips/libvips/wiki/Speed-and-memory-use#results
456
+ [streamio-ffmpeg]: https://github.com/streamio/streamio-ffmpeg
457
+ [CarrierWave]: https://shrinerb.com/docs/carrierwave
458
+ [Paperclip]: https://shrinerb.com/docs/paperclip
459
+ [Refile]: https://shrinerb.com/docs/refile