shrine 2.19.4 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (209) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +485 -43
  3. data/LICENSE.txt +1 -1
  4. data/README.md +81 -977
  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 +102 -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 +1115 -0
  20. data/doc/metadata.md +190 -109
  21. data/doc/multiple_files.md +62 -34
  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 +162 -101
  34. data/doc/plugins/derivatives.md +829 -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 +14 -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 +185 -167
  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 +4 -0
  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/retrieving_uploads.md +4 -1
  116. data/doc/securing_uploads.md +60 -37
  117. data/doc/storage/file_system.md +20 -3
  118. data/doc/storage/memory.md +19 -0
  119. data/doc/storage/s3.md +117 -83
  120. data/doc/testing.md +124 -144
  121. data/doc/upgrading_to_3.md +710 -0
  122. data/doc/validation.md +54 -90
  123. data/lib/shrine/attacher.rb +287 -171
  124. data/lib/shrine/attachment.rb +13 -46
  125. data/lib/shrine/plugins/_persistence.rb +93 -0
  126. data/lib/shrine/plugins/activerecord.rb +77 -34
  127. data/lib/shrine/plugins/add_metadata.rb +25 -17
  128. data/lib/shrine/plugins/atomic_helpers.rb +119 -0
  129. data/lib/shrine/plugins/backgrounding.rb +77 -113
  130. data/lib/shrine/plugins/cached_attachment_data.rb +6 -15
  131. data/lib/shrine/plugins/column.rb +102 -0
  132. data/lib/shrine/plugins/data_uri.rb +38 -36
  133. data/lib/shrine/plugins/default_storage.rb +45 -15
  134. data/lib/shrine/plugins/default_url.rb +12 -24
  135. data/lib/shrine/plugins/default_url_options.rb +3 -30
  136. data/lib/shrine/plugins/delete_raw.rb +10 -16
  137. data/lib/shrine/plugins/derivation_endpoint.rb +89 -134
  138. data/lib/shrine/plugins/derivatives.rb +637 -0
  139. data/lib/shrine/plugins/determine_mime_type.rb +9 -21
  140. data/lib/shrine/plugins/download_endpoint.rb +109 -133
  141. data/lib/shrine/plugins/dynamic_storage.rb +5 -11
  142. data/lib/shrine/plugins/entity.rb +152 -0
  143. data/lib/shrine/plugins/form_assign.rb +108 -0
  144. data/lib/shrine/plugins/included.rb +6 -6
  145. data/lib/shrine/plugins/infer_extension.rb +13 -20
  146. data/lib/shrine/plugins/instrumentation.rb +54 -42
  147. data/lib/shrine/plugins/keep_files.rb +3 -15
  148. data/lib/shrine/plugins/metadata_attributes.rb +28 -19
  149. data/lib/shrine/plugins/mirroring.rb +142 -0
  150. data/lib/shrine/plugins/model.rb +158 -0
  151. data/lib/shrine/plugins/module_include.rb +3 -3
  152. data/lib/shrine/plugins/multi_cache.rb +27 -0
  153. data/lib/shrine/plugins/presign_endpoint.rb +18 -22
  154. data/lib/shrine/plugins/pretty_location.rb +15 -9
  155. data/lib/shrine/plugins/processing.rb +22 -9
  156. data/lib/shrine/plugins/rack_file.rb +2 -42
  157. data/lib/shrine/plugins/rack_response.rb +15 -10
  158. data/lib/shrine/plugins/recache.rb +6 -5
  159. data/lib/shrine/plugins/refresh_metadata.rb +13 -11
  160. data/lib/shrine/plugins/remote_url.rb +49 -49
  161. data/lib/shrine/plugins/remove_attachment.rb +10 -6
  162. data/lib/shrine/plugins/remove_invalid.rb +19 -8
  163. data/lib/shrine/plugins/restore_cached_data.rb +13 -7
  164. data/lib/shrine/plugins/sequel.rb +86 -36
  165. data/lib/shrine/plugins/signature.rb +10 -16
  166. data/lib/shrine/plugins/store_dimensions.rb +35 -40
  167. data/lib/shrine/plugins/tempfile.rb +1 -3
  168. data/lib/shrine/plugins/type_predicates.rb +113 -0
  169. data/lib/shrine/plugins/upload_endpoint.rb +25 -23
  170. data/lib/shrine/plugins/upload_options.rb +14 -15
  171. data/lib/shrine/plugins/url_options.rb +31 -0
  172. data/lib/shrine/plugins/validation.rb +80 -0
  173. data/lib/shrine/plugins/validation_helpers.rb +34 -57
  174. data/lib/shrine/plugins/versions.rb +107 -87
  175. data/lib/shrine/plugins.rb +22 -0
  176. data/lib/shrine/storage/file_system.rb +46 -64
  177. data/lib/shrine/storage/linter.rb +42 -7
  178. data/lib/shrine/storage/memory.rb +49 -0
  179. data/lib/shrine/storage/s3.rb +154 -158
  180. data/lib/shrine/uploaded_file.rb +28 -30
  181. data/lib/shrine/version.rb +3 -3
  182. data/lib/shrine.rb +86 -149
  183. data/shrine.gemspec +9 -10
  184. metadata +79 -83
  185. data/doc/migrating_storage.md +0 -76
  186. data/doc/plugins/backup.md +0 -31
  187. data/doc/plugins/copy.md +0 -24
  188. data/doc/plugins/delete_promoted.md +0 -12
  189. data/doc/plugins/direct_upload.md +0 -172
  190. data/doc/plugins/hooks.md +0 -58
  191. data/doc/plugins/logging.md +0 -42
  192. data/doc/plugins/migration_helpers.md +0 -60
  193. data/doc/plugins/moving.md +0 -19
  194. data/doc/plugins/multi_delete.md +0 -20
  195. data/doc/plugins/parallelize.md +0 -16
  196. data/doc/plugins/parsed_json.md +0 -23
  197. data/doc/regenerating_versions.md +0 -143
  198. data/lib/shrine/plugins/background_helpers.rb +0 -5
  199. data/lib/shrine/plugins/backup.rb +0 -90
  200. data/lib/shrine/plugins/copy.rb +0 -50
  201. data/lib/shrine/plugins/delete_promoted.rb +0 -20
  202. data/lib/shrine/plugins/direct_upload.rb +0 -217
  203. data/lib/shrine/plugins/hooks.rb +0 -90
  204. data/lib/shrine/plugins/logging.rb +0 -142
  205. data/lib/shrine/plugins/migration_helpers.rb +0 -70
  206. data/lib/shrine/plugins/moving.rb +0 -57
  207. data/lib/shrine/plugins/multi_delete.rb +0 -32
  208. data/lib/shrine/plugins/parallelize.rb +0 -78
  209. 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
+ | Help & Discussion | [discourse.shrinerb.com](https://discourse.shrinerb.com) |
54
29
 
55
- ## Quick start
30
+ ## Setup
56
31
 
57
- Add Shrine to the Gemfile and write an initializer which sets up the storage and
58
- loads the ORM plugin:
32
+ Add the gem to your Gemfile:
59
33
 
60
34
  ```rb
61
35
  # Gemfile
62
- gem "shrine", "~> 2.0"
36
+ gem "shrine", "~> 3.0"
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
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
78
54
  ```
79
55
 
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:
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:
83
58
 
84
- ```rb
85
- Sequel.migration do
86
- change do
87
- add_column :photos, :image_data, :text
88
- end
89
- end
90
59
  ```
91
-
92
- In Rails with Active Record the migration would look similar:
93
-
94
- ```sh
95
- $ rails generate migration add_image_data_to_photos image_data:text
96
- ```
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 %>
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
113
 
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
-
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