shrine 3.0.0 → 3.2.2

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.

Potentially problematic release.


This version of shrine might be problematic. Click here for more details.

Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -33
  3. data/LICENSE.txt +1 -1
  4. data/README.md +94 -4
  5. data/doc/advantages.md +35 -18
  6. data/doc/attacher.md +16 -17
  7. data/doc/carrierwave.md +75 -34
  8. data/doc/changing_derivatives.md +39 -39
  9. data/doc/design.md +134 -85
  10. data/doc/external/articles.md +56 -41
  11. data/doc/external/extensions.md +38 -34
  12. data/doc/getting_started.md +182 -112
  13. data/doc/metadata.md +79 -43
  14. data/doc/multiple_files.md +5 -3
  15. data/doc/paperclip.md +110 -42
  16. data/doc/plugins/activerecord.md +5 -5
  17. data/doc/plugins/add_metadata.md +92 -35
  18. data/doc/plugins/backgrounding.md +12 -2
  19. data/doc/plugins/column.md +36 -7
  20. data/doc/plugins/data_uri.md +2 -2
  21. data/doc/plugins/default_url.md +6 -3
  22. data/doc/plugins/derivation_endpoint.md +26 -28
  23. data/doc/plugins/derivatives.md +205 -169
  24. data/doc/plugins/determine_mime_type.md +2 -2
  25. data/doc/plugins/entity.md +3 -3
  26. data/doc/plugins/form_assign.md +5 -5
  27. data/doc/plugins/included.md +25 -5
  28. data/doc/plugins/infer_extension.md +2 -2
  29. data/doc/plugins/instrumentation.md +1 -1
  30. data/doc/plugins/metadata_attributes.md +21 -10
  31. data/doc/plugins/model.md +4 -4
  32. data/doc/plugins/persistence.md +1 -0
  33. data/doc/plugins/refresh_metadata.md +5 -4
  34. data/doc/plugins/remote_url.md +8 -3
  35. data/doc/plugins/remove_invalid.md +9 -1
  36. data/doc/plugins/sequel.md +4 -4
  37. data/doc/plugins/signature.md +11 -2
  38. data/doc/plugins/store_dimensions.md +2 -2
  39. data/doc/plugins/type_predicates.md +96 -0
  40. data/doc/plugins/upload_endpoint.md +7 -11
  41. data/doc/plugins/upload_options.md +1 -1
  42. data/doc/plugins/url_options.md +2 -2
  43. data/doc/plugins/validation.md +14 -4
  44. data/doc/plugins/validation_helpers.md +3 -3
  45. data/doc/plugins/versions.md +11 -11
  46. data/doc/processing.md +289 -125
  47. data/doc/refile.md +39 -18
  48. data/doc/release_notes/2.19.0.md +1 -1
  49. data/doc/release_notes/3.0.0.md +275 -258
  50. data/doc/release_notes/3.0.1.md +22 -0
  51. data/doc/release_notes/3.1.0.md +73 -0
  52. data/doc/release_notes/3.2.0.md +96 -0
  53. data/doc/release_notes/3.2.1.md +32 -0
  54. data/doc/release_notes/3.2.2.md +14 -0
  55. data/doc/securing_uploads.md +3 -3
  56. data/doc/storage/file_system.md +1 -1
  57. data/doc/storage/memory.md +19 -0
  58. data/doc/storage/s3.md +105 -86
  59. data/doc/testing.md +2 -2
  60. data/doc/upgrading_to_3.md +115 -33
  61. data/doc/validation.md +3 -2
  62. data/lib/shrine.rb +8 -8
  63. data/lib/shrine/attacher.rb +19 -14
  64. data/lib/shrine/attachment.rb +5 -5
  65. data/lib/shrine/plugins.rb +22 -0
  66. data/lib/shrine/plugins/add_metadata.rb +12 -3
  67. data/lib/shrine/plugins/default_storage.rb +6 -6
  68. data/lib/shrine/plugins/default_url.rb +1 -1
  69. data/lib/shrine/plugins/derivation_endpoint.rb +10 -6
  70. data/lib/shrine/plugins/derivatives.rb +19 -17
  71. data/lib/shrine/plugins/determine_mime_type.rb +3 -3
  72. data/lib/shrine/plugins/entity.rb +6 -6
  73. data/lib/shrine/plugins/metadata_attributes.rb +1 -1
  74. data/lib/shrine/plugins/model.rb +3 -3
  75. data/lib/shrine/plugins/presign_endpoint.rb +2 -2
  76. data/lib/shrine/plugins/pretty_location.rb +1 -1
  77. data/lib/shrine/plugins/processing.rb +1 -1
  78. data/lib/shrine/plugins/refresh_metadata.rb +2 -2
  79. data/lib/shrine/plugins/remote_url.rb +3 -3
  80. data/lib/shrine/plugins/remove_invalid.rb +10 -5
  81. data/lib/shrine/plugins/signature.rb +7 -6
  82. data/lib/shrine/plugins/store_dimensions.rb +18 -9
  83. data/lib/shrine/plugins/type_predicates.rb +113 -0
  84. data/lib/shrine/plugins/upload_endpoint.rb +3 -3
  85. data/lib/shrine/plugins/upload_options.rb +2 -2
  86. data/lib/shrine/plugins/url_options.rb +2 -2
  87. data/lib/shrine/plugins/validation.rb +9 -7
  88. data/lib/shrine/storage/linter.rb +4 -4
  89. data/lib/shrine/storage/s3.rb +62 -38
  90. data/lib/shrine/uploaded_file.rb +5 -1
  91. data/lib/shrine/version.rb +2 -2
  92. data/shrine.gemspec +6 -7
  93. metadata +23 -29
@@ -4,43 +4,47 @@ 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-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-color](https://github.com/jnylen/shrine-color) | Plugin for finding dominant color in an image |
32
+ | [shrine-configurable_storage](https://github.com/SleeplessByte/shrine-configurable_storage) | Plugin for lazy storage registration |
33
+ | [shrine-content_addressable](https://github.com/SleeplessByte/shrine-content_addressable) | Plugin for generating content addressable locations |
34
+ | [shrine-imgix](https://github.com/shrinerb/shrine-imgix) | Plugin for [Imgix](https://www.imgix.com/) |
35
+ | [shrine-transloadit](https://github.com/shrinerb/shrine-transloadit) | Plugin for [Transloadit](https://transloadit.com/) |
36
+ | [shrine-lambda](https://github.com/texpert/shrine-lambda) | Plugin for [AWS Lambda](https://aws.amazon.com/lambda/) |
37
+ | [hanami-shrine](https://github.com/katafrakt/hanami-shrine) | Plugin for [Hanami](https://hanamirb.org/) |
38
+ | [shrine-mongoid](https://github.com/shrinerb/shrine-mongoid) | Plugin for [Mongoid](https://mongoid.org) |
39
+ | [shrine-rails](https://github.com/abepetrillo/shrine-rails) | Plugin for [Rails](https://rubyonrails.org/) |
40
+ | [shrine-rom](https://github.com/shrinerb/shrine-rom) | Plugin for [ROM](https://rom-rb.org/) |
41
+ | [shrine-tus](https://github.com/shrinerb/shrine-tus) | Plugin for [tus](https://tus.io) server integration |
40
42
 
41
43
  ## Libraries
42
44
 
43
- * [ckeditor](https://github.com/galetahub/ckeditor)
44
- * [imgproxy](https://github.com/imgproxy/imgproxy.rb)
45
- * [rails_admin](https://github.com/sferik/rails_admin)
46
- * [uppy-s3_multipart](https://github.com/janko/uppy-s3_multipart)
45
+ | Gem | Description |
46
+ | :----- | :------- |
47
+ | [ckeditor](https://github.com/galetahub/ckeditor) | Integration for [CKEditor](https://ckeditor.com/ckeditor-4/) |
48
+ | [imgproxy](https://github.com/imgproxy/imgproxy.rb) | Integration for [imgproxy](https://github.com/imgproxy/imgproxy) |
49
+ | [rails_admin](https://github.com/sferik/rails_admin) | Integration for [RailsAdmin](https://github.com/sferik/rails_admin) |
50
+ | [uppy-s3_multipart](https://github.com/janko/uppy-s3_multipart) | Integration for [Uppy AWS S3 Multipart](https://uppy.io/docs/aws-s3-multipart/) |
@@ -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 information:
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,12 +309,12 @@ record [attachment]. It contains the following information:
287
309
  | `metadata` | file [metadata] that was extracted before upload |
288
310
 
289
311
  ```rb
290
- uploaded_file = uploader.upload(file)
291
- uploaded_file.data #=> {"id"=>"949sdjg834.jpg","storage"=>"store","metadata"=>{...}}
312
+ uploaded_file #=> #<Shrine::UploadedFile id="949sdjg834.jpg" storage=:store metadata={...}>
292
313
 
293
- uploaded_file.id #=> "949sdjg834.jpg"
294
- uploaded_file.storage #=> #<Shrine::Storage::S3>
295
- 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 #=> {...}
296
318
  ```
297
319
 
298
320
  It comes with many convenient methods that delegate to the storage:
@@ -340,7 +362,7 @@ The easiest way to attach files is with the `Shrine::Attachment` module:
340
362
  ```rb
341
363
  class Photo < Sequel::Model # ActiveRecord::Base
342
364
  include ImageUploader::Attachment.new(:image) #
343
- include ImageUploader::Attachment[:image] # use a preferred syntax
365
+ include ImageUploader::Attachment[:image] # use your preferred syntax
344
366
  include ImageUploader::Attachment(:image) #
345
367
  end
346
368
  ```
@@ -363,14 +385,14 @@ that attachments are deleted when the record is destroyed.
363
385
  photo.image #=> nil
364
386
 
365
387
  # the assigned file is cached to temporary storage and written to `image_data` column
366
- photo.image = File.open("waterfall.jpg")
367
- photo.image #=> #<Shrine::UploadedFile @data={...}>
388
+ photo.image = File.open("waterfall.jpg", "rb")
389
+ photo.image #=> #<Shrine::UploadedFile ...>
368
390
  photo.image_url #=> "/uploads/cache/0sdfllasfi842.jpg"
369
391
  photo.image_data #=> '{"id":"0sdfllasfi842.jpg","storage":"cache","metadata":{...}}'
370
392
 
371
393
  # the cached file is promoted to permanent storage and saved to `image_data` column
372
394
  photo.save
373
- photo.image #=> #<Shrine::UploadedFile @data={...}>
395
+ photo.image #=> #<Shrine::UploadedFile ...>
374
396
  photo.image_url #=> "/uploads/store/l02kladf8jlda.jpg"
375
397
  photo.image_data #=> '{"id":"l02kladf8jlda.jpg","storage":"store","metadata":{...}}'
376
398
 
@@ -407,38 +429,36 @@ attacher.url # equivalent to `photo.image_url`
407
429
  ```
408
430
 
409
431
  The attacher is what drives attaching files to model instances; you can use it
410
- as a more explicit alternative to models' attachment interface, or simply when
411
- 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.
412
434
 
413
- You can do things such as change the temporary and permanent storage the
414
- attacher uses, or upload files directly to permanent storage. See the [Using
415
- Attacher] guide for more details.
435
+ See [Using Attacher] guide for more details.
416
436
 
417
437
  ### Temporary storage
418
438
 
419
- Shrine uses temporary storage to support retaining uploaded files across form
420
- redisplays and [direct uploads]. But you can disable this behaviour, and have
421
- 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:
422
442
 
423
443
  ```rb
424
444
  Shrine.plugin :model, cache: false
425
445
  ```
426
- <!--DOCUSAURUS_CODE_TABS-->
427
- <!--Attachment-->
428
446
  ```rb
429
- photo.image = File.open("waterfall.jpg")
447
+ photo.image = File.open("waterfall.jpg", "rb")
430
448
  photo.image.storage_key #=> :store
431
449
  ```
432
- <!--Attacher-->
450
+
451
+ If you're using the attacher directly, you can just use `Attacher#attach`
452
+ instead of `Attacher#assign`:
453
+
433
454
  ```rb
434
- attacher.attach File.open("waterfall.jpg")
455
+ attacher.attach File.open("waterfall.jpg", "rb")
435
456
  attacher.file.storage_key #=> :store
436
457
  ```
437
- <!--END_DOCUSAURUS_CODE_TABS-->
438
458
 
439
459
  ## Plugin system
440
460
 
441
- 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
442
462
  functionality. All additional features are available via [plugins], which also
443
463
  ship with Shrine. This way you can choose exactly what and how much Shrine does
444
464
  for you, and you load the code only for features that you use.
@@ -496,7 +516,7 @@ gem "marcel", "~> 0.3"
496
516
  Shrine.plugin :determine_mime_type, analyzer: :marcel
497
517
  ```
498
518
  ```rb
499
- photo = Photo.create(image: StringIO.new("<?php ... ?>"))
519
+ photo = Photo.new(image: StringIO.new("<?php ... ?>"))
500
520
  photo.image.mime_type #=> "application/x-php"
501
521
  ```
502
522
 
@@ -509,23 +529,25 @@ the [Extracting Metadata] guide for more details.
509
529
 
510
530
  ## Processing
511
531
 
512
- Shrine allows you to process attached files up front or on-the-fly. For
513
- example, if your app is accepting image uploads, you can generate a predefined
514
- set of of thumbnails when the image is attached to a record, or you can have
515
- 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.
516
536
 
517
537
  For image processing, it's recommended to use the **[ImageProcessing]** gem,
518
538
  which is a high-level wrapper for processing with
519
539
  [MiniMagick][ImageProcessing::MiniMagick] and [libvips][ImageProcessing::Vips].
520
540
 
521
- ```sh
541
+ ```
522
542
  $ brew install imagemagick vips
523
543
  ```
524
544
 
525
- ### Processing up front
545
+ ### Eager processing
526
546
 
527
- You can use the [`derivatives`][derivatives plugin] plugin to generate a set of
528
- 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:
529
551
 
530
552
  ```rb
531
553
  # Gemfile
@@ -538,7 +560,7 @@ Shrine.plugin :derivatives
538
560
  require "image_processing/mini_magick"
539
561
 
540
562
  class ImageUploader < Shrine
541
- Attacher.derivatives_processor do |original|
563
+ Attacher.derivatives do |original|
542
564
  magick = ImageProcessing::MiniMagick.source(original)
543
565
 
544
566
  {
@@ -551,40 +573,38 @@ end
551
573
  ```
552
574
  ```rb
553
575
  photo = Photo.new(image: file)
554
- photo.image_derivatives! # calls derivatives processor and uploads results
555
- photo.save
576
+
577
+ if photo.valid?
578
+ photo.image_derivatives! if photo.image_changed? # create derivatives
579
+ photo.save
580
+ end
556
581
  ```
557
582
 
558
- If you're allowing the attached file to be updated later on, in your update
559
- route make sure to trigger derivatives creation for new attachments:
583
+ You can then retrieve the URL of a processed derivative:
560
584
 
561
585
  ```rb
562
- photo.image_derivatives! if photo.image_changed?
586
+ photo.image_url(:large) #=> "https://s3.amazonaws.com/path/to/large.jpg"
563
587
  ```
564
588
 
565
- After the processed files are uploaded, their data is saved into the
566
- `<attachment>_data` column. You can then retrieve the derivatives as
567
- [`Shrine::UploadedFile`][uploaded file] objects:
589
+ The derivatives data is stored in the `<attachment>_data` column, and you can
590
+ retrieve them as [`Shrine::UploadedFile`][uploaded file] objects:
568
591
 
569
592
  ```rb
570
- photo.image(:large) #=> #<Shrine::UploadedFile ...>
571
- photo.image(:large).url #=> "/uploads/store/lg043.jpg"
572
- photo.image(:large).size #=> 5825949
573
- photo.image(:large).mime_type #=> "image/jpeg"
593
+ photo.image(:large) #=> #<Shrine::UploadedFile id="path/to/large.jpg" storage=:store metadata={...}>
594
+ photo.image(:large).url #=> "https://s3.amazonaws.com/path/to/large.jpg"
595
+ photo.image(:large).size #=> 5825949
596
+ photo.image(:large).mime_type #=> "image/jpeg"
574
597
  ```
575
598
 
576
- For more details, see the [`derivatives`][derivatives plugin] plugin
577
- documentation and the [File Processing] guide.
599
+ For more details, see the [File Processing] guide and the
600
+ [`derivatives`][derivatives plugin] plugin documentation.
578
601
 
579
- ### Processing on-the-fly
602
+ ### On-the-fly processing
580
603
 
581
604
  On-the-fly processing is provided by the
582
- [`derivation_endpoint`][derivation_endpoint plugin] plugin. It comes with a
583
- [mountable][Mounting Endpoints] Rack app which applies processing on request
584
- and returns processed files.
585
-
586
- To set it up, we mount the Rack app in our router on a chosen path prefix,
587
- configure the plugin with a secret key and that path prefix, and define
605
+ [`derivation_endpoint`][derivation_endpoint plugin] plugin. To set it up, we
606
+ configure the plugin with a secret key and a path prefix, [mount][Mounting
607
+ Endpoints] its Rack app in our routes on the configured path prefix, and define
588
608
  processing we want to perform:
589
609
 
590
610
  ```rb
@@ -592,19 +612,15 @@ processing we want to perform:
592
612
  gem "image_processing", "~> 1.8"
593
613
  ```
594
614
  ```rb
595
- # config/routes.rb (Rails)
596
- Rails.application.routes.draw do
597
- # ...
598
- mount ImageUploader.derivation_endpoint => "/derivations/image"
599
- end
615
+ # config/initializers/rails.rb (Rails)
616
+ # ...
617
+ Shrine.plugin :derivation_endpoint, secret_key: "<YOUR_SECRET_KEY>"
600
618
  ```
601
619
  ```rb
602
620
  require "image_processing/mini_magick"
603
621
 
604
622
  class ImageUploader < Shrine
605
- plugin :derivation_endpoint,
606
- secret_key: "<YOUR SECRET KEY>",
607
- prefix: "derivations/image" # needs to match the mount point in routes
623
+ plugin :derivation_endpoint, prefix: "derivations/image" # matches mount point
608
624
 
609
625
  derivation :thumbnail do |file, width, height|
610
626
  ImageProcessing::MiniMagick
@@ -613,6 +629,13 @@ class ImageUploader < Shrine
613
629
  end
614
630
  end
615
631
  ```
632
+ ```rb
633
+ # config/routes.rb (Rails)
634
+ Rails.application.routes.draw do
635
+ # ...
636
+ mount ImageUploader.derivation_endpoint => "/derivations/image"
637
+ end
638
+ ```
616
639
 
617
640
  Now we can generate URLs from attached files that will perform the desired
618
641
  processing:
@@ -657,36 +680,85 @@ For more details, see the [File Validation] guide and
657
680
 
658
681
  ## Location
659
682
 
660
- Shrine automatically generated random locations before uploading files. By
661
- default the hierarchy is flat, meaning all files are stored in the root
662
- directory of the storage. The [`pretty_location`][pretty_location plugin]
663
- plugin provides a good default hierarchy, but you can also override
664
- `#generate_location` with a custom implementation:
683
+ Shrine automatically generates random locations before uploading files. By
684
+ default, the hierarchy is flat, meaning all files are stored in the root
685
+ directory of the storage.
686
+
687
+ ```
688
+ 024d9fe83bf4fafb.jpg
689
+ 768a336bf54de219.jpg
690
+ adfaa363629f7fc5.png
691
+ ...
692
+ ```
693
+
694
+ The [`pretty_location`][pretty_location plugin] plugin provides a good default
695
+ hierarchy:
696
+
697
+ ```rb
698
+ Shrine.plugin :pretty_location
699
+ ```
700
+ ```
701
+ user/
702
+ 564/
703
+ avatar/
704
+ aa3e0cd715.jpg
705
+ thumb-493g82jf23.jpg
706
+ photo/
707
+ 123/
708
+ image/
709
+ 13f8a7bc18.png
710
+ thumb-9be62da67e.png
711
+ ...
712
+ ```
713
+
714
+ Buy you can also override `Shrine#generate_location` with a custom
715
+ implementation, for example:
665
716
 
666
717
  ```rb
667
718
  class ImageUploader < Shrine
668
719
  def generate_location(io, record: nil, derivative: nil, **)
669
- type = record.class.name.downcase if record
670
- style = derivative ? "thumbs" : "originals"
671
- name = super # the default unique identifier
720
+ return super unless record
721
+
722
+ table = record.class.table_name
723
+ id = record.id
724
+ prefix = derivative || "original"
672
725
 
673
- [type, style, name].compact.join("/")
726
+ "uploads/#{table}/#{id}/#{prefix}-#{super}"
674
727
  end
675
728
  end
676
729
  ```
677
- ```plaintext
730
+ ```
678
731
  uploads/
679
732
  photos/
680
- originals/
681
- la98lda74j3g.jpg
682
- thumbs/
683
- 95kd8kafg80a.jpg
684
- ka8agiaf9gk4.jpg
733
+ 123/
734
+ original-afe929b8b4.jpg
735
+ small-ad61f25883.jpg
736
+ medium-41b75c42bb.jpg
737
+ large-73e67abe50.jpg
738
+ ...
685
739
  ```
686
740
 
687
- Note that there should always be a random component in the location, so that
688
- the ORM dirty tracking is detected properly. Inside `#generate_location` you
689
- can also access the extracted metadata through the `:metadata` option.
741
+ > There should always be a random component in the location, so that the ORM
742
+ dirty tracking is detected properly.
743
+
744
+ The `Shrine#generate_location` method contains a lot of useful context for the
745
+ upcoming upload:
746
+
747
+ ```rb
748
+ class ImageUploader < Shrine
749
+ def generate_location(io, record: nil, name: nil, derivative: nil, metadata: {}, **options)
750
+ storage_key #=> :cache, :store, ...
751
+ io #=> #<File>, #<Shrine::UploadedFile>, ...
752
+ record #=> #<Photo>, #<User>, ...
753
+ name #=> :image, :avatar, ...
754
+ derivative #=> :small, :medium, :large, ... (derivatives plugin)
755
+ metadata #=> { "filename" => "nature.jpg", "mime_type" => "image/jpeg", "size" => 18573, ... }
756
+ options #=> { ... other uploader options ... }
757
+
758
+ # ...
759
+ end
760
+ end
761
+ ```
690
762
 
691
763
  ## Direct uploads
692
764
 
@@ -721,7 +793,7 @@ Shrine.plugin :upload_endpoint
721
793
  # config/routes.rb (Rails)
722
794
  Rails.application.routes.draw do
723
795
  # ...
724
- mount ImageUploader.upload_endpoint(:cache) => "/images/upload" # POST /images/upload
796
+ mount Shrine.upload_endpoint(:cache) => "/upload" # POST /upload
725
797
  end
726
798
  ```
727
799
 
@@ -848,7 +920,7 @@ end
848
920
  class PromoteJob
849
921
  include Sidekiq::Worker
850
922
 
851
- def perform(attacher_class, record_class, record.id, name, file_data)
923
+ def perform(attacher_class, record_class, record_id, name, file_data)
852
924
  attacher_class = Object.const_get(attacher_class)
853
925
  record = Object.const_get(record_class).find(record_id) # if using Active Record
854
926
 
@@ -892,6 +964,8 @@ s3 = Shrine.storages[:cache]
892
964
  s3.clear! { |object| object.last_modified < Time.now - 7*24*60*60 } # delete files older than 1 week
893
965
  ```
894
966
 
967
+ 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.
968
+
895
969
  ## Logging
896
970
 
897
971
  The [`instrumentation`][instrumentation plugin] plugin sends and logs events for
@@ -905,7 +979,7 @@ uploaded_file.exists?
905
979
  uploaded_file.download
906
980
  uploaded_file.delete
907
981
  ```
908
- ```plaintext
982
+ ```
909
983
  Metadata (32ms) – {:storage=>:store, :io=>StringIO, :uploader=>Shrine}
910
984
  Upload (1523ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :io=>StringIO, :upload_options=>{}, :uploader=>Shrine}
911
985
  Exists (755ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}
@@ -942,7 +1016,6 @@ In tests you might want to tell Shrine to log only warnings:
942
1016
  Shrine.logger.level = Logger::WARN
943
1017
  ```
944
1018
 
945
- [Advantages of Shrine]: https://shrinerb.com/docs/advantages
946
1019
  [Creating Plugins]: https://shrinerb.com/docs/creating-plugins
947
1020
  [Creating Storages]: https://shrinerb.com/docs/creating-storages
948
1021
  [Direct Uploads to S3]: https://shrinerb.com/docs/direct-s3
@@ -953,24 +1026,19 @@ Shrine.logger.level = Logger::WARN
953
1026
  [Using Attacher]: https://shrinerb.com/docs/attacher
954
1027
  [FileSystem]: https://shrinerb.com/docs/storage/file-system
955
1028
  [S3]: https://shrinerb.com/docs/storage/s3
1029
+ [Memory]: https://shrinerb.com/docs/storage/memory
1030
+ [Testing with Shrine]: https://shrinerb.com/docs/testing
956
1031
  [`Shrine::UploadedFile`]: https://shrinerb.com/rdoc/classes/Shrine/UploadedFile/InstanceMethods.html
957
1032
 
958
1033
  [attacher]: #attacher
959
- [attachment]: #attachment
960
- [backgrounding]: #backgrounding
1034
+ [attachment]: #attaching
961
1035
  [direct uploads]: #direct-uploads
962
1036
  [io abstraction]: #io-abstraction
963
1037
  [location]: #location
964
1038
  [metadata]: #metadata
965
- [up front]: #processing-up-front
966
- [on-the-fly]: #processing-on-the-fly
967
- [plugin system]: #plugin-system
968
- [simple upload]: #simple-direct-upload
969
1039
  [presigned upload]: #presigned-direct-upload
970
- [resumable upload]: #resumable-direct-upload
971
1040
  [storage]: #storage
972
1041
  [uploaded file]: #uploaded-file
973
- [uploading]: #uploading
974
1042
  [uploader]: #uploader
975
1043
  [validation]: #validation
976
1044
 
@@ -1000,10 +1068,12 @@ Shrine.logger.level = Logger::WARN
1000
1068
  [ImageProcessing::MiniMagick]: https://github.com/janko/image_processing/blob/master/doc/minimagick.md#readme
1001
1069
  [ImageProcessing::Vips]: https://github.com/janko/image_processing/blob/master/doc/vips.md#readme
1002
1070
  [`file`]: http://linux.die.net/man/1/file
1071
+ [Down]: https://github.com/janko/down
1003
1072
 
1004
1073
  [activerecord plugin]: https://shrinerb.com/docs/plugins/activerecord
1005
1074
  [add_metadata plugin]: https://shrinerb.com/docs/plugins/add_metadata
1006
1075
  [backgrounding plugin]: https://shrinerb.com/docs/plugins/backgrounding
1076
+ [data_uri plugin]: https://shrinerb.com/docs/plugins/data_uri
1007
1077
  [derivation_endpoint plugin]: https://shrinerb.com/docs/plugins/derivation_endpoint
1008
1078
  [derivatives plugin]: https://shrinerb.com/docs/plugins/derivatives
1009
1079
  [determine_mime_type plugin]: https://shrinerb.com/docs/plugins/determine_mime_type