shrine 3.0.1 → 3.3.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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +15 -5
  5. data/doc/advantages.md +33 -16
  6. data/doc/attacher.md +2 -2
  7. data/doc/carrierwave.md +78 -34
  8. data/doc/changing_derivatives.md +39 -39
  9. data/doc/design.md +134 -85
  10. data/doc/direct_s3.md +1 -0
  11. data/doc/external/articles.md +57 -45
  12. data/doc/external/extensions.md +41 -35
  13. data/doc/external/misc.md +23 -8
  14. data/doc/getting_started.md +177 -112
  15. data/doc/metadata.md +79 -43
  16. data/doc/multiple_files.md +6 -4
  17. data/doc/paperclip.md +119 -42
  18. data/doc/plugins/activerecord.md +1 -1
  19. data/doc/plugins/add_metadata.md +112 -35
  20. data/doc/plugins/atomic_helpers.md +41 -3
  21. data/doc/plugins/backgrounding.md +12 -2
  22. data/doc/plugins/column.md +36 -7
  23. data/doc/plugins/data_uri.md +2 -2
  24. data/doc/plugins/default_url.md +6 -3
  25. data/doc/plugins/derivation_endpoint.md +26 -28
  26. data/doc/plugins/derivatives.md +238 -171
  27. data/doc/plugins/determine_mime_type.md +2 -2
  28. data/doc/plugins/download_endpoint.md +5 -5
  29. data/doc/plugins/dynamic_storage.md +1 -1
  30. data/doc/plugins/form_assign.md +5 -5
  31. data/doc/plugins/included.md +25 -5
  32. data/doc/plugins/infer_extension.md +11 -2
  33. data/doc/plugins/instrumentation.md +1 -1
  34. data/doc/plugins/metadata_attributes.md +22 -10
  35. data/doc/plugins/mirroring.md +1 -1
  36. data/doc/plugins/persistence.md +11 -1
  37. data/doc/plugins/refresh_metadata.md +5 -4
  38. data/doc/plugins/remote_url.md +8 -3
  39. data/doc/plugins/remove_invalid.md +9 -1
  40. data/doc/plugins/signature.md +11 -2
  41. data/doc/plugins/store_dimensions.md +12 -2
  42. data/doc/plugins/type_predicates.md +96 -0
  43. data/doc/plugins/upload_endpoint.md +7 -11
  44. data/doc/plugins/upload_options.md +1 -1
  45. data/doc/plugins/url_options.md +4 -4
  46. data/doc/plugins/validation.md +14 -4
  47. data/doc/plugins/validation_helpers.md +3 -3
  48. data/doc/plugins/versions.md +7 -7
  49. data/doc/processing.md +290 -127
  50. data/doc/refile.md +39 -18
  51. data/doc/release_notes/2.19.0.md +1 -1
  52. data/doc/release_notes/2.8.0.md +1 -1
  53. data/doc/release_notes/3.0.0.md +1 -1
  54. data/doc/release_notes/3.0.1.md +4 -0
  55. data/doc/release_notes/3.1.0.md +73 -0
  56. data/doc/release_notes/3.2.0.md +96 -0
  57. data/doc/release_notes/3.2.1.md +31 -0
  58. data/doc/release_notes/3.2.2.md +14 -0
  59. data/doc/release_notes/3.3.0.md +105 -0
  60. data/doc/securing_uploads.md +3 -3
  61. data/doc/storage/file_system.md +1 -1
  62. data/doc/storage/memory.md +19 -0
  63. data/doc/storage/s3.md +105 -82
  64. data/doc/testing.md +2 -2
  65. data/doc/upgrading_to_3.md +97 -49
  66. data/doc/validation.md +3 -2
  67. data/lib/shrine.rb +8 -8
  68. data/lib/shrine/attacher.rb +24 -14
  69. data/lib/shrine/attachment.rb +5 -5
  70. data/lib/shrine/plugins.rb +22 -0
  71. data/lib/shrine/plugins/activerecord.rb +1 -1
  72. data/lib/shrine/plugins/add_metadata.rb +18 -7
  73. data/lib/shrine/plugins/backgrounding.rb +2 -2
  74. data/lib/shrine/plugins/default_storage.rb +6 -6
  75. data/lib/shrine/plugins/default_url.rb +1 -1
  76. data/lib/shrine/plugins/derivation_endpoint.rb +12 -7
  77. data/lib/shrine/plugins/derivatives.rb +61 -29
  78. data/lib/shrine/plugins/determine_mime_type.rb +3 -3
  79. data/lib/shrine/plugins/entity.rb +6 -6
  80. data/lib/shrine/plugins/mirroring.rb +8 -8
  81. data/lib/shrine/plugins/model.rb +3 -3
  82. data/lib/shrine/plugins/presign_endpoint.rb +16 -4
  83. data/lib/shrine/plugins/pretty_location.rb +1 -1
  84. data/lib/shrine/plugins/processing.rb +1 -1
  85. data/lib/shrine/plugins/refresh_metadata.rb +2 -2
  86. data/lib/shrine/plugins/remote_url.rb +3 -3
  87. data/lib/shrine/plugins/remove_attachment.rb +5 -0
  88. data/lib/shrine/plugins/remove_invalid.rb +10 -5
  89. data/lib/shrine/plugins/sequel.rb +1 -1
  90. data/lib/shrine/plugins/signature.rb +7 -6
  91. data/lib/shrine/plugins/store_dimensions.rb +22 -11
  92. data/lib/shrine/plugins/type_predicates.rb +113 -0
  93. data/lib/shrine/plugins/upload_endpoint.rb +10 -5
  94. data/lib/shrine/plugins/upload_options.rb +2 -2
  95. data/lib/shrine/plugins/url_options.rb +2 -2
  96. data/lib/shrine/plugins/validation.rb +9 -7
  97. data/lib/shrine/storage/linter.rb +4 -4
  98. data/lib/shrine/storage/memory.rb +5 -3
  99. data/lib/shrine/storage/s3.rb +117 -38
  100. data/lib/shrine/uploaded_file.rb +0 -1
  101. data/lib/shrine/version.rb +2 -2
  102. data/shrine.gemspec +7 -8
  103. metadata +25 -31
@@ -4,44 +4,50 @@ title: Extensions
4
4
 
5
5
  ## Storages
6
6
 
7
- * [shrine-aliyun-oss](https://github.com/zillou/shrine-aliyun-oss)
8
- * [shrine-cloudinary](https://github.com/shrinerb/shrine-cloudinary)
9
- * [shrine-flickr](https://github.com/shrinerb/shrine-flickr)
10
- * [shrine-fog](https://github.com/shrinerb/shrine-fog)
11
- * [shrine-ftp](https://github.com/ProjectResound/shrine-ftp)
12
- * [shrine-google_cloud_storage](https://github.com/renchap/shrine-google_cloud_storage)
13
- * [shrine-gdrive_storage](https://github.com/edwardsharp/shrine-gdrive_storage)
14
- * [shrine-gridfs](https://github.com/shrinerb/shrine-gridfs)
15
- * [shrine-imgix](https://github.com/shrinerb/shrine-imgix)
16
- * [shrine-memory](https://github.com/shrinerb/shrine-memory)
17
- * [shrine-redis](https://github.com/dbongo/shrine-redis)
18
- * [shrine-scp](https://github.com/jordanandree/shrine-scp)
19
- * [shrine-sql](https://github.com/shrinerb/shrine-sql)
20
- * [shrine-thumbor](https://github.com/havran/shrine-thumbor)
21
- * [shrine-transloadit](https://github.com/shrinerb/shrine-transloadit)
22
- * [shrine-uploadcare](https://github.com/shrinerb/shrine-uploadcare)
23
- * [shrine-url](https://github.com/shrinerb/shrine-url)
24
- * [shrine-storage-you_tube](https://github.com/thedyrt/shrine-storage-you_tube)
25
- * [shrine-webdav](https://github.com/funbox/shrine-webdav)
7
+ | Gem | Description |
8
+ | :---- | :---------- |
9
+ | [shrine-aliyun-oss](https://github.com/zillou/shrine-aliyun-oss) | Storage using [Alibaba Cloud OSS](https://www.alibabacloud.com/product/oss) |
10
+ | [shrine-cloudinary](https://github.com/shrinerb/shrine-cloudinary) | Storage using [Cloudinary](https://cloudinary.com/) |
11
+ | [shrine-flickr](https://github.com/shrinerb/shrine-flickr) | Storage using [Flickr](https://flickr.com/) |
12
+ | [shrine-fog](https://github.com/shrinerb/shrine-fog) | Storage using [Fog](http://fog.io/) |
13
+ | [shrine-ftp](https://github.com/ProjectResound/shrine-ftp) | Storage using an FTP server |
14
+ | [shrine-google_cloud_storage](https://github.com/renchap/shrine-google_cloud_storage) | Storage using [Google Cloud Storage](https://cloud.google.com/storage/) |
15
+ | [shrine-gdrive_storage](https://github.com/edwardsharp/shrine-gdrive_storage) | Storage using [Google Drive](https://www.google.com/drive/) |
16
+ | [shrine-gridfs](https://github.com/shrinerb/shrine-gridfs) | Storage using [Mongo GridFS](https://docs.mongodb.com/manual/core/gridfs/) |
17
+ | [shrine-redis](https://github.com/dbongo/shrine-redis) | Storage using [Redis](https://redis.io/) |
18
+ | [shrine-scp](https://github.com/jordanandree/shrine-scp) | Storage using `scp` |
19
+ | [shrine-sql](https://github.com/shrinerb/shrine-sql) | Storage using an SQL database |
20
+ | [shrine-uploadcare](https://github.com/shrinerb/shrine-uploadcare) | Storage using [Uploadcare](https://uploadcare.com) |
21
+ | [shrine-url](https://github.com/shrinerb/shrine-url) | Storage for handling remote URLs |
22
+ | [shrine-storage-you_tube](https://github.com/thedyrt/shrine-storage-you_tube) | Storage using [YouTube](https://www.youtube.com/) |
23
+ | [shrine-webdav](https://github.com/funbox/shrine-webdav) | Storage using a [WebDAV](https://en.wikipedia.org/wiki/WebDAV) server |
26
24
 
27
25
  ## Plugins
28
26
 
29
- * [administrate-field-shrine](https://github.com/catsky/administrate-field-shrine)
30
- * [rails_admin_shrine](https://github.com/iquest/rails_admin_shrine)
31
- * [shrine-color](https://github.com/jnylen/shrine-color)
32
- * [shrine-configurable_storage](https://github.com/SleeplessByte/shrine-configurable_storage)
33
- * [shrine-content_addressable](https://github.com/SleeplessByte/shrine-content_addressable)
34
- * [shrine-lambda](https://github.com/texpert/shrine-lambda)
35
- * [hanami-shrine](https://github.com/katafrakt/hanami-shrine)
36
- * [shrine-mongoid](https://github.com/shrinerb/shrine-mongoid)
37
- * [shrine-rails](https://github.com/abepetrillo/shrine-rails)
38
- * [shrine-reform](https://github.com/shrinerb/shrine-reform)
39
- * [shrine-rom](https://github.com/shrinerb/shrine-rom)
40
- * [shrine-tus](https://github.com/shrinerb/shrine-tus)
27
+ | Gem | Description |
28
+ | :---- | :-------- |
29
+ | [administrate-field-shrine](https://github.com/catsky/administrate-field-shrine) | Plugin for [Administrate](https://github.com/thoughtbot/administrate) |
30
+ | [rails_admin_shrine](https://github.com/iquest/rails_admin_shrine) | Plugin for [RailsAdmin](https://github.com/sferik/rails_admin) |
31
+ | [shrine-blurhash](https://github.com/renchap/shrine-blurhash) | Plugin for computing [Blurhash](https://blurha.sh/) on images |
32
+ | [shrine-cloudimage](https://github.com/janklimo/shrine-cloudimage) | Plugin for [Cloudimage](https://www.cloudimage.io/) |
33
+ | [shrine-color](https://github.com/jnylen/shrine-color) | Plugin for finding dominant color in an image |
34
+ | [shrine-configurable_storage](https://github.com/SleeplessByte/shrine-configurable_storage) | Plugin for lazy storage registration |
35
+ | [shrine-content_addressable](https://github.com/SleeplessByte/shrine-content_addressable) | Plugin for generating content addressable locations |
36
+ | [shrine-imgix](https://github.com/shrinerb/shrine-imgix) | Plugin for [Imgix](https://www.imgix.com/) |
37
+ | [shrine-transloadit](https://github.com/shrinerb/shrine-transloadit) | Plugin for [Transloadit](https://transloadit.com/) |
38
+ | [shrine-lambda](https://github.com/texpert/shrine-lambda) | Plugin for [AWS Lambda](https://aws.amazon.com/lambda/) |
39
+ | [hanami-shrine](https://github.com/katafrakt/hanami-shrine) | Plugin for [Hanami](https://hanamirb.org/) |
40
+ | [shrine-mongoid](https://github.com/shrinerb/shrine-mongoid) | Plugin for [Mongoid](https://mongoid.org) |
41
+ | [shrine-rails](https://github.com/abepetrillo/shrine-rails) | Plugin for [Rails](https://rubyonrails.org/) |
42
+ | [shrine-rom](https://github.com/shrinerb/shrine-rom) | Plugin for [ROM](https://rom-rb.org/) |
43
+ | [shrine-tus](https://github.com/shrinerb/shrine-tus) | Plugin for [tus](https://tus.io) server integration |
41
44
 
42
45
  ## Libraries
43
46
 
44
- * [ckeditor](https://github.com/galetahub/ckeditor)
45
- * [imgproxy](https://github.com/imgproxy/imgproxy.rb)
46
- * [rails_admin](https://github.com/sferik/rails_admin)
47
- * [uppy-s3_multipart](https://github.com/janko/uppy-s3_multipart)
47
+ | Gem | Description |
48
+ | :----- | :------- |
49
+ | [ckeditor](https://github.com/galetahub/ckeditor) | Integration for [CKEditor](https://ckeditor.com/ckeditor-4/) |
50
+ | [faster_s3_url](https://github.com/jrochkind/faster_s3_url) | Optimized generation of public and presigned AWS S3 GET URLs |
51
+ | [imgproxy](https://github.com/imgproxy/imgproxy.rb) | Integration for [imgproxy](https://github.com/imgproxy/imgproxy) |
52
+ | [rails_admin](https://github.com/sferik/rails_admin) | Integration for [RailsAdmin](https://github.com/sferik/rails_admin) |
53
+ | [uppy-s3_multipart](https://github.com/janko/uppy-s3_multipart) | Integration for [Uppy AWS S3 Multipart](https://uppy.io/docs/aws-s3-multipart/) |
@@ -4,14 +4,29 @@ title: Miscellaneous
4
4
 
5
5
  ## Demos
6
6
 
7
- * [Dropzone demo](https://github.com/codyeatworld/example-shrine-dropzone)
8
- * [Hanami demo](https://github.com/katafrakt/hanami-shrine-example)
9
- * [Rails demo](https://github.com/erikdahlstrand/shrine-rails-example)
10
- * [Resumable uploads demo](https://github.com/shrinerb/shrine-tus-demo)
11
- * [Roda demo (official)](https://github.com/shrinerb/shrine/tree/master/demo)
12
- * [ROM & dry-rb demo](https://github.com/shrinerb/shrine-rom/tree/master/demo)
13
- * [Transloadit demo](https://github.com/shrinerb/shrine-transloadit/tree/master/demo)
7
+ | Demo | Description |
8
+ | :--- | :---------- |
9
+ | [Dropzone demo](https://github.com/codyeatworld/example-shrine-dropzone) | Shows direct upload using [Dropzone.js] |
10
+ | [Hanami demo](https://github.com/katafrakt/hanami-shrine-example) | Shows file attachment in [Hanami] |
11
+ | [Crop demo](https://github.com/shrinerb/shrine-crop-example) | Shows image cropping using [Cropper.js] |
12
+ | [Rails demo](https://github.com/erikdahlstrand/shrine-rails-example) | Shows direct upload in [Rails] |
13
+ | [Resumable uploads demo](https://github.com/shrinerb/shrine-tus-demo) | Shows resumable direct upload on [tus] |
14
+ | [Roda demo (official)](https://github.com/shrinerb/shrine/tree/master/demo) | Shows direct upload in [Roda] |
15
+ | [rom-rb & dry-rb demo](https://github.com/shrinerb/shrine-rom/tree/master/demo) | Shows file attachment with [rom-rb] and [dry-rb] |
16
+ | [Transloadit demo](https://github.com/shrinerb/shrine-transloadit/tree/master/demo) | Shows file processing using [Transloadit] |
14
17
 
15
18
  ## Projects
16
19
 
17
- * [Cortex CMS](https://docs.cortexcms.org)
20
+ | Project | Description |
21
+ | :------ | :---------- |
22
+ | [CortexCMS](https://docs.cortexcms.org) | An open source, enterprise content management and distribution platform |
23
+
24
+ [Dropzone.js]: https://www.dropzonejs.com/
25
+ [Hanami]: https://hanamirb.org/
26
+ [Cropper.js]: https://github.com/fengyuanchen/cropperjs
27
+ [Rails]: https://rubyonrails.org/
28
+ [tus]: https://tus.io/
29
+ [Roda]: https://roda.jeremyevans.net/
30
+ [rom-rb]: https://rom-rb.org/
31
+ [dry-rb]: https://dry-rb.org/
32
+ [Transloadit]: https://transloadit.com/
@@ -50,7 +50,7 @@ class AddImageDataToPhotos < ActiveRecord::Migration
50
50
  end
51
51
  ```
52
52
  <!--Rails-->
53
- ```sh
53
+ ```
54
54
  $ rails generate migration add_image_data_to_photos image_data:text
55
55
  ```
56
56
  <!--END_DOCUSAURUS_CODE_TABS-->
@@ -110,6 +110,14 @@ form @photo, action: "/photos", enctype: "multipart/form-data" do |f|
110
110
  f.button "Create"
111
111
  end
112
112
  ```
113
+ <!--HTML-->
114
+ ```erb
115
+ <form action="/photos" method="post" enctype="multipart/form-data">
116
+ <input name="photo[image]" type="hidden" value="<%= @photo.cached_image_data %>" />
117
+ <input name="photo[image] "type="file" />
118
+ <input type="submit" value="Create" />
119
+ </form>
120
+ ```
113
121
  <!--END_DOCUSAURUS_CODE_TABS-->
114
122
 
115
123
  Note that the file field needs to go *after* the hidden field, so that
@@ -167,9 +175,13 @@ specific storage service, by implementing a common public interface. Storage
167
175
  instances are registered under an identifier in `Shrine.storages`, so that they
168
176
  can later be used by [uploaders][uploader].
169
177
 
170
- Previously we've shown the [FileSystem] storage which saves files to disk, but
171
- Shrine also ships with [S3] storage which stores files on [AWS S3] (or any
172
- S3-compatible service such as [DigitalOcean Spaces] or [MinIO]).
178
+ Shrine ships with the following storages:
179
+
180
+ * [`Shrine::Storage::FileSystem`][FileSystem] stores files on disk
181
+ * [`Shrine::Storage::S3`][S3] – stores files on [AWS S3] (or [DigitalOcean Spaces], [MinIO], ...)
182
+ * [`Shrine::Storage::Memory`][Memory] – stores file in memory (convenient for [testing][Testing with Shrine])
183
+
184
+ Here is how we might configure Shrine with S3 storage:
173
185
 
174
186
  ```rb
175
187
  # Gemfile
@@ -180,14 +192,14 @@ require "shrine/storage/s3"
180
192
 
181
193
  s3_options = {
182
194
  bucket: "<YOUR BUCKET>", # required
195
+ region: "<YOUR REGION>", # required
183
196
  access_key_id: "<YOUR ACCESS KEY ID>",
184
197
  secret_access_key: "<YOUR SECRET ACCESS KEY>",
185
- region: "<YOUR REGION>",
186
198
  }
187
199
 
188
200
  Shrine.storages = {
189
- cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
190
- store: Shrine::Storage::S3.new(**s3_options),
201
+ cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options), # temporary
202
+ store: Shrine::Storage::S3.new(**s3_options), # permanent
191
203
  }
192
204
  ```
193
205
 
@@ -196,9 +208,9 @@ suitable for [direct uploads][presigned upload]. The `:cache` and `:store`
196
208
  names are special only in terms that the [attacher] will automatically pick
197
209
  them up, you can also register more storage objects under different names.
198
210
 
199
- See the [FileSystem] and [S3] storage docs for more details. There are [many
200
- more Shrine storages][storages] provided by external gems, and you can also
201
- [create your own storage][Creating Storages].
211
+ See the [FileSystem]/[S3]/[Memory] storage docs for more details. There are
212
+ [many more Shrine storages][storages] provided by external gems, and you can
213
+ also [create your own storage][Creating Storages].
202
214
 
203
215
  ## Uploader
204
216
 
@@ -254,15 +266,17 @@ uploader.upload(io, upload_options: { acl: "public-read" }) # add options to Sto
254
266
 
255
267
  Shrine is able to upload any IO-like object that implement methods [`#read`],
256
268
  [`#rewind`], [`#eof?`] and [`#close`] whose behaviour matches the [`IO`] class.
257
- This includes built-in IO and IO-like objects like File, Tempfile and StringIO.
269
+ This includes but is not limited to the following objects:
258
270
 
259
- When a file is uploaded to a Rails app, in request params it will be
260
- represented by an `ActionDispatch::Http::UploadedFile` object, which is also an
261
- IO-like object accepted by Shrine. In other Rack applications the uploaded file
262
- will be represented as a Hash, but it can be converted into an IO-like object
263
- with the [`rack_file`][rack_file plugin] plugin.
264
-
265
- Here are some examples of various IO-like objects that can be uploaded:
271
+ * [`File`](https://ruby-doc.org/core/File.html)
272
+ * [`Tempfile`](https://ruby-doc.org/stdlib/libdoc/tempfile/rdoc/Tempfile.html)
273
+ * [`StringIO`](https://ruby-doc.org/stdlib/libdoc/stringio/rdoc/StringIO.html)
274
+ * [`ActionDispatch::Http::UploadedFile`](https://api.rubyonrails.org/classes/ActionDispatch/Http/UploadedFile.html)
275
+ * [`Shrine::RackFile`](https://shrinerb.com/docs/plugins/rack_file)
276
+ * [`Shrine::DataFile`](https://shrinerb.com/docs/plugins/data_uri)
277
+ * [`Shrine::UploadedFile`](#uploaded-file)
278
+ * [`Down::ChunkedIO`](https://github.com/janko/down#streaming)
279
+ * ...
266
280
 
267
281
  ```rb
268
282
  uploader.upload File.open("/path/to/file", binmode: true) # upload from disk
@@ -278,7 +292,15 @@ uploader.upload Shrine::UploadedFile.new(...) # upload from Shrine
278
292
 
279
293
  The `Shrine::UploadedFile` object represents the file that was uploaded to a
280
294
  storage, and it's what's returned from `Shrine#upload` or when retrieving a
281
- record [attachment]. It contains the following data:
295
+ record [attachment].
296
+
297
+ ```rb
298
+ uploader.upload(file) #=> #<Shrine::UploadedFile ...> (uploader)
299
+ photo.image #=> #<Shrine::UploadedFile ...> (attachment)
300
+ attacher.file #=> #<Shrine::UploadedFile ...> (attacher)
301
+ ```
302
+
303
+ An uploaded file object contains the following data:
282
304
 
283
305
  | Key | Description |
284
306
  | :------- | :---------- |
@@ -287,13 +309,12 @@ record [attachment]. It contains the following data:
287
309
  | `metadata` | file [metadata] that was extracted before upload |
288
310
 
289
311
  ```rb
290
- uploaded_file = uploader.upload(file)
291
312
  uploaded_file #=> #<Shrine::UploadedFile id="949sdjg834.jpg" storage=:store metadata={...}>
292
313
 
293
- uploaded_file.id # => "949sdjg834.jpg"
294
- uploaded_file.storage_key # => :store
295
- uploaded_file.storage # => #<Shrine::Storage::S3>
296
- uploaded_file.metadata # => {...}
314
+ uploaded_file.id #=> "949sdjg834.jpg"
315
+ uploaded_file.storage_key #=> :store
316
+ uploaded_file.storage #=> #<Shrine::Storage::S3>
317
+ uploaded_file.metadata #=> {...}
297
318
  ```
298
319
 
299
320
  It comes with many convenient methods that delegate to the storage:
@@ -341,7 +362,7 @@ The easiest way to attach files is with the `Shrine::Attachment` module:
341
362
  ```rb
342
363
  class Photo < Sequel::Model # ActiveRecord::Base
343
364
  include ImageUploader::Attachment.new(:image) #
344
- include ImageUploader::Attachment[:image] # use a preferred syntax
365
+ include ImageUploader::Attachment[:image] # use your preferred syntax
345
366
  include ImageUploader::Attachment(:image) #
346
367
  end
347
368
  ```
@@ -364,7 +385,7 @@ that attachments are deleted when the record is destroyed.
364
385
  photo.image #=> nil
365
386
 
366
387
  # the assigned file is cached to temporary storage and written to `image_data` column
367
- photo.image = File.open("waterfall.jpg")
388
+ photo.image = File.open("waterfall.jpg", "rb")
368
389
  photo.image #=> #<Shrine::UploadedFile ...>
369
390
  photo.image_url #=> "/uploads/cache/0sdfllasfi842.jpg"
370
391
  photo.image_data #=> '{"id":"0sdfllasfi842.jpg","storage":"cache","metadata":{...}}'
@@ -408,38 +429,36 @@ attacher.url # equivalent to `photo.image_url`
408
429
  ```
409
430
 
410
431
  The attacher is what drives attaching files to model instances; you can use it
411
- as a more explicit alternative to models' attachment interface, or simply when
412
- you need something that's not available through the attachment methods.
432
+ as a more explicit alternative to models' attachment interface, or when you
433
+ need something that's not available through the attachment methods.
413
434
 
414
- You can do things such as change the temporary and permanent storage the
415
- attacher uses, or upload files directly to permanent storage. See the [Using
416
- Attacher] guide for more details.
435
+ See [Using Attacher] guide for more details.
417
436
 
418
437
  ### Temporary storage
419
438
 
420
- Shrine uses temporary storage to support retaining uploaded files across form
421
- redisplays and [direct uploads]. But you can disable this behaviour, and have
422
- files go straight to permanent storage:
439
+ Shrine uses temporary storage to support [file validation][validation] and
440
+ [direct uploads]. If you don't need these features, you can tell Shrine to
441
+ upload files directly to permanent storage:
423
442
 
424
443
  ```rb
425
444
  Shrine.plugin :model, cache: false
426
445
  ```
427
- <!--DOCUSAURUS_CODE_TABS-->
428
- <!--Attachment-->
429
446
  ```rb
430
- photo.image = File.open("waterfall.jpg")
447
+ photo.image = File.open("waterfall.jpg", "rb")
431
448
  photo.image.storage_key #=> :store
432
449
  ```
433
- <!--Attacher-->
450
+
451
+ If you're using the attacher directly, you can just use `Attacher#attach`
452
+ instead of `Attacher#assign`:
453
+
434
454
  ```rb
435
- attacher.attach File.open("waterfall.jpg")
455
+ attacher.attach File.open("waterfall.jpg", "rb")
436
456
  attacher.file.storage_key #=> :store
437
457
  ```
438
- <!--END_DOCUSAURUS_CODE_TABS-->
439
458
 
440
459
  ## Plugin system
441
460
 
442
- By default Shrine comes with a small core which provides only the essential
461
+ By default, Shrine comes with a small core which provides only the essential
443
462
  functionality. All additional features are available via [plugins], which also
444
463
  ship with Shrine. This way you can choose exactly what and how much Shrine does
445
464
  for you, and you load the code only for features that you use.
@@ -497,7 +516,7 @@ gem "marcel", "~> 0.3"
497
516
  Shrine.plugin :determine_mime_type, analyzer: :marcel
498
517
  ```
499
518
  ```rb
500
- photo = Photo.create(image: StringIO.new("<?php ... ?>"))
519
+ photo = Photo.new(image: StringIO.new("<?php ... ?>"))
501
520
  photo.image.mime_type #=> "application/x-php"
502
521
  ```
503
522
 
@@ -510,36 +529,38 @@ the [Extracting Metadata] guide for more details.
510
529
 
511
530
  ## Processing
512
531
 
513
- Shrine allows you to process attached files up front or on-the-fly. For
514
- example, if your app is accepting image uploads, you can generate a predefined
515
- set of of thumbnails when the image is attached to a record, or you can have
516
- thumbnails generated dynamically as they're needed.
532
+ Shrine allows you to process attached files both "eagerly" and "on-the-fly".
533
+ For example, if your app is accepting image uploads, you can generate a
534
+ predefined set of of thumbnails when the image is attached to a record, or you
535
+ can have thumbnails generated dynamically as they're needed.
517
536
 
518
537
  For image processing, it's recommended to use the **[ImageProcessing]** gem,
519
538
  which is a high-level wrapper for processing with
520
539
  [MiniMagick][ImageProcessing::MiniMagick] and [libvips][ImageProcessing::Vips].
521
540
 
522
- ```sh
541
+ ```
523
542
  $ brew install imagemagick vips
524
543
  ```
525
544
 
526
- ### Processing up front
545
+ ### Eager processing
527
546
 
528
- You can use the [`derivatives`][derivatives plugin] plugin to generate a set of
529
- pre-defined processed files:
547
+ We can use the [`derivatives`][derivatives plugin] plugin to generate a
548
+ pre-defined set of processed files (e.g. image thumbnails). We do this by
549
+ registering a derivatives processor block and then explicitly triggering
550
+ creation:
530
551
 
531
552
  ```rb
532
553
  # Gemfile
533
554
  gem "image_processing", "~> 1.8"
534
555
  ```
535
556
  ```rb
536
- Shrine.plugin :derivatives
557
+ Shrine.plugin :derivatives, create_on_promote: true
537
558
  ```
538
559
  ```rb
539
560
  require "image_processing/mini_magick"
540
561
 
541
562
  class ImageUploader < Shrine
542
- Attacher.derivatives_processor do |original|
563
+ Attacher.derivatives do |original|
543
564
  magick = ImageProcessing::MiniMagick.source(original)
544
565
 
545
566
  {
@@ -552,40 +573,34 @@ end
552
573
  ```
553
574
  ```rb
554
575
  photo = Photo.new(image: file)
555
- photo.image_derivatives! # calls derivatives processor and uploads results
556
- photo.save
576
+ photo.save # automatically creates derivatives on promotion
557
577
  ```
558
578
 
559
- If you're allowing the attached file to be updated later on, in your update
560
- route make sure to trigger derivatives creation for new attachments:
579
+ You can then retrieve the URL of a processed derivative:
561
580
 
562
581
  ```rb
563
- photo.image_derivatives! if photo.image_changed?
582
+ photo.image_url(:large) #=> "https://s3.amazonaws.com/path/to/large.jpg"
564
583
  ```
565
584
 
566
- After the processed files are uploaded, their data is saved into the
567
- `<attachment>_data` column. You can then retrieve the derivatives as
568
- [`Shrine::UploadedFile`][uploaded file] objects:
585
+ The derivatives data is stored in the `<attachment>_data` column, and you can
586
+ retrieve them as [`Shrine::UploadedFile`][uploaded file] objects:
569
587
 
570
588
  ```rb
571
- photo.image(:large) #=> #<Shrine::UploadedFile ...>
572
- photo.image(:large).url #=> "/uploads/store/lg043.jpg"
573
- photo.image(:large).size #=> 5825949
574
- photo.image(:large).mime_type #=> "image/jpeg"
589
+ photo.image(:large) #=> #<Shrine::UploadedFile id="path/to/large.jpg" storage=:store metadata={...}>
590
+ photo.image(:large).url #=> "https://s3.amazonaws.com/path/to/large.jpg"
591
+ photo.image(:large).size #=> 5825949
592
+ photo.image(:large).mime_type #=> "image/jpeg"
575
593
  ```
576
594
 
577
- For more details, see the [`derivatives`][derivatives plugin] plugin
578
- documentation and the [File Processing] guide.
595
+ For more details, see the [File Processing] guide and the
596
+ [`derivatives`][derivatives plugin] plugin documentation.
579
597
 
580
- ### Processing on-the-fly
598
+ ### On-the-fly processing
581
599
 
582
600
  On-the-fly processing is provided by the
583
- [`derivation_endpoint`][derivation_endpoint plugin] plugin. It comes with a
584
- [mountable][Mounting Endpoints] Rack app which applies processing on request
585
- and returns processed files.
586
-
587
- To set it up, we mount the Rack app in our router on a chosen path prefix,
588
- configure the plugin with a secret key and that path prefix, and define
601
+ [`derivation_endpoint`][derivation_endpoint plugin] plugin. To set it up, we
602
+ configure the plugin with a secret key and a path prefix, [mount][Mounting
603
+ Endpoints] its Rack app in our routes on the configured path prefix, and define
589
604
  processing we want to perform:
590
605
 
591
606
  ```rb
@@ -593,19 +608,15 @@ processing we want to perform:
593
608
  gem "image_processing", "~> 1.8"
594
609
  ```
595
610
  ```rb
596
- # config/routes.rb (Rails)
597
- Rails.application.routes.draw do
598
- # ...
599
- mount ImageUploader.derivation_endpoint => "/derivations/image"
600
- end
611
+ # config/initializers/rails.rb (Rails)
612
+ # ...
613
+ Shrine.plugin :derivation_endpoint, secret_key: "<YOUR_SECRET_KEY>"
601
614
  ```
602
615
  ```rb
603
616
  require "image_processing/mini_magick"
604
617
 
605
618
  class ImageUploader < Shrine
606
- plugin :derivation_endpoint,
607
- secret_key: "<YOUR SECRET KEY>",
608
- prefix: "derivations/image" # needs to match the mount point in routes
619
+ plugin :derivation_endpoint, prefix: "derivations/image" # matches mount point
609
620
 
610
621
  derivation :thumbnail do |file, width, height|
611
622
  ImageProcessing::MiniMagick
@@ -614,6 +625,13 @@ class ImageUploader < Shrine
614
625
  end
615
626
  end
616
627
  ```
628
+ ```rb
629
+ # config/routes.rb (Rails)
630
+ Rails.application.routes.draw do
631
+ # ...
632
+ mount ImageUploader.derivation_endpoint => "/derivations/image"
633
+ end
634
+ ```
617
635
 
618
636
  Now we can generate URLs from attached files that will perform the desired
619
637
  processing:
@@ -658,36 +676,85 @@ For more details, see the [File Validation] guide and
658
676
 
659
677
  ## Location
660
678
 
661
- Shrine automatically generated random locations before uploading files. By
662
- default the hierarchy is flat, meaning all files are stored in the root
663
- directory of the storage. The [`pretty_location`][pretty_location plugin]
664
- plugin provides a good default hierarchy, but you can also override
665
- `#generate_location` with a custom implementation:
679
+ Shrine automatically generates 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.
682
+
683
+ ```
684
+ 024d9fe83bf4fafb.jpg
685
+ 768a336bf54de219.jpg
686
+ adfaa363629f7fc5.png
687
+ ...
688
+ ```
689
+
690
+ The [`pretty_location`][pretty_location plugin] plugin provides a good default
691
+ hierarchy:
692
+
693
+ ```rb
694
+ Shrine.plugin :pretty_location
695
+ ```
696
+ ```
697
+ user/
698
+ 564/
699
+ avatar/
700
+ aa3e0cd715.jpg
701
+ thumb-493g82jf23.jpg
702
+ photo/
703
+ 123/
704
+ image/
705
+ 13f8a7bc18.png
706
+ thumb-9be62da67e.png
707
+ ...
708
+ ```
709
+
710
+ But you can also override `Shrine#generate_location` with a custom
711
+ implementation, for example:
666
712
 
667
713
  ```rb
668
714
  class ImageUploader < Shrine
669
715
  def generate_location(io, record: nil, derivative: nil, **)
670
- type = record.class.name.downcase if record
671
- style = derivative ? "thumbs" : "originals"
672
- name = super # the default unique identifier
716
+ return super unless record
673
717
 
674
- [type, style, name].compact.join("/")
718
+ table = record.class.table_name
719
+ id = record.id
720
+ prefix = derivative || "original"
721
+
722
+ "uploads/#{table}/#{id}/#{prefix}-#{super}"
675
723
  end
676
724
  end
677
725
  ```
678
- ```plaintext
726
+ ```
679
727
  uploads/
680
728
  photos/
681
- originals/
682
- la98lda74j3g.jpg
683
- thumbs/
684
- 95kd8kafg80a.jpg
685
- ka8agiaf9gk4.jpg
729
+ 123/
730
+ original-afe929b8b4.jpg
731
+ small-ad61f25883.jpg
732
+ medium-41b75c42bb.jpg
733
+ large-73e67abe50.jpg
734
+ ...
686
735
  ```
687
736
 
688
- Note that there should always be a random component in the location, so that
689
- the ORM dirty tracking is detected properly. Inside `#generate_location` you
690
- can also access the extracted metadata through the `:metadata` option.
737
+ > There should always be a random component in the location, so that the ORM
738
+ dirty tracking is detected properly.
739
+
740
+ The `Shrine#generate_location` method contains a lot of useful context for the
741
+ upcoming upload:
742
+
743
+ ```rb
744
+ class ImageUploader < Shrine
745
+ def generate_location(io, record: nil, name: nil, derivative: nil, metadata: {}, **options)
746
+ storage_key #=> :cache, :store, ...
747
+ io #=> #<File>, #<Shrine::UploadedFile>, ...
748
+ record #=> #<Photo>, #<User>, ...
749
+ name #=> :image, :avatar, ...
750
+ derivative #=> :small, :medium, :large, ... (derivatives plugin)
751
+ metadata #=> { "filename" => "nature.jpg", "mime_type" => "image/jpeg", "size" => 18573, ... }
752
+ options #=> { ... other uploader options ... }
753
+
754
+ # ...
755
+ end
756
+ end
757
+ ```
691
758
 
692
759
  ## Direct uploads
693
760
 
@@ -722,7 +789,7 @@ Shrine.plugin :upload_endpoint
722
789
  # config/routes.rb (Rails)
723
790
  Rails.application.routes.draw do
724
791
  # ...
725
- mount ImageUploader.upload_endpoint(:cache) => "/images/upload" # POST /images/upload
792
+ mount Shrine.upload_endpoint(:cache) => "/upload" # POST /upload
726
793
  end
727
794
  ```
728
795
 
@@ -832,7 +899,7 @@ resumable uploads from scratch, it includes a complete JavaScript example
832
899
 
833
900
  ## Backgrounding
834
901
 
835
- The [`backgrounding`][backgrounding plugin] allows you to move file promotion
902
+ The [`backgrounding`][backgrounding plugin] plugin allows you to move file promotion
836
903
  and deletion into a background job, using the backgrounding library [of your
837
904
  choice][Backgrounding Libraries]:
838
905
 
@@ -849,7 +916,7 @@ end
849
916
  class PromoteJob
850
917
  include Sidekiq::Worker
851
918
 
852
- def perform(attacher_class, record_class, record.id, name, file_data)
919
+ def perform(attacher_class, record_class, record_id, name, file_data)
853
920
  attacher_class = Object.const_get(attacher_class)
854
921
  record = Object.const_get(record_class).find(record_id) # if using Active Record
855
922
 
@@ -893,6 +960,8 @@ s3 = Shrine.storages[:cache]
893
960
  s3.clear! { |object| object.last_modified < Time.now - 7*24*60*60 } # delete files older than 1 week
894
961
  ```
895
962
 
963
+ For S3, it may be easier and cheaper to use [S3 bucket lifecycle expiration rules](http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html) instead.
964
+
896
965
  ## Logging
897
966
 
898
967
  The [`instrumentation`][instrumentation plugin] plugin sends and logs events for
@@ -906,7 +975,7 @@ uploaded_file.exists?
906
975
  uploaded_file.download
907
976
  uploaded_file.delete
908
977
  ```
909
- ```plaintext
978
+ ```
910
979
  Metadata (32ms) – {:storage=>:store, :io=>StringIO, :uploader=>Shrine}
911
980
  Upload (1523ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :io=>StringIO, :upload_options=>{}, :uploader=>Shrine}
912
981
  Exists (755ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}
@@ -943,7 +1012,6 @@ In tests you might want to tell Shrine to log only warnings:
943
1012
  Shrine.logger.level = Logger::WARN
944
1013
  ```
945
1014
 
946
- [Advantages of Shrine]: https://shrinerb.com/docs/advantages
947
1015
  [Creating Plugins]: https://shrinerb.com/docs/creating-plugins
948
1016
  [Creating Storages]: https://shrinerb.com/docs/creating-storages
949
1017
  [Direct Uploads to S3]: https://shrinerb.com/docs/direct-s3
@@ -954,24 +1022,19 @@ Shrine.logger.level = Logger::WARN
954
1022
  [Using Attacher]: https://shrinerb.com/docs/attacher
955
1023
  [FileSystem]: https://shrinerb.com/docs/storage/file-system
956
1024
  [S3]: https://shrinerb.com/docs/storage/s3
1025
+ [Memory]: https://shrinerb.com/docs/storage/memory
1026
+ [Testing with Shrine]: https://shrinerb.com/docs/testing
957
1027
  [`Shrine::UploadedFile`]: https://shrinerb.com/rdoc/classes/Shrine/UploadedFile/InstanceMethods.html
958
1028
 
959
1029
  [attacher]: #attacher
960
- [attachment]: #attachment
961
- [backgrounding]: #backgrounding
1030
+ [attachment]: #attaching
962
1031
  [direct uploads]: #direct-uploads
963
1032
  [io abstraction]: #io-abstraction
964
1033
  [location]: #location
965
1034
  [metadata]: #metadata
966
- [up front]: #processing-up-front
967
- [on-the-fly]: #processing-on-the-fly
968
- [plugin system]: #plugin-system
969
- [simple upload]: #simple-direct-upload
970
1035
  [presigned upload]: #presigned-direct-upload
971
- [resumable upload]: #resumable-direct-upload
972
1036
  [storage]: #storage
973
1037
  [uploaded file]: #uploaded-file
974
- [uploading]: #uploading
975
1038
  [uploader]: #uploader
976
1039
  [validation]: #validation
977
1040
 
@@ -1001,10 +1064,12 @@ Shrine.logger.level = Logger::WARN
1001
1064
  [ImageProcessing::MiniMagick]: https://github.com/janko/image_processing/blob/master/doc/minimagick.md#readme
1002
1065
  [ImageProcessing::Vips]: https://github.com/janko/image_processing/blob/master/doc/vips.md#readme
1003
1066
  [`file`]: http://linux.die.net/man/1/file
1067
+ [Down]: https://github.com/janko/down
1004
1068
 
1005
1069
  [activerecord plugin]: https://shrinerb.com/docs/plugins/activerecord
1006
1070
  [add_metadata plugin]: https://shrinerb.com/docs/plugins/add_metadata
1007
1071
  [backgrounding plugin]: https://shrinerb.com/docs/plugins/backgrounding
1072
+ [data_uri plugin]: https://shrinerb.com/docs/plugins/data_uri
1008
1073
  [derivation_endpoint plugin]: https://shrinerb.com/docs/plugins/derivation_endpoint
1009
1074
  [derivatives plugin]: https://shrinerb.com/docs/plugins/derivatives
1010
1075
  [determine_mime_type plugin]: https://shrinerb.com/docs/plugins/determine_mime_type