shrine 3.0.0.rc → 3.0.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 (167) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -66
  3. data/README.md +39 -1061
  4. data/doc/advantages.md +151 -148
  5. data/doc/attacher.md +12 -30
  6. data/doc/carrierwave.md +150 -115
  7. data/doc/changing_derivatives.md +5 -11
  8. data/doc/changing_location.md +8 -5
  9. data/doc/changing_storage.md +5 -2
  10. data/doc/creating_persistence_plugins.md +9 -6
  11. data/doc/creating_plugins.md +42 -22
  12. data/doc/creating_storages.md +4 -1
  13. data/doc/design.md +7 -5
  14. data/doc/direct_s3.md +9 -4
  15. data/doc/external/articles.md +50 -0
  16. data/doc/external/extensions.md +46 -0
  17. data/doc/external/misc.md +17 -0
  18. data/doc/getting_started.md +1038 -0
  19. data/doc/metadata.md +5 -3
  20. data/doc/multiple_files.md +55 -29
  21. data/doc/paperclip.md +206 -163
  22. data/doc/plugins/activerecord.md +26 -6
  23. data/doc/plugins/add_metadata.md +4 -2
  24. data/doc/plugins/atomic_helpers.md +4 -2
  25. data/doc/plugins/backgrounding.md +83 -44
  26. data/doc/plugins/cached_attachment_data.md +4 -2
  27. data/doc/plugins/column.md +4 -2
  28. data/doc/plugins/data_uri.md +10 -6
  29. data/doc/plugins/default_storage.md +5 -3
  30. data/doc/plugins/default_url.md +4 -2
  31. data/doc/plugins/delete_raw.md +4 -2
  32. data/doc/plugins/derivation_endpoint.md +63 -39
  33. data/doc/plugins/derivatives.md +13 -50
  34. data/doc/plugins/determine_mime_type.md +6 -4
  35. data/doc/plugins/download_endpoint.md +6 -3
  36. data/doc/plugins/dynamic_storage.md +4 -2
  37. data/doc/plugins/entity.md +6 -4
  38. data/doc/plugins/form_assign.md +4 -2
  39. data/doc/plugins/included.md +4 -2
  40. data/doc/plugins/infer_extension.md +6 -4
  41. data/doc/plugins/instrumentation.md +5 -3
  42. data/doc/plugins/keep_files.md +9 -2
  43. data/doc/plugins/metadata_attributes.md +5 -3
  44. data/doc/plugins/mirroring.md +4 -2
  45. data/doc/plugins/model.md +6 -4
  46. data/doc/plugins/module_include.md +4 -2
  47. data/doc/plugins/multi_cache.md +4 -2
  48. data/doc/plugins/persistence.md +5 -3
  49. data/doc/plugins/presign_endpoint.md +6 -2
  50. data/doc/plugins/pretty_location.md +5 -3
  51. data/doc/plugins/processing.md +4 -2
  52. data/doc/plugins/rack_file.md +8 -2
  53. data/doc/plugins/rack_response.md +6 -2
  54. data/doc/plugins/recache.md +4 -2
  55. data/doc/plugins/refresh_metadata.md +5 -3
  56. data/doc/plugins/remote_url.md +26 -5
  57. data/doc/plugins/remove_attachment.md +4 -2
  58. data/doc/plugins/remove_invalid.md +10 -2
  59. data/doc/plugins/restore_cached_data.md +9 -3
  60. data/doc/plugins/sequel.md +26 -6
  61. data/doc/plugins/signature.md +6 -4
  62. data/doc/plugins/store_dimensions.md +6 -4
  63. data/doc/plugins/tempfile.md +4 -2
  64. data/doc/plugins/upload_endpoint.md +6 -2
  65. data/doc/plugins/upload_options.md +6 -4
  66. data/doc/plugins/url_options.md +4 -2
  67. data/doc/plugins/validation.md +7 -3
  68. data/doc/plugins/validation_helpers.md +13 -10
  69. data/doc/plugins/versions.md +4 -8
  70. data/doc/processing.md +27 -9
  71. data/doc/refile.md +119 -127
  72. data/doc/release_notes/1.0.0.md +4 -0
  73. data/doc/release_notes/1.1.0.md +4 -0
  74. data/doc/release_notes/1.2.0.md +4 -0
  75. data/doc/release_notes/1.3.0.md +4 -0
  76. data/doc/release_notes/1.4.0.md +4 -0
  77. data/doc/release_notes/1.4.1.md +4 -0
  78. data/doc/release_notes/1.4.2.md +4 -0
  79. data/doc/release_notes/2.0.0.md +4 -0
  80. data/doc/release_notes/2.0.1.md +4 -0
  81. data/doc/release_notes/2.1.0.md +4 -0
  82. data/doc/release_notes/2.1.1.md +4 -0
  83. data/doc/release_notes/2.10.0.md +4 -0
  84. data/doc/release_notes/2.10.1.md +4 -0
  85. data/doc/release_notes/2.11.0.md +4 -0
  86. data/doc/release_notes/2.12.0.md +4 -0
  87. data/doc/release_notes/2.13.0.md +4 -0
  88. data/doc/release_notes/2.14.0.md +5 -1
  89. data/doc/release_notes/2.15.0.md +10 -6
  90. data/doc/release_notes/2.16.0.md +4 -0
  91. data/doc/release_notes/2.17.0.md +4 -0
  92. data/doc/release_notes/2.18.0.md +4 -0
  93. data/doc/release_notes/2.19.0.md +7 -4
  94. data/doc/release_notes/2.2.0.md +4 -0
  95. data/doc/release_notes/2.3.0.md +4 -0
  96. data/doc/release_notes/2.3.1.md +4 -0
  97. data/doc/release_notes/2.4.0.md +4 -0
  98. data/doc/release_notes/2.4.1.md +4 -0
  99. data/doc/release_notes/2.5.0.md +4 -0
  100. data/doc/release_notes/2.6.0.md +4 -0
  101. data/doc/release_notes/2.6.1.md +4 -0
  102. data/doc/release_notes/2.7.0.md +4 -0
  103. data/doc/release_notes/2.8.0.md +4 -0
  104. data/doc/release_notes/2.9.0.md +4 -0
  105. data/doc/release_notes/3.0.0.md +120 -38
  106. data/doc/retrieving_uploads.md +4 -1
  107. data/doc/securing_uploads.md +4 -1
  108. data/doc/storage/file_system.md +12 -4
  109. data/doc/storage/s3.md +4 -2
  110. data/doc/testing.md +27 -41
  111. data/doc/upgrading_to_3.md +105 -26
  112. data/doc/validation.md +8 -6
  113. data/lib/shrine/attacher.rb +2 -2
  114. data/lib/shrine/attachment.rb +7 -10
  115. data/lib/shrine/plugins/activerecord.rb +10 -10
  116. data/lib/shrine/plugins/add_metadata.rb +1 -3
  117. data/lib/shrine/plugins/atomic_helpers.rb +6 -8
  118. data/lib/shrine/plugins/backgrounding.rb +4 -6
  119. data/lib/shrine/plugins/cached_attachment_data.rb +1 -3
  120. data/lib/shrine/plugins/column.rb +2 -4
  121. data/lib/shrine/plugins/data_uri.rb +1 -3
  122. data/lib/shrine/plugins/default_storage.rb +1 -3
  123. data/lib/shrine/plugins/default_url.rb +1 -3
  124. data/lib/shrine/plugins/delete_raw.rb +1 -3
  125. data/lib/shrine/plugins/derivation_endpoint.rb +3 -4
  126. data/lib/shrine/plugins/derivatives.rb +2 -4
  127. data/lib/shrine/plugins/determine_mime_type.rb +1 -3
  128. data/lib/shrine/plugins/download_endpoint.rb +1 -3
  129. data/lib/shrine/plugins/dynamic_storage.rb +1 -3
  130. data/lib/shrine/plugins/entity.rb +25 -9
  131. data/lib/shrine/plugins/form_assign.rb +1 -3
  132. data/lib/shrine/plugins/included.rb +1 -3
  133. data/lib/shrine/plugins/infer_extension.rb +1 -3
  134. data/lib/shrine/plugins/instrumentation.rb +1 -3
  135. data/lib/shrine/plugins/keep_files.rb +1 -3
  136. data/lib/shrine/plugins/metadata_attributes.rb +1 -3
  137. data/lib/shrine/plugins/mirroring.rb +2 -1
  138. data/lib/shrine/plugins/model.rb +2 -4
  139. data/lib/shrine/plugins/module_include.rb +1 -3
  140. data/lib/shrine/plugins/multi_cache.rb +3 -3
  141. data/lib/shrine/plugins/presign_endpoint.rb +1 -3
  142. data/lib/shrine/plugins/pretty_location.rb +1 -3
  143. data/lib/shrine/plugins/processing.rb +1 -3
  144. data/lib/shrine/plugins/rack_file.rb +1 -3
  145. data/lib/shrine/plugins/rack_response.rb +1 -3
  146. data/lib/shrine/plugins/recache.rb +1 -3
  147. data/lib/shrine/plugins/refresh_metadata.rb +1 -3
  148. data/lib/shrine/plugins/remote_url.rb +1 -3
  149. data/lib/shrine/plugins/remove_attachment.rb +1 -3
  150. data/lib/shrine/plugins/remove_invalid.rb +1 -3
  151. data/lib/shrine/plugins/restore_cached_data.rb +1 -3
  152. data/lib/shrine/plugins/sequel.rb +10 -12
  153. data/lib/shrine/plugins/signature.rb +1 -3
  154. data/lib/shrine/plugins/store_dimensions.rb +1 -3
  155. data/lib/shrine/plugins/tempfile.rb +1 -3
  156. data/lib/shrine/plugins/upload_endpoint.rb +1 -3
  157. data/lib/shrine/plugins/upload_options.rb +1 -3
  158. data/lib/shrine/plugins/url_options.rb +1 -3
  159. data/lib/shrine/plugins/validation.rb +1 -3
  160. data/lib/shrine/plugins/validation_helpers.rb +1 -3
  161. data/lib/shrine/plugins/versions.rb +1 -3
  162. data/lib/shrine/storage/file_system.rb +1 -1
  163. data/lib/shrine/storage/linter.rb +1 -1
  164. data/lib/shrine/storage/memory.rb +2 -1
  165. data/lib/shrine/storage/s3.rb +3 -3
  166. data/lib/shrine/version.rb +1 -1
  167. metadata +8 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 99f93b258aea07a618b1f35bf08f6b8394dea5d0c6301ed2f96cc72ec12371a4
4
- data.tar.gz: ac05d107593d54ce6260d5159c8484f03c24ea0e9df4026b615f79fd5bc04a92
3
+ metadata.gz: 6c4cf7d403c331ddb66dc01ea8e9210a827065c08fd206d89be8aa1dac24775b
4
+ data.tar.gz: 9a1b304b4fd4c9640f1bda7904c6402490af9456c896d69bd815fcf8f34ea1e2
5
5
  SHA512:
6
- metadata.gz: 640d3a4f07816d7f4a20d385ac1249cd5cdbc46f320a6b8d377338b4ceaa724f5ce1e84782391fd2af6be1419aecd61474a36f03bcffb41c6600f6d999b0be0f
7
- data.tar.gz: fa93fe25894f9a1125a852e438ca5ecd20518e206418d464afbcf59d8576959e373a3084ff133ef2c8aff062e1a4ba93d64841e19cc14fdc256f87d8871dcb4c
6
+ metadata.gz: 0eeb00bf6fa625a51f993bda58e0d016b6c85d0831f9be4ce6f914c720054f03684f83617b963f894b51216b96f9ad42eddbcfcf4f717521123d474397c8e3d6
7
+ data.tar.gz: dd07bd03ea16adf721fdbd8568aa33f1f47a36a32ac5c82537721492c12a21e1b169ebbf8b2872fcb4c938f68bbbde9a1aff6b447fee0a50663898c105e2c62e
@@ -1,4 +1,8 @@
1
- ## 3.0.0.rc (2019-09-28)
1
+ ## 3.0.0 (14-10-2019)
2
+
3
+ * `derivation_endpoint` – Pass `action: :derivation` when uploading derivation results (@janko)
4
+
5
+ * `core` – Add `Shrine::Attachment[]` shorthand for `Shrine::Attachment.new` (@janko)
2
6
 
3
7
  * `core` – Add `Storage#delete_prefixed` method for deleting all files in specified directory (@jrochkind)
4
8
 
@@ -12,73 +16,29 @@
12
16
 
13
17
  * Update `down` dependency to `~> 5.0` (@janko)
14
18
 
15
- ## 3.0.0.beta3 (2019-09-25)
16
-
17
19
  * `multi_cache` – Add new plugin for whitelisting additional temporary storages (@janko, @jrochkind)
18
20
 
19
- * `s3` – Allow uploading files larger than 50 GB (@janko)
20
-
21
21
  * `sequel` – Extract callback code into attacher methods that can be overridden (@janko)
22
22
 
23
23
  * `activerecord` – Extract callback code into attacher methods that can be overridden (@janko)
24
24
 
25
- * `derivatives` – Ensure binary mode for `File` and `Tempfile` objects (@janko)
26
-
27
- * `derivatives` – Ensure refreshed file descriptor for `Tempfile` objects (@janko)
28
-
29
25
  * `derivation_endpoint` – Stop re-opening `File` objects returned in derivation result (@janko)
30
26
 
31
27
  * `derivation_endpoint` – Allow only `File` or `Tempfile` object as derivation result (@janko)
32
28
 
33
- * `derivatives` – Add support for default processors (@janko)
34
-
35
- * `remote_url` – Convert only `Down::Error` and `DownloadError` exceptions into validation errors (@janko)
36
-
37
- * `remote_url` – Bring back `Attacher#remote_url=` and `Attacher#remote_url` (@janko)
38
-
39
- * `data_uri` – Bring back `Attacher#data_uri=` and `Attacher#data_uri` (@janko)
40
-
41
- * `mirroring` – Allow skipping mirroring by passing `mirror: false` to `#upload` and `#delete` (@janko)
42
-
43
- * `mirroring` – Rename `Shrine.mirror_(upload|delete)` to `Shrine.mirror_(upload|delete)_block` (@janko)
44
-
45
- * `mirroring` – Add `UploadedFile#mirror_(upload|delete)_background` (@janko)
46
-
47
- * `attacher_options` – Remove plugin (@janko)
48
-
49
- * `backgrounding` – Move `Attacher#destroy(background: true)` to `Attacher#destroy_background` (@janko)
50
-
51
- * `backgrounding` – Move `Attacher#promote(background: true)` to `Attacher#promote_background` (@janko)
52
-
53
29
  * `download_endpoint` – Add `Shrine.download_response` for calling in controller (@janko)
54
30
 
55
31
  * `core` – Fetch storage object lazily in `Shrine` instance (@janko)
56
32
 
57
- ## 3.0.0.beta2 (2019-09-11)
58
-
59
- * `column` – Allow `Attacher#load_column` to receive a hash (@janko)
60
-
61
- * `activerecord` – Fix integration not working with JSON columns (@janko)
62
-
63
33
  * `mirroring` – Add new plugin for replicating uploads and deletes to other storages (@janko)
64
34
 
65
- * `model` – Change disabling model attachment behaviour from `type: :entity` to `model: false` (@janko)
66
-
67
35
  * `sequel` – Rename `:callbacks` option to `:hooks` (@janko)
68
36
 
69
- * `derivatives` – Auto-download `UploadedFile` objects passed to `Attacher#process_derivatives` (@janko)
70
-
71
- ## 3.0.0.beta (2019-08-29)
72
-
73
- * `atomic_helpers` – Rename `:data` argument to `:file` in `Attacher.retrieve` (@janko)
74
-
75
- * `atomic_helpers` – Add `Attacher#file_data` which returns only main file data without metadata (@janko)
76
-
77
37
  * `model` – Add `Attacher#set_model` for setting model without loading attachment (@janko)
78
38
 
79
39
  * `entity` – Add `Attacher#set_entity` for setting entity without loading attachment (@janko)
80
40
 
81
- * `core` – Define `#<name>_attacher` class method when including `Shrine::Attachment` (@janko)
41
+ * `entity` – Define `#<name>_attacher` class method when including `Shrine::Attachment` (@janko)
82
42
 
83
43
  * `derivation_endpoint` – Send only `:derivation` in the instrumentation event payload (@janko)
84
44
 
@@ -88,7 +48,7 @@
88
48
 
89
49
  * `default_storage` – Evaluate storage block in context of `Attacher` instance (@janko)
90
50
 
91
- * Simplify persistence interface (@janko)
51
+ * Unify persistence plugin interface (@janko)
92
52
 
93
53
  * `upload_options` – Keep `Shrine#_upload` private (@janko)
94
54
 
@@ -96,13 +56,7 @@
96
56
 
97
57
  * `model` – Add `#<name>_changed?` method to attachment module (@janko)
98
58
 
99
- * `core` – Make it easier for plugins to define entity and model attachment methods (@janko)
100
-
101
- * `derivatives` – Add `#<name>_derivatives!` module method which delegates to `#create_derivatives` (@janko)
102
-
103
- * `derivatives` – Change `#create_derivatives` to forward all arguments to `#process_derivatives` (@janko)
104
-
105
- ## 3.0.0.alpha (2019-08-19)
59
+ * Make it easier for plugins to define entity and model attachment methods (@janko)
106
60
 
107
61
  * `form_assign` – Add new plugin for assigning attachment from form params without a form object (@janko)
108
62
 
@@ -122,8 +76,6 @@
122
76
 
123
77
  * `download_endpoint` – Remove extra `Storage#exists?` check (@janko)
124
78
 
125
- * `derivation_endpoint` – Remove support for derivative being deleted during upload (@janko)
126
-
127
79
  * `derivation_endpoint` – Add `Derivation#opened` for retrieving an opened derivation result (@janko)
128
80
 
129
81
  * `derivation_endpoint` – Remove extra `Storage#exists?` check when `:upload` is enabled but not `:upload_redirect` (@janko)
@@ -198,8 +150,6 @@
198
150
 
199
151
  * `restore_cached_data` – Forward options passed to `Attacher#attach_cached` to metadata extraction (@janko)
200
152
 
201
- * `attacher_options` – Add new plugin for setting default attacher operation options (@janko)
202
-
203
153
  * `validation` – Allow skipping validations on attaching by passing `validate: false` (@janko)
204
154
 
205
155
  * `validation` – Add `:validate` option to `Attacher#assign` or `Attacher#attach` for passing options to validation block (@janko)
@@ -212,8 +162,6 @@
212
162
 
213
163
  * `remote_url` – Require custom downloaders to raise `Shrine::Plugins::RemoteUrl::DownloadError` for conversion into a validation error (@janko)
214
164
 
215
- * `remote_url` – Remove `Attacher#remote_url=` and `Attacher#remote_url` (@janko)
216
-
217
165
  * `infer_extension` – Fix compatibility with the `pretty_location` plugin (@janko)
218
166
 
219
167
  * `presign_endpoint` – Remove deprecated `Shrine::Plugins::PresignEndpoint::App` constant (@janko)
@@ -224,17 +172,13 @@
224
172
 
225
173
  * `default_url_options` – Allow overriding passed URL options by deleting them inside the block (@janko)
226
174
 
227
- * `data_uri` – Remove `Attacher#data_uri=` and `Attacher#data_uri` methods (@janko)
228
-
229
175
  * `cached_attachment_data` – Rename `Attacher#read_cached` to `Attacher#cached_data` (@janko)
230
176
 
231
177
  * `sequel` – Add `Attacher#atomic_promote` and `Attacher#atomic_persist` (@janko)
232
178
 
233
- * `sequel` – Remove adding persistence to `Attacher#promote` (@janko)
234
-
235
- * `activerecord` – Add `Attacher#atomic_promote` and `Attacher#atomic_persist` (@janko)
179
+ * `sequel` – Remove persistence from `Attacher#promote` (@janko)
236
180
 
237
- * `activerecord` – Remove adding persistence to `Attacher#promote` (@janko)
181
+ * `activerecord` – Remove persistence from `Attacher#promote` (@janko)
238
182
 
239
183
  * `atomic_helpers` – Add new plugin with helper methods for atomic promotion and persistence (@janko)
240
184
 
data/README.md CHANGED
@@ -1,972 +1,27 @@
1
1
  # [Shrine]
2
2
 
3
- Shrine is a toolkit for file attachments in Ruby applications. Some highlights:
3
+ Shrine is a toolkit for handling file attachments in Ruby applications. Some highlights:
4
4
 
5
5
  * **Modular design** – the [plugin system] allows you to load only the functionality you need
6
6
  * **Memory friendly** – streaming uploads and [downloads][Retrieving Uploads] make it work great with large files
7
- * **Cloud storage** – store files on [disk][FileSystem], [AWS S3][S3], [Google Cloud][GCS], [Cloudinary] and [others][external]
8
- * **Persistence integrations** – works with [Sequel][sequel plugin], [ActiveRecord][activerecord plugin], [ROM][rom plugin], [Hanami::Model][hanami plugin] and [Mongoid][mongoid plugin]
9
- * **Flexible processing** – generate thumbnails [up front] or [on-the-fly] using [ImageMagick][ImageProcessing::MiniMagick] or [libvips][ImageProcessing::Vips]
7
+ * **Cloud storage** – store files on [disk][FileSystem], [AWS S3][S3], [Google Cloud][GCS], [Cloudinary] and others
8
+ * **Persistence integrations** – works with [Sequel], [ActiveRecord], [ROM], [Hanami] and [Mongoid] and others
9
+ * **Flexible processing** – generate thumbnails [up front] or [on-the-fly] using [ImageMagick] or [libvips]
10
10
  * **Metadata validation** – [validate files][validation] based on [extracted metadata][metadata]
11
11
  * **Direct uploads** – upload asynchronously [to your app][simple upload] or [to the cloud][presigned upload] using [Uppy]
12
12
  * **Resumable uploads** – make large file uploads [resumable][resumable upload] on [S3][uppy-s3_multipart] or [tus][tus-ruby-server]
13
13
  * **Background jobs** – built-in support for [background processing][backgrounding] that supports [any backgrounding library][Backgrounding Libraries]
14
14
 
15
- If you're curious how it compares to other file attachment libraries, see the [Advantages of Shrine].
15
+ Please follow along with the **[Getting Started guide]**.
16
16
 
17
- ## Resources
17
+ ## Links
18
18
 
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 | [discourse.shrinerb.com](https://discourse.shrinerb.com) |
27
-
28
- ## Contents
29
-
30
- * [Quick start](#quick-start)
31
- * [Storage](#storage)
32
- * [Uploader](#uploader)
33
- - [Uploading](#uploading)
34
- - [IO abstraction](#io-abstraction)
35
- * [Uploaded file](#uploaded-file)
36
- * [Attachment](#attachment)
37
- - [Temporary storage](#temporary-storage)
38
- * [Attacher](#attacher)
39
- * [Plugin system](#plugin-system)
40
- * [Metadata](#metadata)
41
- * [MIME type](#mime-type)
42
- * [Other metadata](#other-metadata)
43
- * [Processing](#processing)
44
- * [Processing up front](#processing-up-front)
45
- * [Processing on-the-fly](#processing-on-the-fly)
46
- * [Validation](#validation)
47
- * [Location](#location)
48
- * [Direct uploads](#direct-uploads)
49
- - [Simple direct upload](#simple-direct-upload)
50
- - [Presigned direct upload](#presigned-direct-upload)
51
- - [Resumable direct upload](#resumable-direct-upload)
52
- * [Backgrounding](#backgrounding)
53
- * [Clearing cache](#clearing-cache)
54
- * [Logging](#logging)
55
-
56
- ## Quick start
57
-
58
- Add Shrine to the Gemfile and write an initializer which sets up the storage and
59
- loads the ORM plugin:
60
-
61
- ```rb
62
- # Gemfile
63
- gem "shrine", "~> 2.0"
64
- ```
65
-
66
- ```rb
67
- require "shrine"
68
- require "shrine/storage/file_system"
69
-
70
- Shrine.storages = {
71
- cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"), # temporary
72
- store: Shrine::Storage::FileSystem.new("public", prefix: "uploads"), # permanent
73
- }
74
-
75
- Shrine.plugin :sequel # or :activerecord
76
- Shrine.plugin :cached_attachment_data # for retaining the cached file across form redisplays
77
- Shrine.plugin :restore_cached_data # re-extract metadata when attaching a cached file
78
- Shrine.plugin :rack_file # for non-Rails apps
79
- ```
80
-
81
- Next decide how you will name the attachment attribute on your model, and run a
82
- migration that adds an `<attachment>_data` text or JSON column, which Shrine
83
- will use to store all information about the attachment:
84
-
85
- ```rb
86
- Sequel.migration do
87
- change do
88
- add_column :photos, :image_data, :text # or :jsonb
89
- end
90
- end
91
- ```
92
-
93
- In Rails with Active Record the migration would look similar:
94
-
95
- ```sh
96
- $ rails generate migration add_image_data_to_photos image_data:text
97
- ```
98
- ```rb
99
- class AddImageDataToPhotos < ActiveRecord::Migration
100
- def change
101
- add_column :photos, :image_data, :text # or :jsonb
102
- end
103
- end
104
- ```
105
-
106
- Now you can create an uploader class for the type of files you want to upload,
107
- and add a virtual attribute for handling attachments using this uploader to
108
- your model. If you do not care about adding plugins or additional processing,
109
- you can use `Shrine::Attachment`.
110
-
111
- ```rb
112
- class ImageUploader < Shrine
113
- # plugins and uploading logic
114
- end
115
- ```
116
-
117
- ```rb
118
- class Photo < Sequel::Model # ActiveRecord::Base
119
- include ImageUploader::Attachment(:image) # adds an `image` virtual attribute
120
- end
121
- ```
122
-
123
- Let's now add the form fields which will use this virtual attribute (NOT the
124
- `<attachment>_data` column attribute). We need (1) a file field for choosing
125
- files, and (2) a hidden field for retaining the uploaded file in case of
126
- validation errors and for potential [direct uploads].
127
-
128
- ```rb
129
- # with Rails form builder:
130
- form_for @photo do |f|
131
- f.hidden_field :image, value: @photo.cached_image_data
132
- f.file_field :image
133
- f.submit
134
- end
135
- ```
136
- ```rb
137
- # with Simple Form:
138
- simple_form_for @photo do |f|
139
- f.input :image, as: :hidden, input_html: { value: @photo.cached_image_data }
140
- f.input :image, as: :file
141
- f.button :submit
142
- end
143
- ```
144
- ```rb
145
- # with Forme:
146
- form @photo, action: "/photos", enctype: "multipart/form-data" do |f|
147
- f.input :image, type: :hidden, value: @photo.cached_image_data
148
- f.input :image, type: :file
149
- f.button "Create"
150
- end
151
- ```
152
-
153
- Note that the file field needs to go *after* the hidden field, so that
154
- selecting a new file can always override the cached file in the hidden field.
155
- Also notice the `enctype="multipart/form-data"` HTML attribute, which is
156
- required for submitting files through the form (the Rails form builder
157
- will automatically generate this for you).
158
-
159
- When the form is submitted, in your router/controller you can assign the file
160
- from request params to the attachment attribute on the model.
161
-
162
- ```rb
163
- # In Rails:
164
- class PhotosController < ApplicationController
165
- def create
166
- Photo.create(photo_params)
167
- # ...
168
- end
169
-
170
- private
171
-
172
- def photo_params
173
- params.require(:photo).permit(:image)
174
- end
175
- end
176
- ```
177
- ```rb
178
- # In Sinatra:
179
- post "/photos" do
180
- Photo.create(params[:photo])
181
- # ...
182
- end
183
- ```
184
-
185
- Once a file is uploaded and attached to the record, you can retrieve a URL to
186
- the uploaded file with `#<attachment>_url` and display it on the page:
187
-
188
- ```erb
189
- <!-- In Rails: -->
190
- <%= image_tag @photo.image_url %>
191
- ```
192
- ```erb
193
- <!-- In HTML: -->
194
- <img src="<%= @photo.image_url %>" />
195
- ```
196
-
197
- ## Storage
198
-
199
- A "storage" in Shrine is an object that encapsulates communication with a
200
- specific storage service, by implementing a common public interface. Storage
201
- instances are registered under an identifier in `Shrine.storages`, so that they
202
- can later be used by [uploaders][uploader].
203
-
204
- Previously we've shown the [FileSystem] storage which saves files to disk, but
205
- Shrine also ships with [S3] storage which stores files on [AWS S3] (or any
206
- S3-compatible service such as [DigitalOcean Spaces] or [MinIO]).
207
-
208
- ```rb
209
- # Gemfile
210
- gem "aws-sdk-s3", "~> 1.14" # for AWS S3 storage
211
- ```
212
- ```rb
213
- require "shrine/storage/s3"
214
-
215
- s3_options = {
216
- bucket: "<YOUR BUCKET>", # required
217
- access_key_id: "<YOUR ACCESS KEY ID>",
218
- secret_access_key: "<YOUR SECRET ACCESS KEY>",
219
- region: "<YOUR REGION>",
220
- }
221
-
222
- Shrine.storages = {
223
- cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
224
- store: Shrine::Storage::S3.new(**s3_options),
225
- }
226
- ```
227
-
228
- The above example sets up S3 for both temporary and permanent storage, which is
229
- suitable for [direct uploads][Direct Uploads to S3]. The `:cache` and
230
- `:store` names are special only in terms that the [attacher] will automatically
231
- pick them up, you can also register more storage objects under different names.
232
-
233
- See the [FileSystem] and [S3] storage docs for more details. There are [many
234
- more Shrine storages][external] provided by external gems, and you can also
235
- [create your own storage][Creating Storages].
236
-
237
- ## Uploader
238
-
239
- Uploaders are subclasses of `Shrine`, and they wrap the actual upload to the
240
- storage. They perform common tasks around upload that aren't related to a
241
- particular storage.
242
-
243
- ```rb
244
- class MyUploader < Shrine
245
- # image attachment logic
246
- end
247
- ```
248
-
249
- It's common to create an uploader for each type of file that you want to handle
250
- (`ImageUploader`, `VideoUploader`, `AudioUploader` etc), but really you can
251
- organize them in any way you like.
252
-
253
- ### Uploading
254
-
255
- The main method of the uploader is `Shrine.upload`, which takes an [IO-like
256
- object][io abstraction] and a storage identifier on the input, and returns a
257
- representation of the [uploaded file] on the output.
258
-
259
- ```rb
260
- MyUploader.upload(file, :store) #=> #<Shrine::UploadedFile>
261
- ```
262
-
263
- Internally this instantiates the uploader with the storage and calls
264
- `Shrine#upload`:
265
-
266
- ```rb
267
- uploader = MyUploader.new(:store)
268
- uploader.upload(file) #=> #<Shrine::UploadedFile>
269
- ```
270
-
271
- Some of the tasks performed by `#upload` include:
272
-
273
- * extracting [metadata]
274
- * generating [location]
275
- * uploading (this is where the [storage] is called)
276
- * closing the uploaded file
277
-
278
- The second argument is a "context" hash which is forwarded to places like
279
- metadata extraction and location generation, but it has a few special options:
280
-
281
- ```rb
282
- uploader.upload(io, metadata: { "foo" => "bar" }) # add metadata
283
- uploader.upload(io, location: "path/to/file") # specify custom location
284
- uploader.upload(io, upload_options: { acl: "public-read" }) # add options to Storage#upload
285
- ```
286
-
287
- ### IO abstraction
288
-
289
- Shrine is able to upload any IO-like object that implement methods [`#read`],
290
- [`#rewind`], [`#eof?`] and [`#close`] whose behaviour matches the [`IO`] class.
291
- This includes built-in IO and IO-like objects like File, Tempfile and StringIO.
292
-
293
- When a file is uploaded to a Rails app, in request params it will be
294
- represented by an `ActionDispatch::Http::UploadedFile` object, which is also an
295
- IO-like object accepted by Shrine. In other Rack applications the uploaded file
296
- will be represented as a Hash, but it can be converted into an IO-like object
297
- with the [`rack_file`][rack_file plugin] plugin.
298
-
299
- Here are some examples of various IO-like objects that can be uploaded:
300
-
301
- ```rb
302
- uploader.upload File.open("/path/to/file", binmode: true) # upload from disk
303
- uploader.upload StringIO.new("file content") # upload from memory
304
- uploader.upload ActionDispatch::Http::UploadedFile.new(...) # upload from Rails controller
305
- uploader.upload Shrine.rack_file({ tempfile: tempfile }) # upload from Rack controller
306
- uploader.upload Rack::Test::UploadedFile.new(...) # upload from rack-test
307
- uploader.upload Down.open("https://example.org/file") # upload from internet
308
- uploader.upload Shrine::UploadedFile.new(...) # upload from Shrine storage
309
- ```
310
-
311
- ## Uploaded file
312
-
313
- The `Shrine::UploadedFile` object represents the file that was uploaded to a
314
- storage, and it's what's returned from `Shrine#upload` or when retrieving a
315
- record [attachment]. It contains the following information:
316
-
317
- | Key | Description |
318
- | :------- | :---------- |
319
- | `id` | location of the file on the storage |
320
- | `storage` | identifier of the storage the file was uploaded to |
321
- | `metadata` | file [metadata] that was extracted before upload |
322
-
323
- ```rb
324
- uploaded_file = uploader.upload(file)
325
- uploaded_file.data #=> {"id"=>"949sdjg834.jpg","storage"=>"store","metadata"=>{...}}
326
-
327
- uploaded_file.id #=> "949sdjg834.jpg"
328
- uploaded_file.storage #=> #<Shrine::Storage::S3>
329
- uploaded_file.metadata #=> {...}
330
- ```
331
-
332
- It comes with many convenient methods that delegate to the storage:
333
-
334
- ```rb
335
- uploaded_file.url #=> "https://my-bucket.s3.amazonaws.com/949sdjg834.jpg"
336
- uploaded_file.open { |io| ... } # opens the uploaded file stream
337
- uploaded_file.download { |file| ... } # downloads the uploaded file to disk
338
- uploaded_file.stream(destination) # streams uploaded content into a writable destination
339
- uploaded_file.exists? #=> true
340
- uploaded_file.delete # deletes the uploaded file from the storage
341
- ```
342
-
343
- It also implements the IO-like interface that conforms to Shrine's [IO
344
- abstraction][io abstraction], which allows it to be uploaded again to other
345
- storages.
346
-
347
- ```rb
348
- uploaded_file.read # returns content of the uploaded file
349
- uploaded_file.eof? # returns true if the whole IO was read
350
- uploaded_file.rewind # rewinds the IO
351
- uploaded_file.close # closes the IO
352
- ```
353
-
354
- For more details, see the [Retrieving Uploads] guide and
355
- [`Shrine::UploadedFile`] API docs.
356
-
357
- ## Attachment
358
-
359
- Storage objects, uploaders, and uploaded file objects are Shrine's foundational
360
- components. To help you actually attach uploaded files to database records in
361
- your application, Shrine comes with a high-level attachment interface built on
362
- top of these components.
363
-
364
- There are plugins for hooking into most database libraries, and in case of
365
- ActiveRecord and Sequel the plugin will automatically tie the attached files to
366
- records' lifecycles. But you can also use Shrine just with plain old Ruby
367
- objects.
368
-
369
- ```rb
370
- Shrine.plugin :sequel # :activerecord
371
- ```
372
-
373
- ```rb
374
- class Photo < Sequel::Model # ActiveRecord::Base
375
- include ImageUploader::Attachment.new(:image) #
376
- include ImageUploader::Attachment(:image) # these are all equivalent
377
- include ImageUploader[:image] #
378
- end
379
- ```
380
-
381
- You can choose whichever of these syntaxes you prefer. Either of these
382
- will create a `Shrine::Attachment` module with attachment methods for the
383
- specified attribute, which then get added to your model when you include it:
384
-
385
- | Method | Description |
386
- | :----- | :---------- |
387
- | `#image=` | uploads the file to temporary storage and serializes the result into `image_data` |
388
- | `#image` | returns [`Shrine::UploadedFile`][uploaded file] instantiated from `image_data` |
389
- | `#image_url` | calls `url` on the attachment if it's present, otherwise returns nil |
390
- | `#image_attacher` | returns instance of [`Shrine::Attacher`][attacher] which handles the attaching |
391
-
392
- The ORM plugin that we loaded adds appropriate callbacks. For example, saving
393
- the record uploads the attachment to permanent storage, while deleting the
394
- record deletes the attachment.
395
-
396
- ```rb
397
- # no file is attached
398
- photo.image #=> nil
399
-
400
- # the assigned file is cached to temporary storage and written to `image_data` column
401
- photo.image = File.open("waterfall.jpg")
402
- photo.image #=> #<Shrine::UploadedFile @data={...}>
403
- photo.image_url #=> "/uploads/cache/0sdfllasfi842.jpg"
404
- photo.image_data #=> '{"id":"0sdfllasfi842.jpg","storage":"cache","metadata":{...}}'
405
-
406
- # the cached file is promoted to permanent storage and saved to `image_data` column
407
- photo.save
408
- photo.image #=> #<Shrine::UploadedFile @data={...}>
409
- photo.image_url #=> "/uploads/store/l02kladf8jlda.jpg"
410
- photo.image_data #=> '{"id":"l02kladf8jlda.jpg","storage":"store","metadata":{...}}'
411
-
412
- # the attached file is deleted with the record
413
- photo.destroy
414
- photo.image.exists? #=> false
415
- ```
416
-
417
- If there is already a file attached and a new file is attached, the previous
418
- attachment will get deleted when the record gets saved.
419
-
420
- ```rb
421
- photo.update(image: new_file) # changes the attachment and deletes previous
422
- photo.update(image: nil) # removes the attachment and deletes previous
423
- ```
424
-
425
- ### Temporary storage
426
-
427
- Shrine uses temporary storage to enable retaining uploaded files across form
428
- redisplays and for [direct uploads](#direct-uploads), but you can disable this
429
- behaviour and have files go straight to permanent storage:
430
-
431
- ```rb
432
- Shrine.plugin :model, cache: false
433
- ```
434
- ```rb
435
- photo.image = File.open("waterfall.jpg")
436
- photo.image.storage_key #=> :store
437
- ```
438
-
439
- ## Attacher
440
-
441
- The model attachment attributes and callbacks added by `Shrine::Attachment`
442
- just delegate the behaviour to their underlying `Shrine::Attacher` object.
443
-
444
- ```rb
445
- photo.image_attacher #=> #<Shrine::Attacher>
446
- ```
447
-
448
- The `Shrine::Attacher` object can be instantiated and used directly:
449
-
450
- ```rb
451
- attacher = ImageUploader::Attacher.from_model(photo, :image)
452
-
453
- attacher.assign(file) # equivalent to `photo.image = file`
454
- attacher.file # equivalent to `photo.image`
455
- attacher.url # equivalent to `photo.image_url`
456
- ```
457
-
458
- The attacher is what drives attaching files to model instances; you can use it
459
- as a more explicit alternative to models' attachment interface, or simply when
460
- you need something that's not available through the attachment methods.
461
-
462
- You can do things such as change the temporary and permanent storage the
463
- attacher uses, or upload files directly to permanent storage. See the [Using
464
- Attacher] guide for more details.
465
-
466
- ## Plugin system
467
-
468
- By default Shrine comes with a small core which provides only the essential
469
- functionality. All additional features are available via [plugins], which also
470
- ship with Shrine. This way you can choose exactly what and how much Shrine does
471
- for you, and you load the code only for features that you use.
472
-
473
- ```rb
474
- Shrine.plugin :instrumentation # adds instrumentation
475
- ```
476
-
477
- Plugins add behaviour by extending Shrine core classes via module inclusion, and
478
- many of them also accept configuration options. The plugin system respects
479
- inheritance, so you can choose to load a plugin globally or per uploader.
480
-
481
- ```rb
482
- class ImageUploader < Shrine
483
- plugin :store_dimensions # extract image dimensions only for this uploader and its descendants
484
- end
485
- ```
486
-
487
- If you want to extend Shrine functionality with custom behaviour, you can also
488
- [create your own plugin][Creating Plugins].
489
-
490
- ## Metadata
491
-
492
- Shrine automatically extracts some basic file metadata and saves them to the
493
- `Shrine::UploadedFile`. You can access them through the `#metadata` hash or via
494
- metadata methods:
495
-
496
- ```rb
497
- uploaded_file.metadata #=>
498
- # {
499
- # "filename" => "matrix.mp4",
500
- # "mime_type" => "video/mp4",
501
- # "size" => 345993,
502
- # }
503
-
504
- uploaded_file.original_filename #=> "matrix.mp4"
505
- uploaded_file.extension #=> "mp4"
506
- uploaded_file.mime_type #=> "video/mp4"
507
- uploaded_file.size #=> 345993
508
- ```
509
-
510
- ### MIME type
511
-
512
- By default, `mime_type` metadata will be set from the `#content_type` attribute
513
- of the uploaded file (if it exists), which is generally not secure and will
514
- trigger a warning. You can load the [`determine_mime_type`][determine_mime_type
515
- plugin] plugin to have MIME type extracted from file *content* instead.
516
-
517
- ```rb
518
- # Gemfile
519
- gem "marcel", "~> 0.3"
520
- ```
521
- ```rb
522
- Shrine.plugin :determine_mime_type, analyzer: :marcel
523
- ```
524
- ```rb
525
- photo = Photo.create(image: StringIO.new("<?php ... ?>"))
526
- photo.image.mime_type #=> "application/x-php"
527
- ```
528
-
529
- ### Other metadata
530
-
531
- In addition to basic metadata, you can also extract [image
532
- dimensions][store_dimensions plugin], calculate [signatures][signature plugin],
533
- and in general extract any [custom metadata][add_metadata plugin]. Check out
534
- the [Extracting Metadata] guide for more details.
535
-
536
- ## Processing
537
-
538
- Shrine allows you to process attached files up front or on-the-fly. For
539
- example, if your app is accepting image uploads, you can generate a predefined
540
- set of of thumbnails when the image is attached to a record, or you can have
541
- thumbnails generated dynamically as they're needed.
542
-
543
- For image processing, it's recommended to use the **[ImageProcessing]** gem,
544
- which is a high-level wrapper for processing with [ImageMagick] (via
545
- [MiniMagick]) or [libvips] (via [ruby-vips]).
546
-
547
- ```sh
548
- $ brew install imagemagick vips
549
- ```
550
-
551
- ### Processing up front
552
-
553
- You can use the [`derivatives`][derivatives plugin] plugin to generate a set of
554
- pre-defined processed files:
555
-
556
- ```rb
557
- # Gemfile
558
- gem "image_processing", "~> 1.8"
559
- ```
560
- ```rb
561
- Shrine.plugin :derivatives
562
- ```
563
- ```rb
564
- require "image_processing/mini_magick"
565
-
566
- class ImageUploader < Shrine
567
- Attacher.derivatives_processor do |original|
568
- magick = ImageProcessing::MiniMagick.source(original)
569
-
570
- {
571
- large: magick.resize_to_limit!(800, 800),
572
- medium: magick.resize_to_limit!(500, 500),
573
- small: magick.resize_to_limit!(300, 300),
574
- }
575
- end
576
- end
577
- ```
578
- ```rb
579
- photo = Photo.new(image: file)
580
- photo.image_derivatives! # calls derivatives processor and uploads results
581
- photo.save
582
- ```
583
-
584
- If you're allowing the attached file to be updated later on, in your update
585
- route make sure to create derivatives for new attachments:
586
-
587
- ```rb
588
- photo.image_derivatives! if photo.image_changed?
589
- ```
590
-
591
- After the processed files are uploaded, their data is saved into the
592
- `<attachment>_data` column. You can then retrieve the derivatives as
593
- [`Shrine::UploadedFile`][uploaded file] objects:
594
-
595
- ```rb
596
- photo.image(:large) #=> #<Shrine::UploadedFile ...>
597
- photo.image(:large).url #=> "/uploads/store/lg043.jpg"
598
- photo.image(:large).size #=> 5825949
599
- photo.image(:large).mime_type #=> "image/jpeg"
600
- ```
601
-
602
- For more details, see the [`derivatives`][derivatives plugin] plugin
603
- documentation and the [File Processing] guide.
604
-
605
- ### Processing on-the-fly
606
-
607
- On-the-fly processing is provided by the
608
- [`derivation_endpoint`][derivation_endpoint plugin] plugin. It comes with a
609
- [mountable][Mounting Endpoints] Rack app which applies processing on request
610
- and returns processed files.
611
-
612
- To set it up, we mount the Rack app in our router on a chosen path prefix,
613
- configure the plugin with a secret key and that path prefix, and define
614
- processing we want to perform:
615
-
616
- ```rb
617
- # Gemfile
618
- gem "image_processing", "~> 1.8"
619
- ```
620
- ```rb
621
- # config/routes.rb (Rails)
622
- Rails.application.routes.draw do
623
- # ...
624
- mount ImageUploader.derivation_endpoint => "/derivations/image"
625
- end
626
- ```
627
- ```rb
628
- require "image_processing/mini_magick"
629
-
630
- class ImageUploader < Shrine
631
- plugin :derivation_endpoint,
632
- secret_key: "<YOUR SECRET KEY>",
633
- prefix: "derivations/image" # needs to match the mount point in routes
634
-
635
- derivation :thumbnail do |file, width, height|
636
- ImageProcessing::MiniMagick
637
- .source(file)
638
- .resize_to_limit!(width.to_i, height.to_i)
639
- end
640
- end
641
- ```
642
-
643
- Now we can generate URLs from attached files that will perform the desired
644
- processing:
645
-
646
- ```rb
647
- photo.image.derivation_url(:thumbnail, 600, 400)
648
- #=> "/derivations/image/thumbnail/600/400/eyJpZCI6ImZvbyIsInN0b3JhZ2UiOiJzdG9yZSJ9?signature=..."
649
- ```
650
-
651
- The on-the-fly processing feature is highly customizable, see the
652
- [`derivation_endpoint`][derivation_endpoint plugin] plugin documentation for
653
- more details.
654
-
655
- ## Validation
656
-
657
- The [`validation`][validation plugin] plugin allows performing validation for
658
- attached files. For common validations, the
659
- [`validation_helpers`][validation_helpers plugin] plugin provides useful
660
- validators for built in metadata:
661
-
662
- ```rb
663
- Shrine.plugin :validation_helpers
664
- ```
665
- ```rb
666
- class DocumentUploader < Shrine
667
- Attacher.validate do
668
- validate_max_size 5*1024*1024, message: "is too large (max is 5 MB)"
669
- validate_mime_type %w[application/pdf]
670
- end
671
- end
672
- ```
673
-
674
- ```rb
675
- user = User.new
676
- user.cv = File.open("cv.pdf", "rb")
677
- user.valid? #=> false
678
- user.errors.to_hash #=> {:cv=>["is too large (max is 5 MB)"]}
679
- ```
680
-
681
- For more details, see the [File Validation] guide and
682
- [`validation_helpers`][validation_helpers plugin] plugin docs.
683
-
684
- ## Location
685
-
686
- Shrine automatically generated random locations before uploading files. By
687
- default the hierarchy is flat, meaning all files are stored in the root
688
- directory of the storage. The [`pretty_location`][pretty_location plugin]
689
- plugin provides a good default hierarchy, but you can also override
690
- `#generate_location` with a custom implementation:
691
-
692
- ```rb
693
- class ImageUploader < Shrine
694
- def generate_location(io, record: nil, derivative: nil, **)
695
- type = record.class.name.downcase if record
696
- style = derivative ? "thumbs" : "originals"
697
- name = super # the default unique identifier
698
-
699
- [type, style, name].compact.join("/")
700
- end
701
- end
702
- ```
703
- ```
704
- uploads/
705
- photos/
706
- originals/
707
- la98lda74j3g.jpg
708
- thumbs/
709
- 95kd8kafg80a.jpg
710
- ka8agiaf9gk4.jpg
711
- ```
712
-
713
- Note that there should always be a random component in the location, so that
714
- the ORM dirty tracking is detected properly. Inside `#generate_location` you
715
- can also access the extracted metadata through the `:metadata` option.
716
-
717
- ## Direct uploads
718
-
719
- To improve the user experience, it's recommended to upload files asynchronously
720
- as soon as the user selects them. The direct uploads would go to temporary
721
- storage, just like in the synchronous flow. Then, instead of attaching a raw
722
- file to your model, you assign the cached file JSON data.
723
-
724
- ```rb
725
- # in the regular synchronous flow
726
- photo.image = file
727
-
728
- # in the direct upload flow
729
- photo.image = '{"id":"...","storage":"cache","metadata":{...}}'
730
- ```
731
-
732
- On the client side it's highly recommended to use **[Uppy]** :dog:, a very
733
- flexible modern JavaScript file upload library that happens to integrate nicely
734
- with Shrine.
735
-
736
- ### Simple direct upload
737
-
738
- The simplest approach is to upload directly to an endpoint in your app, which
739
- forwards uploads to the specified storage. The
740
- [`upload_endpoint`][upload_endpoint plugin] Shrine plugin provides a
741
- [mountable][Mounting Endpoints] Rack app that implements this endpoint:
742
-
743
- ```rb
744
- Shrine.plugin :upload_endpoint
745
- ```
746
- ```rb
747
- # config/routes.rb (Rails)
748
- Rails.application.routes.draw do
749
- # ...
750
- mount ImageUploader.upload_endpoint(:cache) => "/images/upload" # POST /images/upload
751
- end
752
- ```
753
-
754
- Then you can configure Uppy's [XHR Upload][uppy xhr-upload] plugin to upload to
755
- this endpoint. See [this walkthrough][Adding Direct App Uploads] for adding
756
- simple direct uploads from scratch, it includes a complete JavaScript example
757
- (there is also the [Roda][roda demo] / [Rails][rails demo] demo app).
758
-
759
- ### Presigned direct upload
760
-
761
- For better performance, you can also upload files directly to your cloud
762
- storage service (AWS S3, Google Cloud Storage etc). For this, your temporary
763
- storage needs to be your cloud service:
764
-
765
- ```rb
766
- require "shrine/storage/s3"
767
-
768
- Shrine.storages = {
769
- cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
770
- store: Shrine::Storage::S3.new(**s3_options)
771
- }
772
- ```
773
-
774
- In this flow, the client needs to first fetch upload parameters from the
775
- server, and then use these parameters for the upload to the cloud service.
776
- The [`presign_endpoint`][presign_endpoint plugin] Shrine plugin provides a
777
- [mountable][Mounting Endpoints] Rack app that generates upload parameters:
778
-
779
- ```rb
780
- Shrine.plugin :presign_endpoint
781
- ```
782
- ```rb
783
- # config/routes.rb (Rails)
784
- Rails.application.routes.draw do
785
- # ...
786
- mount Shrine.presign_endpoint(:cache) => "/s3/params" # GET /s3/params
787
- end
788
- ```
789
-
790
- Then you can configure Uppy's [AWS S3][uppy aws-s3] plugin to fetch params from
791
- your endpoint before uploading to S3. See [this walkthrough][Adding Direct S3
792
- Uploads] for adding direct uploads to S3 from scratch, it includes a complete
793
- JavaScript example (there is also the [Roda][roda demo] / [Rails][rails demo]
794
- demo). See also the [Direct Uploads to S3] guide for more details.
795
-
796
- ### Resumable direct upload
797
-
798
- If your app is accepting large uploads, you can improve resilience by making
799
- the uploads **resumable**. This can significantly improve experience for users
800
- on slow and flaky internet connections.
801
-
802
- #### Uppy S3 Multipart
803
-
804
- You can achieve resumable uploads directly to S3 with the [AWS S3
805
- Multipart][uppy aws-s3-multipart] Uppy plugin, accompanied with
806
- `uppy_s3_multipart` Shrine plugin provided by the [uppy-s3_multipart] gem.
807
-
808
- ```rb
809
- # Gemfile
810
- gem "uppy-s3_multipart", "~> 0.3"
811
- ```
812
- ```rb
813
- Shrine.plugin :uppy_s3_multipart
814
- ```
815
- ```rb
816
- # config/routes.rb (Rails)
817
- Rails.application.routes.draw do
818
- # ...
819
- mount Shrine.uppy_s3_multipart(:cache) => "/s3/multipart"
820
- end
821
- ```
822
-
823
- See the [uppy-s3_multipart] docs for more details.
824
-
825
- #### Tus protocol
826
-
827
- If you want a more generic approach, you can build your resumable uploads on
828
- **[tus]** – an open resumable upload protocol. On the server side you can use
829
- the [tus-ruby-server] gem, on the client side Uppy's [Tus][uppy tus] plugin,
830
- and the [shrine-tus] gem for the glue.
831
-
832
- ```rb
833
- # Gemfile
834
- gem "tus-server", "~> 2.0"
835
- gem "shrine-tus", "~> 1.2"
836
- ```
837
- ```rb
838
- require "shrine/storage/tus"
839
-
840
- Shrine.storages = {
841
- cache: Shrine::Storage::Tus.new, # tus server acts as temporary storage
842
- store: ..., # your permanent storage
843
- }
844
- ```
845
- ```rb
846
- # config/routes.rb (Rails)
847
- Rails.application.routes.draw do
848
- # ...
849
- mount Tus::Server => "/files"
850
- end
851
- ```
852
-
853
- See [this walkthrough][Adding Resumable Uploads] for adding tus-powered
854
- resumable uploads from scratch, it includes a complete JavaScript example
855
- (there is also a [demo app][resumable demo]). See also [shrine-tus] and
856
- [tus-ruby-server] docs for more details.
857
-
858
- ## Backgrounding
859
-
860
- The [`backgrounding`][backgrounding plugin] allows you to move file promotion
861
- and deletion into a background job, using the backgrounding library [of your
862
- choice][Backgrounding Libraries]:
863
-
864
- ```rb
865
- Shrine.plugin :backgrounding
866
- Shrine::Attacher.promote_block do
867
- PromoteJob.perform_async(self.class.name, record.class.name, record.id, name, file_data)
868
- end
869
- Shrine::Attacher.destroy_block do
870
- DestroyJob.perform_async(self.class.name, data)
871
- end
872
- ```
873
- ```rb
874
- class PromoteJob
875
- include Sidekiq::Worker
876
-
877
- def perform(attacher_class, record_class, record.id, name, file_data)
878
- attacher_class = Object.const_get(attacher_class)
879
- record = Object.const_get(record_class).find(record_id) # if using Active Record
880
-
881
- attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
882
- attacher.atomic_promote
883
- rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
884
- # attachment has changed or the record has been deleted, nothing to do
885
- end
886
- end
887
- ```
888
- ```rb
889
- class DestroyJob
890
- include Sidekiq::Worker
891
-
892
- def perform(attacher_class, data)
893
- attacher_class = Object.const_get(attacher_class)
894
-
895
- attacher = attacher_class.from_data(data)
896
- attacher.destroy
897
- end
898
- end
899
- ```
900
-
901
- ## Clearing cache
902
-
903
- Shrine doesn't automatically delete files uploaded to temporary storage, instead
904
- you should set up a separate recurring task that will automatically delete old
905
- cached files.
906
-
907
- Most Shrine storage classes come with a `#clear!` method, which you can call in
908
- a recurring script. For FileSystem and S3 storage it would look like this:
909
-
910
- ```rb
911
- # FileSystem storage
912
- file_system = Shrine.storages[:cache]
913
- file_system.clear! { |path| path.mtime < Time.now - 7*24*60*60 } # delete files older than 1 week
914
- ```
915
- ```rb
916
- # S3 storage
917
- s3 = Shrine.storages[:cache]
918
- s3.clear! { |object| object.last_modified < Time.now - 7*24*60*60 } # delete files older than 1 week
919
- ```
920
-
921
- ## Logging
922
-
923
- The [`instrumentation`][instrumentation plugin] plugin sends and logs events for
924
- important operations:
925
-
926
- ```rb
927
- Shrine.plugin :instrumentation, notifications: ActiveSupport::Notifications
928
-
929
- uploaded_file = Shrine.upload(io, :store)
930
- uploaded_file.exists?
931
- uploaded_file.download
932
- uploaded_file.delete
933
- ```
934
- ```
935
- Metadata (32ms) – {:storage=>:store, :io=>StringIO, :uploader=>Shrine}
936
- Upload (1523ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :io=>StringIO, :upload_options=>{}, :uploader=>Shrine}
937
- Exists (755ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}
938
- Download (1002ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :download_options=>{}, :uploader=>Shrine}
939
- Delete (700ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}
940
- ```
941
-
942
- Some plugins add their own instrumentation as well when they detect that the
943
- `instrumentation` plugin has been loaded. For that to work, the
944
- `instrumentation` plugin needs to be loaded *before* any of these plugins.
945
-
946
- | Plugin | Instrumentation |
947
- | :----- | :-------------- |
948
- | `derivation_endpoint` | instruments file processing |
949
- | `derivatives` | instruments file processing |
950
- | `determine_mime_type` | instruments analyzing MIME type |
951
- | `store_dimensions` | instruments extracting image dimensions |
952
- | `signature` | instruments calculating signature |
953
- | `infer_extension` | instruments inferring extension |
954
- | `remote_url` | instruments remote URL downloading |
955
- | `data_uri` | instruments data URI parsing |
956
-
957
- For instrumentation, warnings, and other logging, Shrine uses its internal
958
- logger. You can tell Shrine to use a different logger. For example, if you're
959
- using Rails, you might want to tell it to use the Rails logger:
960
-
961
- ```rb
962
- Shrine.logger = Rails.logger
963
- ```
964
-
965
- In tests you might want to tell Shrine to log only warnings:
966
-
967
- ```rb
968
- Shrine.logger.level = Logger::WARN
969
- ```
19
+ | Resource | URL |
20
+ | :---------------- | :----------------------------------------------------------------------------- |
21
+ | Website & Documentation | [shrinerb.com](https://shrinerb.com) |
22
+ | Demo code | [Roda][roda demo] / [Rails][rails demo] |
23
+ | Wiki | [github.com/shrinerb/shrine/wiki](https://github.com/shrinerb/shrine/wiki) |
24
+ | Help & Discussion | [discourse.shrinerb.com](https://discourse.shrinerb.com) |
970
25
 
971
26
  ## Inspiration
972
27
 
@@ -992,113 +47,36 @@ mailing lists is expected to follow the [Shrine code of conduct][CoC].
992
47
 
993
48
  The gem is available as open source under the terms of the [MIT License].
994
49
 
995
- <!-- Guides & RDocs -->
996
- [Advantages of Shrine]: /doc/advantages.md#readme
997
- [Creating Plugins]: /doc/creating_plugins.md#readme
998
- [Creating Storages]: /doc/creating_storages.md#readme
999
- [Direct Uploads to S3]: /doc/direct_s3.md#readme
1000
- [Extracting Metadata]: /doc/metadata.md#readme
1001
- [File Processing]: /doc/processing.md#readme
1002
- [File Validation]: /doc/validation.md#readme
1003
- [Retrieving Uploads]: /doc/retrieving_uploads.md#readme
1004
- [Using Attacher]: /doc/attacher.md#readme
1005
- [FileSystem]: /doc/storage/file_system.md#readme
1006
- [S3]: /doc/storage/s3.md#readme
1007
- [`Shrine::UploadedFile`]: https://shrinerb.com/rdoc/classes/Shrine/UploadedFile/InstanceMethods.html
1008
-
1009
- <!-- Sections -->
1010
- [attacher]: #attacher
1011
- [attachment]: #attachment
1012
- [backgrounding]: #backgrounding
1013
- [direct uploads]: #direct-uploads
1014
- [io abstraction]: #io-abstraction
1015
- [location]: #location
1016
- [metadata]: #metadata
1017
- [up front]: #processing-up-front
1018
- [on-the-fly]: #processing-on-the-fly
1019
- [plugin system]: #plugin-system
1020
- [simple upload]: #simple-direct-upload
1021
- [presigned upload]: #presigned-direct-upload
1022
- [resumable upload]: #resumable-direct-upload
1023
- [storage]: #storage
1024
- [uploaded file]: #uploaded-file
1025
- [uploading]: #uploading
1026
- [uploader]: #uploader
1027
- [validation]: #validation
1028
-
1029
- <!-- Wikis -->
1030
- [Adding Direct App Uploads]: https://github.com/shrinerb/shrine/wiki/Adding-Direct-App-Uploads
1031
- [Adding Resumable Uploads]: https://github.com/shrinerb/shrine/wiki/Adding-Resumable-Uploads
1032
- [Adding Direct S3 Uploads]: https://github.com/shrinerb/shrine/wiki/Adding-Direct-S3-Uploads
1033
- [Backgrounding Libraries]: https://github.com/shrinerb/shrine/wiki/Backgrounding-Libraries
1034
- [Mounting Endpoints]: https://github.com/shrinerb/shrine/wiki/Mounting-Endpoints
1035
-
1036
- <!-- Storage & Destinations -->
1037
- [AWS S3]: https://aws.amazon.com/s3/
1038
- [MinIO]: https://min.io/
1039
- [DigitalOcean Spaces]: https://www.digitalocean.com/products/spaces/
1040
- [Cloudinary]: https://github.com/shrinerb/shrine-cloudinary
50
+ [Shrine]: https://shrinerb.com
51
+ [plugin system]: https://shrinerb.com/docs/getting-started#plugin-system
52
+ [Retrieving Uploads]: https://shrinerb.com/docs/retrieving-uploads
53
+ [FileSystem]: https://shrinerb.com/docs/storage/file-system
54
+ [S3]: https://shrinerb.com/docs/storage/s3
1041
55
  [GCS]: https://github.com/renchap/shrine-google_cloud_storage
56
+ [Cloudinary]: https://github.com/shrinerb/shrine-cloudinary
57
+ [Sequel]: https://shrinerb.com/docs/plugins/sequel
58
+ [ActiveRecord]: https://shrinerb.com/docs/plugins/activerecord
59
+ [ROM]: https://github.com/shrinerb/shrine-rom
60
+ [Hanami]: https://github.com/katafrakt/hanami-shrine
61
+ [Mongoid]: https://github.com/shrinerb/shrine-mongoid
62
+ [up front]: https://shrinerb.com/docs/getting-started#processing-up-front
63
+ [on-the-fly]: https://shrinerb.com/docs/getting-started#processing-on-the-fly
64
+ [ImageMagick]: https://github.com/janko/image_processing/blob/master/doc/minimagick.md#readme
65
+ [libvips]: https://github.com/janko/image_processing/blob/master/doc/vips.md#readme
66
+ [validation]: https://shrinerb.com/docs/validation
67
+ [metadata]: https://shrinerb.com/docs/metadata
68
+ [simple upload]: https://shrinerb.com/docs/getting-started#simple-direct-upload
69
+ [presigned upload]: https://shrinerb.com/docs/getting-started#presigned-direct-upload
70
+ [resumable upload]: https://shrinerb.com/docs/getting-started#resumable-direct-upload
71
+ [Uppy]: https://uppy.io/
1042
72
  [uppy-s3_multipart]: https://github.com/janko/uppy-s3_multipart
1043
73
  [tus-ruby-server]: https://github.com/janko/tus-ruby-server
1044
-
1045
- <!-- Direct Uploads -->
1046
- [Uppy]: https://uppy.io
1047
- [shrine-tus]: https://github.com/shrinerb/shrine-tus
1048
- [tus]: https://tus.io
1049
- [uppy aws-s3-multipart]: https://uppy.io/docs/aws-s3-multipart/
1050
- [uppy aws-s3]: https://uppy.io/docs/aws-s3/
1051
- [uppy tus]: https://uppy.io/docs/tus/
1052
- [uppy xhr-upload]: https://uppy.io/docs/xhr-upload/
1053
-
1054
- <!-- Processing -->
1055
- [ImageMagick]: https://imagemagick.org/
1056
- [MiniMagick]: https://github.com/minimagick/minimagick
1057
- [ruby-vips]: https://github.com/libvips/ruby-vips
1058
- [ImageProcessing]: https://github.com/janko/image_processing
1059
- [ImageProcessing::MiniMagick]: https://github.com/janko/image_processing/blob/master/doc/minimagick.md#readme
1060
- [ImageProcessing::Vips]: https://github.com/janko/image_processing/blob/master/doc/vips.md#readme
1061
- [libvips]: http://libvips.github.io/libvips/
1062
- [`file`]: http://linux.die.net/man/1/file
1063
-
1064
- <!-- Plugins -->
1065
- [activerecord plugin]: /doc/plugins/activerecord.md#readme
1066
- [add_metadata plugin]: /doc/plugins/add_metadata.md#readme
1067
- [backgrounding plugin]: /doc/plugins/backgrounding.md#readme
1068
- [derivation_endpoint plugin]: /doc/plugins/derivation_endpoint.md#readme
1069
- [derivatives plugin]: /doc/plugins/derivatives.md#readme
1070
- [determine_mime_type plugin]: /doc/plugins/determine_mime_type.md#readme
1071
- [instrumentation plugin]: /doc/plugins/instrumentation.md#readme
1072
- [hanami plugin]: https://github.com/katafrakt/hanami-shrine
1073
- [mongoid plugin]: https://github.com/shrinerb/shrine-mongoid
1074
- [presign_endpoint plugin]: /doc/plugins/presign_endpoint.md#readme
1075
- [pretty_location plugin]: /doc/plugins/pretty_location.md#readme
1076
- [rack_file plugin]: /doc/plugins/rack_file.md#readme
1077
- [rom plugin]: https://github.com/shrinerb/shrine-rom
1078
- [sequel plugin]: /doc/plugins/sequel.md#readme
1079
- [signature plugin]: /doc/plugins/signature.md#readme
1080
- [store_dimensions plugin]: /doc/plugins/store_dimensions.md#readme
1081
- [upload_endpoint plugin]: /doc/plugins/upload_endpoint.md#readme
1082
- [validation_helpers plugin]: /doc/plugins/validation_helpers.md#readme
1083
- [validation plugin]: /doc/plugins/validation.md#readme
1084
-
1085
- <!-- Demos -->
74
+ [backgrounding]: https://shrinerb.com/docs/plugins/backgrounding
75
+ [Backgrounding Libraries]: https://github.com/shrinerb/shrine/wiki/Backgrounding-Libraries
76
+ [Getting Started guide]: https://shrinerb.com/docs/getting-started
77
+ [roda demo]: /demo
1086
78
  [rails demo]: https://github.com/erikdahlstrand/shrine-rails-example
1087
- [roda demo]: https://github.com/shrinerb/shrine/tree/master/demo
1088
- [resumable demo]: https://github.com/shrinerb/shrine-tus-demo
1089
-
1090
- <!-- Misc -->
1091
- [`#read`]: https://ruby-doc.org/core/IO.html#method-i-read
1092
- [`#eof?`]: https://ruby-doc.org/core/IO.html#method-i-eof
1093
- [`#rewind`]: https://ruby-doc.org/core/IO.html#method-i-rewind
1094
- [`#close`]: https://ruby-doc.org/core/IO.html#method-i-close
1095
- [`IO`]: https://ruby-doc.org/core/IO.html
1096
79
  [Refile]: https://github.com/refile/refile
1097
80
  [Roda]: https://github.com/jeremyevans/roda
1098
-
1099
- <!-- Project -->
1100
- [Shrine]: https://shrinerb.com
1101
- [external]: https://shrinerb.com/#external
1102
- [plugins]: https://shrinerb.com/#plugins
1103
- [CoC]: CODE_OF_CONDUCT.md
1104
- [MIT License]: http://opensource.org/licenses/MIT
81
+ [CoC]: /CODE_OF_CONDUCT.md
82
+ [MIT License]: /LICENSE.txt