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