shrine 3.0.0.rc → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -66
- data/README.md +39 -1061
- data/doc/advantages.md +151 -148
- data/doc/attacher.md +12 -30
- data/doc/carrierwave.md +150 -115
- data/doc/changing_derivatives.md +5 -11
- data/doc/changing_location.md +8 -5
- data/doc/changing_storage.md +5 -2
- data/doc/creating_persistence_plugins.md +9 -6
- data/doc/creating_plugins.md +42 -22
- data/doc/creating_storages.md +4 -1
- data/doc/design.md +7 -5
- data/doc/direct_s3.md +9 -4
- data/doc/external/articles.md +50 -0
- data/doc/external/extensions.md +46 -0
- data/doc/external/misc.md +17 -0
- data/doc/getting_started.md +1038 -0
- data/doc/metadata.md +5 -3
- data/doc/multiple_files.md +55 -29
- data/doc/paperclip.md +206 -163
- data/doc/plugins/activerecord.md +26 -6
- data/doc/plugins/add_metadata.md +4 -2
- data/doc/plugins/atomic_helpers.md +4 -2
- data/doc/plugins/backgrounding.md +83 -44
- data/doc/plugins/cached_attachment_data.md +4 -2
- data/doc/plugins/column.md +4 -2
- data/doc/plugins/data_uri.md +10 -6
- data/doc/plugins/default_storage.md +5 -3
- data/doc/plugins/default_url.md +4 -2
- data/doc/plugins/delete_raw.md +4 -2
- data/doc/plugins/derivation_endpoint.md +63 -39
- data/doc/plugins/derivatives.md +13 -50
- data/doc/plugins/determine_mime_type.md +6 -4
- data/doc/plugins/download_endpoint.md +6 -3
- data/doc/plugins/dynamic_storage.md +4 -2
- data/doc/plugins/entity.md +6 -4
- data/doc/plugins/form_assign.md +4 -2
- data/doc/plugins/included.md +4 -2
- data/doc/plugins/infer_extension.md +6 -4
- data/doc/plugins/instrumentation.md +5 -3
- data/doc/plugins/keep_files.md +9 -2
- data/doc/plugins/metadata_attributes.md +5 -3
- data/doc/plugins/mirroring.md +4 -2
- data/doc/plugins/model.md +6 -4
- data/doc/plugins/module_include.md +4 -2
- data/doc/plugins/multi_cache.md +4 -2
- data/doc/plugins/persistence.md +5 -3
- data/doc/plugins/presign_endpoint.md +6 -2
- data/doc/plugins/pretty_location.md +5 -3
- data/doc/plugins/processing.md +4 -2
- data/doc/plugins/rack_file.md +8 -2
- data/doc/plugins/rack_response.md +6 -2
- data/doc/plugins/recache.md +4 -2
- data/doc/plugins/refresh_metadata.md +5 -3
- data/doc/plugins/remote_url.md +26 -5
- data/doc/plugins/remove_attachment.md +4 -2
- data/doc/plugins/remove_invalid.md +10 -2
- data/doc/plugins/restore_cached_data.md +9 -3
- data/doc/plugins/sequel.md +26 -6
- data/doc/plugins/signature.md +6 -4
- data/doc/plugins/store_dimensions.md +6 -4
- data/doc/plugins/tempfile.md +4 -2
- data/doc/plugins/upload_endpoint.md +6 -2
- data/doc/plugins/upload_options.md +6 -4
- data/doc/plugins/url_options.md +4 -2
- data/doc/plugins/validation.md +7 -3
- data/doc/plugins/validation_helpers.md +13 -10
- data/doc/plugins/versions.md +4 -8
- data/doc/processing.md +27 -9
- data/doc/refile.md +119 -127
- data/doc/release_notes/1.0.0.md +4 -0
- data/doc/release_notes/1.1.0.md +4 -0
- data/doc/release_notes/1.2.0.md +4 -0
- data/doc/release_notes/1.3.0.md +4 -0
- data/doc/release_notes/1.4.0.md +4 -0
- data/doc/release_notes/1.4.1.md +4 -0
- data/doc/release_notes/1.4.2.md +4 -0
- data/doc/release_notes/2.0.0.md +4 -0
- data/doc/release_notes/2.0.1.md +4 -0
- data/doc/release_notes/2.1.0.md +4 -0
- data/doc/release_notes/2.1.1.md +4 -0
- data/doc/release_notes/2.10.0.md +4 -0
- data/doc/release_notes/2.10.1.md +4 -0
- data/doc/release_notes/2.11.0.md +4 -0
- data/doc/release_notes/2.12.0.md +4 -0
- data/doc/release_notes/2.13.0.md +4 -0
- data/doc/release_notes/2.14.0.md +5 -1
- data/doc/release_notes/2.15.0.md +10 -6
- data/doc/release_notes/2.16.0.md +4 -0
- data/doc/release_notes/2.17.0.md +4 -0
- data/doc/release_notes/2.18.0.md +4 -0
- data/doc/release_notes/2.19.0.md +7 -4
- data/doc/release_notes/2.2.0.md +4 -0
- data/doc/release_notes/2.3.0.md +4 -0
- data/doc/release_notes/2.3.1.md +4 -0
- data/doc/release_notes/2.4.0.md +4 -0
- data/doc/release_notes/2.4.1.md +4 -0
- data/doc/release_notes/2.5.0.md +4 -0
- data/doc/release_notes/2.6.0.md +4 -0
- data/doc/release_notes/2.6.1.md +4 -0
- data/doc/release_notes/2.7.0.md +4 -0
- data/doc/release_notes/2.8.0.md +4 -0
- data/doc/release_notes/2.9.0.md +4 -0
- data/doc/release_notes/3.0.0.md +120 -38
- data/doc/retrieving_uploads.md +4 -1
- data/doc/securing_uploads.md +4 -1
- data/doc/storage/file_system.md +12 -4
- data/doc/storage/s3.md +4 -2
- data/doc/testing.md +27 -41
- data/doc/upgrading_to_3.md +105 -26
- data/doc/validation.md +8 -6
- data/lib/shrine/attacher.rb +2 -2
- data/lib/shrine/attachment.rb +7 -10
- data/lib/shrine/plugins/activerecord.rb +10 -10
- data/lib/shrine/plugins/add_metadata.rb +1 -3
- data/lib/shrine/plugins/atomic_helpers.rb +6 -8
- data/lib/shrine/plugins/backgrounding.rb +4 -6
- data/lib/shrine/plugins/cached_attachment_data.rb +1 -3
- data/lib/shrine/plugins/column.rb +2 -4
- data/lib/shrine/plugins/data_uri.rb +1 -3
- data/lib/shrine/plugins/default_storage.rb +1 -3
- data/lib/shrine/plugins/default_url.rb +1 -3
- data/lib/shrine/plugins/delete_raw.rb +1 -3
- data/lib/shrine/plugins/derivation_endpoint.rb +3 -4
- data/lib/shrine/plugins/derivatives.rb +2 -4
- data/lib/shrine/plugins/determine_mime_type.rb +1 -3
- data/lib/shrine/plugins/download_endpoint.rb +1 -3
- data/lib/shrine/plugins/dynamic_storage.rb +1 -3
- data/lib/shrine/plugins/entity.rb +25 -9
- data/lib/shrine/plugins/form_assign.rb +1 -3
- data/lib/shrine/plugins/included.rb +1 -3
- data/lib/shrine/plugins/infer_extension.rb +1 -3
- data/lib/shrine/plugins/instrumentation.rb +1 -3
- data/lib/shrine/plugins/keep_files.rb +1 -3
- data/lib/shrine/plugins/metadata_attributes.rb +1 -3
- data/lib/shrine/plugins/mirroring.rb +2 -1
- data/lib/shrine/plugins/model.rb +2 -4
- data/lib/shrine/plugins/module_include.rb +1 -3
- data/lib/shrine/plugins/multi_cache.rb +3 -3
- data/lib/shrine/plugins/presign_endpoint.rb +1 -3
- data/lib/shrine/plugins/pretty_location.rb +1 -3
- data/lib/shrine/plugins/processing.rb +1 -3
- data/lib/shrine/plugins/rack_file.rb +1 -3
- data/lib/shrine/plugins/rack_response.rb +1 -3
- data/lib/shrine/plugins/recache.rb +1 -3
- data/lib/shrine/plugins/refresh_metadata.rb +1 -3
- data/lib/shrine/plugins/remote_url.rb +1 -3
- data/lib/shrine/plugins/remove_attachment.rb +1 -3
- data/lib/shrine/plugins/remove_invalid.rb +1 -3
- data/lib/shrine/plugins/restore_cached_data.rb +1 -3
- data/lib/shrine/plugins/sequel.rb +10 -12
- data/lib/shrine/plugins/signature.rb +1 -3
- data/lib/shrine/plugins/store_dimensions.rb +1 -3
- data/lib/shrine/plugins/tempfile.rb +1 -3
- data/lib/shrine/plugins/upload_endpoint.rb +1 -3
- data/lib/shrine/plugins/upload_options.rb +1 -3
- data/lib/shrine/plugins/url_options.rb +1 -3
- data/lib/shrine/plugins/validation.rb +1 -3
- data/lib/shrine/plugins/validation_helpers.rb +1 -3
- data/lib/shrine/plugins/versions.rb +1 -3
- data/lib/shrine/storage/file_system.rb +1 -1
- data/lib/shrine/storage/linter.rb +1 -1
- data/lib/shrine/storage/memory.rb +2 -1
- data/lib/shrine/storage/s3.rb +3 -3
- data/lib/shrine/version.rb +1 -1
- metadata +8 -4
data/doc/creating_plugins.md
CHANGED
@@ -1,6 +1,13 @@
|
|
1
|
-
|
1
|
+
---
|
2
|
+
id: creating-plugins
|
3
|
+
title: Writing a Plugin
|
4
|
+
---
|
5
|
+
|
6
|
+
Shrine has a lot of plugins built-in, but you can use Shrine's plugin system to
|
7
|
+
create your own.
|
8
|
+
|
9
|
+
## Definition
|
2
10
|
|
3
|
-
Shrine has a lot of plugins built-in, but you can also easily create your own.
|
4
11
|
Simply put, a plugin is a module:
|
5
12
|
|
6
13
|
```rb
|
@@ -11,9 +18,9 @@ end
|
|
11
18
|
Shrine.plugin MyPlugin
|
12
19
|
```
|
13
20
|
|
14
|
-
If you would like to load plugins with a symbol
|
15
|
-
that ship with Shrine, you need to put the plugin in
|
16
|
-
`shrine/plugins/my_plugin.rb` in
|
21
|
+
If you would like to load plugins with a symbol (like you already do with
|
22
|
+
plugins that ship with Shrine), you need to put the plugin in
|
23
|
+
`shrine/plugins/my_plugin.rb` in your load path and register it:
|
17
24
|
|
18
25
|
```rb
|
19
26
|
# shrine/plugins/my_plugin.rb
|
@@ -31,6 +38,8 @@ end
|
|
31
38
|
Shrine.plugin :my_plugin
|
32
39
|
```
|
33
40
|
|
41
|
+
## Methods
|
42
|
+
|
34
43
|
The way to make plugins actually extend Shrine's core classes is by defining
|
35
44
|
special modules inside the plugin. Here's a list of all "special" modules:
|
36
45
|
|
@@ -51,7 +60,7 @@ uploading:
|
|
51
60
|
```rb
|
52
61
|
module MyPlugin
|
53
62
|
module InstanceMethods
|
54
|
-
def upload(io,
|
63
|
+
def upload(io, **options)
|
55
64
|
time = Time.now
|
56
65
|
result = super
|
57
66
|
duration = Time.now - time
|
@@ -61,40 +70,51 @@ module MyPlugin
|
|
61
70
|
end
|
62
71
|
```
|
63
72
|
|
64
|
-
Notice that we can call `super` to get the original behaviour.
|
65
|
-
these modules, you can also make your plugin configurable:
|
73
|
+
Notice that we can call `super` to get the original behaviour.
|
66
74
|
|
67
|
-
|
68
|
-
Shrine.plugin :my_plugin, foo: "bar"
|
69
|
-
```
|
75
|
+
## Configuration
|
70
76
|
|
71
|
-
You
|
72
|
-
|
73
|
-
|
74
|
-
methods.
|
77
|
+
You'll likely want to make your plugin configurable. You can do that by
|
78
|
+
overriding the `.configure` class method and storing received options into
|
79
|
+
`Shrine.opts`:
|
75
80
|
|
76
81
|
```rb
|
77
82
|
module MyPlugin
|
78
|
-
def self.configure(uploader,
|
79
|
-
uploader
|
80
|
-
uploader.opts[:
|
83
|
+
def self.configure(uploader, **opts)
|
84
|
+
uploader.opts[:my_plugin] ||= {}
|
85
|
+
uploader.opts[:my_plugin].merge!(opts)
|
81
86
|
end
|
82
87
|
|
83
88
|
module InstanceMethods
|
84
|
-
def
|
85
|
-
opts[:
|
89
|
+
def upload(io, **options)
|
90
|
+
opts[:my_plugin] #=> { ... }
|
91
|
+
# ...
|
86
92
|
end
|
87
93
|
end
|
88
94
|
end
|
89
95
|
```
|
90
96
|
|
97
|
+
Users can now pass these configuration options when loading your plugin:
|
98
|
+
|
99
|
+
```rb
|
100
|
+
Shrine.plugin :my_plugin, foo: "bar"
|
101
|
+
```
|
102
|
+
|
103
|
+
## Dependencies
|
104
|
+
|
91
105
|
If your plugin depends on other plugins, you can load them inside of
|
92
|
-
`.load_dependencies
|
106
|
+
`.load_dependencies`:
|
93
107
|
|
94
108
|
```rb
|
95
109
|
module MyPlugin
|
96
|
-
def self.load_dependencies(uploader,
|
110
|
+
def self.load_dependencies(uploader, **opts)
|
97
111
|
uploader.plugin :derivatives # depends on the derivatives plugin
|
98
112
|
end
|
99
113
|
end
|
100
114
|
```
|
115
|
+
|
116
|
+
The dependencies will get loaded before your plugin, allowing you to override
|
117
|
+
methods of your dependencies in your method modules.
|
118
|
+
|
119
|
+
The same configuration options passed to `.configure` are passed to
|
120
|
+
`.load_dependencies` as well.
|
data/doc/creating_storages.md
CHANGED
data/doc/design.md
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
---
|
2
|
+
title: The Design of Shrine
|
3
|
+
---
|
2
4
|
|
3
5
|
*If you want an in-depth walkthrough through the Shrine codebase, see [Notes on study of shrine implementation] article by Jonathan Rochkind.*
|
4
6
|
|
@@ -187,8 +189,8 @@ Shrine::Attachment.new(:image).instance_methods #=> [:image=, :image, :image_url
|
|
187
189
|
|
188
190
|
# equivalents
|
189
191
|
Shrine::Attachment.new(:image)
|
192
|
+
Shrine::Attachment[:image]
|
190
193
|
Shrine::Attachment(:image)
|
191
|
-
Shrine[:image]
|
192
194
|
```
|
193
195
|
|
194
196
|
We can include this module to a model:
|
@@ -206,7 +208,7 @@ photo.image_url # shorthand for `photo.image_attacher.url`
|
|
206
208
|
photo.image_attacher #=> #<Shrine::Attacher>
|
207
209
|
```
|
208
210
|
|
209
|
-
When
|
211
|
+
When a persistence plugin is loaded, the `Shrine::Attachment` module also
|
210
212
|
automatically:
|
211
213
|
|
212
214
|
* syncs Shrine's validation errors with the record
|
@@ -214,7 +216,7 @@ automatically:
|
|
214
216
|
* deletes the uploaded file if attachment was replaced/removed or the record
|
215
217
|
destroyed
|
216
218
|
|
217
|
-
[Using Attacher]: /
|
219
|
+
[Using Attacher]: https://shrinerb.com/docs/attacher
|
218
220
|
[Notes on study of shrine implementation]: https://bibwild.wordpress.com/2018/09/12/notes-on-study-of-shrine-implementation/
|
219
|
-
[Creating a New Plugin]: /
|
221
|
+
[Creating a New Plugin]: https://shrinerb.com/docs/creating-plugins
|
220
222
|
[Plugin system of Sequel and Roda]: https://twin.github.io/the-plugin-system-of-sequel-and-roda/
|
data/doc/direct_s3.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
---
|
2
|
+
id: direct-s3
|
3
|
+
title: Direct Uploads to S3
|
4
|
+
---
|
2
5
|
|
3
6
|
Shrine gives you the ability to upload files directly to Amazon S3 (or any
|
4
7
|
other storage service that accepts direct uploads). Uploading directly to a
|
@@ -23,12 +26,14 @@ storage service is beneficial for several reasons:
|
|
23
26
|
request-response lifecycle might not be able to finish before the request
|
24
27
|
times out.
|
25
28
|
|
29
|
+
## Storage
|
30
|
+
|
26
31
|
To start, let's set both temporary and permanent storage to S3, with the
|
27
32
|
temporary storage uploading to the `cache/` prefix:
|
28
33
|
|
29
34
|
```rb
|
30
35
|
# Gemfile
|
31
|
-
gem "shrine", "~>
|
36
|
+
gem "shrine", "~> 3.0"
|
32
37
|
gem "aws-sdk-s3", "~> 1.14"
|
33
38
|
```
|
34
39
|
```rb
|
@@ -375,6 +380,6 @@ setup] guide.
|
|
375
380
|
[lifecycle Console]: http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html
|
376
381
|
[lifecycle API]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#put_bucket_lifecycle_configuration-instance_method
|
377
382
|
[Minio]: https://minio.io
|
378
|
-
[minio setup]: /
|
379
|
-
[metadata direct uploads]: /
|
383
|
+
[minio setup]: https://shrinerb.com/docs/testing#minio
|
384
|
+
[metadata direct uploads]: https://shrinerb.com/docs/metadata#direct-uploads
|
380
385
|
[content_disposition]: https://github.com/shrinerb/content_disposition
|
@@ -0,0 +1,50 @@
|
|
1
|
+
---
|
2
|
+
title: Articles
|
3
|
+
---
|
4
|
+
|
5
|
+
## Official articles
|
6
|
+
|
7
|
+
* [Introducing Shrine](http://twin.github.io/introducing-shrine)
|
8
|
+
* [Asynchronous File Uploads](http://twin.github.io/file-uploads-asynchronous-world)
|
9
|
+
* [Shrine 2.0 Released](https://twin.github.io/shrine-2-0-released/)
|
10
|
+
* [Shrine meets Transloadit](https://twin.github.io/shrine-meets-transloadit/)
|
11
|
+
* [Resumable File Uploads in Ruby](https://twin.github.io/resumable-file-uploads-in-ruby/)
|
12
|
+
* [Adding Direct S3 Uploads to a Roda App with Shrine](https://github.com/shrinerb/shrine/wiki/Adding-Direct-S3-Uploads)
|
13
|
+
* [Adding Resumable Uploads to a Roda App with Shrine](https://github.com/shrinerb/shrine/wiki/Adding-Resumable-Uploads)
|
14
|
+
* [Better File Uploads with Shrine: Motivation](https://twin.github.io/better-file-uploads-with-shrine-motivation/)
|
15
|
+
* [Better File Uploads with Shrine: Uploader](https://twin.github.io/better-file-uploads-with-shrine-uploader/)
|
16
|
+
* [Better File Uploads with Shrine: Attachment](https://twin.github.io/better-file-uploads-with-shrine-attachment/)
|
17
|
+
* [Better File Uploads with Shrine: Processing](https://twin.github.io/better-file-uploads-with-shrine-processing/)
|
18
|
+
* [Better File Uploads with Shrine: Metadata](https://twin.github.io/better-file-uploads-with-shrine-metadata/)
|
19
|
+
* [Better File Uploads with Shrine: Direct Uploads](https://twin.github.io/better-file-uploads-with-shrine-direct-uploads)
|
20
|
+
|
21
|
+
## Other Articles
|
22
|
+
|
23
|
+
* [Uploading files with Shrine in Hanami](http://katafrakt.me/2016/02/04/shrine-hanami-uploads/)
|
24
|
+
* [Rails File Uploading You Can Believe in with Shrine](http://www.sitepoint.com/rails-file-uploading-you-can-believe-in-with-shrine/)
|
25
|
+
* [Uploading Files With Rails and Shrine](https://code.tutsplus.com/tutorials/uploading-files-with-rails-and-shrine--cms-27596)
|
26
|
+
* [Upload images using Shrine.rb and Dropzone.js](https://codyeatworld.com/2017/04/18/rails-uploading-images-confidently-with-shrine-rb/)
|
27
|
+
* [Background file uploads to S3 using Shrine](http://elixirator.com/blog/2017/background-file-uploads-to-s3-using-shrine.html)
|
28
|
+
* [Rails + Shrine + DropzoneJS](https://stephencodes.com/rails-5-shrine-dropzonejs/)
|
29
|
+
* [Uploading Files with Rails and ActionCable](https://scotch.io/tutorials/uploading-files-with-rails-and-actioncable)
|
30
|
+
* [Creating Streaming Radio With Rails and Icecast](https://scotch.io/tutorials/creating-online-streaming-radio-with-rails-and-icecast)
|
31
|
+
* [Multiple Photo Upload using Shrine](https://github.com/pyksoft/multi-photo-upload#multiple-photo-upload-using-shrine)
|
32
|
+
* [Store Your Files on S3: Setup & Configuration](https://www.ironin.it/blog/store-your-files-on-s3-using-the-ruby-shrine-gem-part-1.html)
|
33
|
+
* [Store Your Files on S3: Direct Uploads](https://www.ironin.it/blog/store-your-files-on-s3-using-the-ruby-shrine-gem-part-2.html)
|
34
|
+
* [Store Your Files on S3: Uploading from a URL](https://www.ironin.it/blog/store-your-files-on-s3-using-the-ruby-shrine-gem-part-3.html)
|
35
|
+
* [How to use Trix and Shrine for WYSIWYG Editing with Drag-and-Drop Image Uploading](http://headway.io/blog/how-to-use-trix-and-shrine-for-wysiwyg-editing-with-drag-and-drop-image-uploading/)
|
36
|
+
* [Happy users uploading files with Rails 5, Shrine, and Vue.js](https://itnext.io/happy-users-uploading-files-with-rails-5-shrine-and-vue-js-bbcc470a327f)
|
37
|
+
* [Notes on study of shrine implementation](https://bibwild.wordpress.com/2018/09/12/notes-on-study-of-shrine-implementation/)
|
38
|
+
* [GraphQL file upload with Shrine](https://blog.stanko.io/graphql-file-upload-with-shrine-45fa26463c68)
|
39
|
+
|
40
|
+
## Videos
|
41
|
+
|
42
|
+
* [wroc_love.rb – Handling file uploads for a modern developer](https://www.youtube.com/watch?v=fP2JGjTZU2s)
|
43
|
+
* [File Uploads in Rails with Shrine (GoRails)](https://gorails.com/episodes/file-uploading-with-shrine?autoplay=1)
|
44
|
+
* [Direct File Uploads to S3: Part 1 (GoRails)](https://gorails.com/episodes/direct-file-uploads-to-s3-part-1?autoplay=1)
|
45
|
+
* [Direct File Uploads to S3: Part 2 (GoRails)](https://gorails.com/episodes/direct-file-uploads-to-s3-part-2?autoplay=1)
|
46
|
+
* [Direct File Uploads to S3: Part 3 (GoRails)](https://gorails.com/episodes/direct-file-uploads-to-s3-part-3?autoplay=1)
|
47
|
+
* [Backgrounding and Video Transcoding (GoRails)](https://gorails.com/episodes/shrine-background-and-video-transcoding?autoplay=1)
|
48
|
+
* [Multiple File Uploads with Shrine (GoRails)](https://gorails.com/episodes/multiple-file-uploads-with-shrine?autoplay=1)
|
49
|
+
* [Trix WYSIWYG Editor And File Uploads (GoRails)](https://gorails.com/episodes/trix-editor?autoplay=1)
|
50
|
+
* [Uploading Files to DigitalOcean Spaces (GoRails)](https://gorails.com/episodes/digital-ocean-spaces-with-rails?autoplay=1)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
---
|
2
|
+
title: Extensions
|
3
|
+
---
|
4
|
+
|
5
|
+
## Storages
|
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)
|
26
|
+
|
27
|
+
## Plugins
|
28
|
+
|
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)
|
40
|
+
|
41
|
+
## Libraries
|
42
|
+
|
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)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
---
|
2
|
+
title: Miscellaneous
|
3
|
+
---
|
4
|
+
|
5
|
+
## Demos
|
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)
|
14
|
+
|
15
|
+
## Projects
|
16
|
+
|
17
|
+
* [Cortex CMS](https://docs.cortexcms.org)
|
@@ -0,0 +1,1038 @@
|
|
1
|
+
---
|
2
|
+
id: getting-started
|
3
|
+
title: Getting Started
|
4
|
+
---
|
5
|
+
|
6
|
+
## Quick start
|
7
|
+
|
8
|
+
Add Shrine to the Gemfile and write an initializer which sets up the storage
|
9
|
+
and loads integration for your persistence library:
|
10
|
+
|
11
|
+
```rb
|
12
|
+
# Gemfile
|
13
|
+
gem "shrine", "~> 3.0"
|
14
|
+
```
|
15
|
+
|
16
|
+
```rb
|
17
|
+
require "shrine"
|
18
|
+
require "shrine/storage/file_system"
|
19
|
+
|
20
|
+
Shrine.storages = {
|
21
|
+
cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"), # temporary
|
22
|
+
store: Shrine::Storage::FileSystem.new("public", prefix: "uploads"), # permanent
|
23
|
+
}
|
24
|
+
|
25
|
+
Shrine.plugin :sequel # or :activerecord
|
26
|
+
Shrine.plugin :cached_attachment_data # for retaining the cached file across form redisplays
|
27
|
+
Shrine.plugin :restore_cached_data # re-extract metadata when attaching a cached file
|
28
|
+
Shrine.plugin :rack_file # for non-Rails apps
|
29
|
+
```
|
30
|
+
|
31
|
+
Next decide how you will name the attachment attribute on your model, and run a
|
32
|
+
migration that adds an `<attachment>_data` text or JSON column, which Shrine
|
33
|
+
will use to store all information about the attachment:
|
34
|
+
|
35
|
+
<!--DOCUSAURUS_CODE_TABS-->
|
36
|
+
<!--Sequel-->
|
37
|
+
```rb
|
38
|
+
Sequel.migration do
|
39
|
+
change do
|
40
|
+
add_column :photos, :image_data, :text # or :jsonb
|
41
|
+
end
|
42
|
+
end
|
43
|
+
```
|
44
|
+
<!--ActiveRecord-->
|
45
|
+
```rb
|
46
|
+
class AddImageDataToPhotos < ActiveRecord::Migration
|
47
|
+
def change
|
48
|
+
add_column :photos, :image_data, :text # or :jsonb
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
<!--Rails-->
|
53
|
+
```sh
|
54
|
+
$ rails generate migration add_image_data_to_photos image_data:text
|
55
|
+
```
|
56
|
+
<!--END_DOCUSAURUS_CODE_TABS-->
|
57
|
+
|
58
|
+
Now you can create an uploader class for the type of files you want to upload,
|
59
|
+
and add a virtual attribute for handling attachments using this uploader to
|
60
|
+
your model. If you do not care about adding plugins or additional processing,
|
61
|
+
you can use `Shrine::Attachment`.
|
62
|
+
|
63
|
+
```rb
|
64
|
+
class ImageUploader < Shrine
|
65
|
+
# plugins and uploading logic
|
66
|
+
end
|
67
|
+
```
|
68
|
+
<!--DOCUSAURUS_CODE_TABS-->
|
69
|
+
<!--Sequel-->
|
70
|
+
```rb
|
71
|
+
class Photo < Sequel::Model
|
72
|
+
include ImageUploader::Attachment(:image) # adds an `image` virtual attribute
|
73
|
+
end
|
74
|
+
```
|
75
|
+
<!--ActiveRecord-->
|
76
|
+
```rb
|
77
|
+
class Photo < ActiveRecord::Base
|
78
|
+
include ImageUploader::Attachment(:image) # adds an `image` virtual attribute
|
79
|
+
end
|
80
|
+
```
|
81
|
+
<!--END_DOCUSAURUS_CODE_TABS-->
|
82
|
+
|
83
|
+
Let's now add the form fields which will use this virtual attribute (NOT the
|
84
|
+
`<attachment>_data` column attribute). We need (1) a file field for choosing
|
85
|
+
files, and (2) a hidden field for retaining the uploaded file in case of
|
86
|
+
validation errors and for potential [direct uploads].
|
87
|
+
|
88
|
+
<!--DOCUSAURUS_CODE_TABS-->
|
89
|
+
<!--Rails form builder-->
|
90
|
+
```rb
|
91
|
+
form_for @photo do |f|
|
92
|
+
f.hidden_field :image, value: @photo.cached_image_data
|
93
|
+
f.file_field :image
|
94
|
+
f.submit
|
95
|
+
end
|
96
|
+
```
|
97
|
+
<!--Simple Form-->
|
98
|
+
```rb
|
99
|
+
simple_form_for @photo do |f|
|
100
|
+
f.input :image, as: :hidden, input_html: { value: @photo.cached_image_data }
|
101
|
+
f.input :image, as: :file
|
102
|
+
f.button :submit
|
103
|
+
end
|
104
|
+
```
|
105
|
+
<!--Forme-->
|
106
|
+
```rb
|
107
|
+
form @photo, action: "/photos", enctype: "multipart/form-data" do |f|
|
108
|
+
f.input :image, type: :hidden, value: @photo.cached_image_data
|
109
|
+
f.input :image, type: :file
|
110
|
+
f.button "Create"
|
111
|
+
end
|
112
|
+
```
|
113
|
+
<!--END_DOCUSAURUS_CODE_TABS-->
|
114
|
+
|
115
|
+
Note that the file field needs to go *after* the hidden field, so that
|
116
|
+
selecting a new file can always override the cached file in the hidden field.
|
117
|
+
Also notice the `enctype="multipart/form-data"` HTML attribute, which is
|
118
|
+
required for submitting files through the form (the Rails form builder
|
119
|
+
will automatically generate this for you).
|
120
|
+
|
121
|
+
When the form is submitted, in your router/controller you can assign the file
|
122
|
+
from request params to the attachment attribute on the model.
|
123
|
+
|
124
|
+
<!--DOCUSAURUS_CODE_TABS-->
|
125
|
+
<!--Rails-->
|
126
|
+
```rb
|
127
|
+
class PhotosController < ApplicationController
|
128
|
+
def create
|
129
|
+
Photo.create(photo_params)
|
130
|
+
# ...
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
def photo_params
|
136
|
+
params.require(:photo).permit(:image)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
```
|
140
|
+
<!--Sinatra-->
|
141
|
+
```rb
|
142
|
+
post "/photos" do
|
143
|
+
Photo.create(params[:photo])
|
144
|
+
# ...
|
145
|
+
end
|
146
|
+
```
|
147
|
+
<!--END_DOCUSAURUS_CODE_TABS-->
|
148
|
+
|
149
|
+
Once a file is uploaded and attached to the record, you can retrieve a URL to
|
150
|
+
the uploaded file with `#<attachment>_url` and display it on the page:
|
151
|
+
|
152
|
+
<!--DOCUSAURUS_CODE_TABS-->
|
153
|
+
<!--Rails-->
|
154
|
+
```erb
|
155
|
+
<%= image_tag @photo.image_url %>
|
156
|
+
```
|
157
|
+
<!--HTML-->
|
158
|
+
```erb
|
159
|
+
<img src="<%= @photo.image_url %>" />
|
160
|
+
```
|
161
|
+
<!--END_DOCUSAURUS_CODE_TABS-->
|
162
|
+
|
163
|
+
## Storage
|
164
|
+
|
165
|
+
A "storage" in Shrine is an object that encapsulates communication with a
|
166
|
+
specific storage service, by implementing a common public interface. Storage
|
167
|
+
instances are registered under an identifier in `Shrine.storages`, so that they
|
168
|
+
can later be used by [uploaders][uploader].
|
169
|
+
|
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]).
|
173
|
+
|
174
|
+
```rb
|
175
|
+
# Gemfile
|
176
|
+
gem "aws-sdk-s3", "~> 1.14" # for AWS S3 storage
|
177
|
+
```
|
178
|
+
```rb
|
179
|
+
require "shrine/storage/s3"
|
180
|
+
|
181
|
+
s3_options = {
|
182
|
+
bucket: "<YOUR BUCKET>", # required
|
183
|
+
access_key_id: "<YOUR ACCESS KEY ID>",
|
184
|
+
secret_access_key: "<YOUR SECRET ACCESS KEY>",
|
185
|
+
region: "<YOUR REGION>",
|
186
|
+
}
|
187
|
+
|
188
|
+
Shrine.storages = {
|
189
|
+
cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
|
190
|
+
store: Shrine::Storage::S3.new(**s3_options),
|
191
|
+
}
|
192
|
+
```
|
193
|
+
|
194
|
+
The above example sets up S3 for both temporary and permanent storage, which is
|
195
|
+
suitable for [direct uploads][presigned upload]. The `:cache` and `:store`
|
196
|
+
names are special only in terms that the [attacher] will automatically pick
|
197
|
+
them up, you can also register more storage objects under different names.
|
198
|
+
|
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].
|
202
|
+
|
203
|
+
## Uploader
|
204
|
+
|
205
|
+
Uploaders are subclasses of `Shrine`, and they wrap the actual upload to the
|
206
|
+
storage. They perform common tasks around upload that aren't related to a
|
207
|
+
particular storage.
|
208
|
+
|
209
|
+
```rb
|
210
|
+
class MyUploader < Shrine
|
211
|
+
# image attachment logic
|
212
|
+
end
|
213
|
+
```
|
214
|
+
|
215
|
+
It's common to create an uploader for each type of file that you want to handle
|
216
|
+
(`ImageUploader`, `VideoUploader`, `AudioUploader` etc), but really you can
|
217
|
+
organize them in any way you like.
|
218
|
+
|
219
|
+
### Uploading
|
220
|
+
|
221
|
+
The main method of the uploader is `Shrine.upload`, which takes an [IO-like
|
222
|
+
object][io abstraction] and a storage identifier on the input, and returns a
|
223
|
+
representation of the [uploaded file] on the output.
|
224
|
+
|
225
|
+
```rb
|
226
|
+
MyUploader.upload(file, :store) #=> #<Shrine::UploadedFile>
|
227
|
+
```
|
228
|
+
|
229
|
+
Internally this instantiates the uploader with the storage and calls
|
230
|
+
`Shrine#upload`:
|
231
|
+
|
232
|
+
```rb
|
233
|
+
uploader = MyUploader.new(:store)
|
234
|
+
uploader.upload(file) #=> #<Shrine::UploadedFile>
|
235
|
+
```
|
236
|
+
|
237
|
+
Some of the tasks performed by `#upload` include:
|
238
|
+
|
239
|
+
* extracting [metadata]
|
240
|
+
* generating [location]
|
241
|
+
* uploading (this is where the [storage] is called)
|
242
|
+
* closing the uploaded file
|
243
|
+
|
244
|
+
The second argument is a "context" hash which is forwarded to places like
|
245
|
+
metadata extraction and location generation, but it has a few special options:
|
246
|
+
|
247
|
+
```rb
|
248
|
+
uploader.upload(io, metadata: { "foo" => "bar" }) # add metadata
|
249
|
+
uploader.upload(io, location: "path/to/file") # specify custom location
|
250
|
+
uploader.upload(io, upload_options: { acl: "public-read" }) # add options to Storage#upload
|
251
|
+
```
|
252
|
+
|
253
|
+
### IO abstraction
|
254
|
+
|
255
|
+
Shrine is able to upload any IO-like object that implement methods [`#read`],
|
256
|
+
[`#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.
|
258
|
+
|
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:
|
266
|
+
|
267
|
+
```rb
|
268
|
+
uploader.upload File.open("/path/to/file", binmode: true) # upload from disk
|
269
|
+
uploader.upload StringIO.new("file content") # upload from memory
|
270
|
+
uploader.upload ActionDispatch::Http::UploadedFile.new(...) # upload from Rails controller
|
271
|
+
uploader.upload Shrine.rack_file({ tempfile: tempfile }) # upload from Rack controller
|
272
|
+
uploader.upload Rack::Test::UploadedFile.new(...) # upload from rack-test
|
273
|
+
uploader.upload Down.open("https://example.org/file") # upload from internet
|
274
|
+
uploader.upload Shrine::UploadedFile.new(...) # upload from Shrine storage
|
275
|
+
```
|
276
|
+
|
277
|
+
## Uploaded file
|
278
|
+
|
279
|
+
The `Shrine::UploadedFile` object represents the file that was uploaded to a
|
280
|
+
storage, and it's what's returned from `Shrine#upload` or when retrieving a
|
281
|
+
record [attachment]. It contains the following information:
|
282
|
+
|
283
|
+
| Key | Description |
|
284
|
+
| :------- | :---------- |
|
285
|
+
| `id` | location of the file on the storage |
|
286
|
+
| `storage` | identifier of the storage the file was uploaded to |
|
287
|
+
| `metadata` | file [metadata] that was extracted before upload |
|
288
|
+
|
289
|
+
```rb
|
290
|
+
uploaded_file = uploader.upload(file)
|
291
|
+
uploaded_file.data #=> {"id"=>"949sdjg834.jpg","storage"=>"store","metadata"=>{...}}
|
292
|
+
|
293
|
+
uploaded_file.id #=> "949sdjg834.jpg"
|
294
|
+
uploaded_file.storage #=> #<Shrine::Storage::S3>
|
295
|
+
uploaded_file.metadata #=> {...}
|
296
|
+
```
|
297
|
+
|
298
|
+
It comes with many convenient methods that delegate to the storage:
|
299
|
+
|
300
|
+
```rb
|
301
|
+
uploaded_file.url #=> "https://my-bucket.s3.amazonaws.com/949sdjg834.jpg"
|
302
|
+
uploaded_file.open { |io| ... } # opens the uploaded file stream
|
303
|
+
uploaded_file.download { |file| ... } # downloads the uploaded file to disk
|
304
|
+
uploaded_file.stream(destination) # streams uploaded content into a writable destination
|
305
|
+
uploaded_file.exists? #=> true
|
306
|
+
uploaded_file.delete # deletes the uploaded file from the storage
|
307
|
+
```
|
308
|
+
|
309
|
+
It also implements the IO-like interface that conforms to Shrine's [IO
|
310
|
+
abstraction][io abstraction], which allows it to be uploaded again to other
|
311
|
+
storages.
|
312
|
+
|
313
|
+
```rb
|
314
|
+
uploaded_file.read # returns content of the uploaded file
|
315
|
+
uploaded_file.eof? # returns true if the whole IO was read
|
316
|
+
uploaded_file.rewind # rewinds the IO
|
317
|
+
uploaded_file.close # closes the IO
|
318
|
+
```
|
319
|
+
|
320
|
+
For more details, see the [Retrieving Uploads] guide and
|
321
|
+
[`Shrine::UploadedFile`] API docs.
|
322
|
+
|
323
|
+
## Attaching
|
324
|
+
|
325
|
+
To attach uploaded files to database records, Shrine offers an attachment
|
326
|
+
interface built on top of uploaders and uploaded files. There are integrations
|
327
|
+
for various persistence libraries ([ActiveRecord][activerecord plugin],
|
328
|
+
[Sequel][sequel plugin], [ROM][rom plugin], [Hanami][hanami plugin],
|
329
|
+
[Mongoid][mongoid plugin]), but you can also attach files to plain structs
|
330
|
+
([mutable][model plugin] or [immutable][entity plugin]).
|
331
|
+
|
332
|
+
```rb
|
333
|
+
Shrine.plugin :sequel # :activerecord
|
334
|
+
```
|
335
|
+
|
336
|
+
### Attachment module
|
337
|
+
|
338
|
+
The easiest way to attach files is with the `Shrine::Attachment` module:
|
339
|
+
|
340
|
+
```rb
|
341
|
+
class Photo < Sequel::Model # ActiveRecord::Base
|
342
|
+
include ImageUploader::Attachment.new(:image) #
|
343
|
+
include ImageUploader::Attachment[:image] # use a preferred syntax
|
344
|
+
include ImageUploader::Attachment(:image) #
|
345
|
+
end
|
346
|
+
```
|
347
|
+
|
348
|
+
The included module will add attachment methods for the specified attribute:
|
349
|
+
|
350
|
+
| Method | Description |
|
351
|
+
| :----- | :---------- |
|
352
|
+
| `#image=` | uploads the file to temporary storage and serializes the result into `image_data` |
|
353
|
+
| `#image` | returns [`Shrine::UploadedFile`][uploaded file] instantiated from `image_data` |
|
354
|
+
| `#image_url` | calls `url` on the attachment if it's present, otherwise returns nil |
|
355
|
+
| `#image_attacher` | returns instance of [`Shrine::Attacher`][attacher] which handles the attaching |
|
356
|
+
|
357
|
+
The persistence plugin we loaded will add callbacks that ensure cached files
|
358
|
+
are automatically promoted to permanent storage on when record is saved, and
|
359
|
+
that attachments are deleted when the record is destroyed.
|
360
|
+
|
361
|
+
```rb
|
362
|
+
# no file is attached
|
363
|
+
photo.image #=> nil
|
364
|
+
|
365
|
+
# 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={...}>
|
368
|
+
photo.image_url #=> "/uploads/cache/0sdfllasfi842.jpg"
|
369
|
+
photo.image_data #=> '{"id":"0sdfllasfi842.jpg","storage":"cache","metadata":{...}}'
|
370
|
+
|
371
|
+
# the cached file is promoted to permanent storage and saved to `image_data` column
|
372
|
+
photo.save
|
373
|
+
photo.image #=> #<Shrine::UploadedFile @data={...}>
|
374
|
+
photo.image_url #=> "/uploads/store/l02kladf8jlda.jpg"
|
375
|
+
photo.image_data #=> '{"id":"l02kladf8jlda.jpg","storage":"store","metadata":{...}}'
|
376
|
+
|
377
|
+
# the attached file is deleted with the record
|
378
|
+
photo.destroy
|
379
|
+
photo.image.exists? #=> false
|
380
|
+
```
|
381
|
+
|
382
|
+
If there is already a file attached and a new file is attached, the previous
|
383
|
+
attachment will get deleted when the record gets saved.
|
384
|
+
|
385
|
+
```rb
|
386
|
+
photo.update(image: new_file) # changes the attachment and deletes previous
|
387
|
+
photo.update(image: nil) # removes the attachment and deletes previous
|
388
|
+
```
|
389
|
+
|
390
|
+
### Attacher
|
391
|
+
|
392
|
+
The methods and callbacks added by the `Shrine::Attachment` module just
|
393
|
+
delegate the behaviour to an underlying `Shrine::Attacher` object.
|
394
|
+
|
395
|
+
```rb
|
396
|
+
photo.image_attacher #=> #<Shrine::Attacher>
|
397
|
+
```
|
398
|
+
|
399
|
+
The `Shrine::Attacher` object can be instantiated and used directly:
|
400
|
+
|
401
|
+
```rb
|
402
|
+
attacher = ImageUploader::Attacher.from_model(photo, :image)
|
403
|
+
|
404
|
+
attacher.assign(file) # equivalent to `photo.image = file`
|
405
|
+
attacher.file # equivalent to `photo.image`
|
406
|
+
attacher.url # equivalent to `photo.image_url`
|
407
|
+
```
|
408
|
+
|
409
|
+
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.
|
412
|
+
|
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.
|
416
|
+
|
417
|
+
### Temporary storage
|
418
|
+
|
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:
|
422
|
+
|
423
|
+
```rb
|
424
|
+
Shrine.plugin :model, cache: false
|
425
|
+
```
|
426
|
+
<!--DOCUSAURUS_CODE_TABS-->
|
427
|
+
<!--Attachment-->
|
428
|
+
```rb
|
429
|
+
photo.image = File.open("waterfall.jpg")
|
430
|
+
photo.image.storage_key #=> :store
|
431
|
+
```
|
432
|
+
<!--Attacher-->
|
433
|
+
```rb
|
434
|
+
attacher.attach File.open("waterfall.jpg")
|
435
|
+
attacher.file.storage_key #=> :store
|
436
|
+
```
|
437
|
+
<!--END_DOCUSAURUS_CODE_TABS-->
|
438
|
+
|
439
|
+
## Plugin system
|
440
|
+
|
441
|
+
By default Shrine comes with a small core which provides only the essential
|
442
|
+
functionality. All additional features are available via [plugins], which also
|
443
|
+
ship with Shrine. This way you can choose exactly what and how much Shrine does
|
444
|
+
for you, and you load the code only for features that you use.
|
445
|
+
|
446
|
+
```rb
|
447
|
+
Shrine.plugin :instrumentation # adds instrumentation
|
448
|
+
```
|
449
|
+
|
450
|
+
Plugins add behaviour by extending Shrine core classes via module inclusion, and
|
451
|
+
many of them also accept configuration options. The plugin system respects
|
452
|
+
inheritance, so you can choose to load a plugin globally or per uploader.
|
453
|
+
|
454
|
+
```rb
|
455
|
+
class ImageUploader < Shrine
|
456
|
+
plugin :store_dimensions # extract image dimensions only for this uploader and its descendants
|
457
|
+
end
|
458
|
+
```
|
459
|
+
|
460
|
+
If you want to extend Shrine functionality with custom behaviour, you can also
|
461
|
+
[create your own plugin][Creating Plugins]. There are also additional [external
|
462
|
+
plugins] created by others.
|
463
|
+
|
464
|
+
## Metadata
|
465
|
+
|
466
|
+
Shrine automatically extracts some basic file metadata and saves them to the
|
467
|
+
`Shrine::UploadedFile`. You can access them through the `#metadata` hash or via
|
468
|
+
metadata methods:
|
469
|
+
|
470
|
+
```rb
|
471
|
+
uploaded_file.metadata #=>
|
472
|
+
# {
|
473
|
+
# "filename" => "matrix.mp4",
|
474
|
+
# "mime_type" => "video/mp4",
|
475
|
+
# "size" => 345993,
|
476
|
+
# }
|
477
|
+
|
478
|
+
uploaded_file.original_filename #=> "matrix.mp4"
|
479
|
+
uploaded_file.extension #=> "mp4"
|
480
|
+
uploaded_file.mime_type #=> "video/mp4"
|
481
|
+
uploaded_file.size #=> 345993
|
482
|
+
```
|
483
|
+
|
484
|
+
### MIME type
|
485
|
+
|
486
|
+
By default, `mime_type` metadata will be set from the `#content_type` attribute
|
487
|
+
of the uploaded file (if it exists), which is generally not secure and will
|
488
|
+
trigger a warning. You can load the [`determine_mime_type`][determine_mime_type
|
489
|
+
plugin] plugin to have MIME type extracted from file *content* instead.
|
490
|
+
|
491
|
+
```rb
|
492
|
+
# Gemfile
|
493
|
+
gem "marcel", "~> 0.3"
|
494
|
+
```
|
495
|
+
```rb
|
496
|
+
Shrine.plugin :determine_mime_type, analyzer: :marcel
|
497
|
+
```
|
498
|
+
```rb
|
499
|
+
photo = Photo.create(image: StringIO.new("<?php ... ?>"))
|
500
|
+
photo.image.mime_type #=> "application/x-php"
|
501
|
+
```
|
502
|
+
|
503
|
+
### Other metadata
|
504
|
+
|
505
|
+
In addition to basic metadata, you can also extract [image
|
506
|
+
dimensions][store_dimensions plugin], calculate [signatures][signature plugin],
|
507
|
+
and in general extract any [custom metadata][add_metadata plugin]. Check out
|
508
|
+
the [Extracting Metadata] guide for more details.
|
509
|
+
|
510
|
+
## Processing
|
511
|
+
|
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.
|
516
|
+
|
517
|
+
For image processing, it's recommended to use the **[ImageProcessing]** gem,
|
518
|
+
which is a high-level wrapper for processing with
|
519
|
+
[MiniMagick][ImageProcessing::MiniMagick] and [libvips][ImageProcessing::Vips].
|
520
|
+
|
521
|
+
```sh
|
522
|
+
$ brew install imagemagick vips
|
523
|
+
```
|
524
|
+
|
525
|
+
### Processing up front
|
526
|
+
|
527
|
+
You can use the [`derivatives`][derivatives plugin] plugin to generate a set of
|
528
|
+
pre-defined processed files:
|
529
|
+
|
530
|
+
```rb
|
531
|
+
# Gemfile
|
532
|
+
gem "image_processing", "~> 1.8"
|
533
|
+
```
|
534
|
+
```rb
|
535
|
+
Shrine.plugin :derivatives
|
536
|
+
```
|
537
|
+
```rb
|
538
|
+
require "image_processing/mini_magick"
|
539
|
+
|
540
|
+
class ImageUploader < Shrine
|
541
|
+
Attacher.derivatives_processor do |original|
|
542
|
+
magick = ImageProcessing::MiniMagick.source(original)
|
543
|
+
|
544
|
+
{
|
545
|
+
large: magick.resize_to_limit!(800, 800),
|
546
|
+
medium: magick.resize_to_limit!(500, 500),
|
547
|
+
small: magick.resize_to_limit!(300, 300),
|
548
|
+
}
|
549
|
+
end
|
550
|
+
end
|
551
|
+
```
|
552
|
+
```rb
|
553
|
+
photo = Photo.new(image: file)
|
554
|
+
photo.image_derivatives! # calls derivatives processor and uploads results
|
555
|
+
photo.save
|
556
|
+
```
|
557
|
+
|
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:
|
560
|
+
|
561
|
+
```rb
|
562
|
+
photo.image_derivatives! if photo.image_changed?
|
563
|
+
```
|
564
|
+
|
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:
|
568
|
+
|
569
|
+
```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"
|
574
|
+
```
|
575
|
+
|
576
|
+
For more details, see the [`derivatives`][derivatives plugin] plugin
|
577
|
+
documentation and the [File Processing] guide.
|
578
|
+
|
579
|
+
### Processing on-the-fly
|
580
|
+
|
581
|
+
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
|
588
|
+
processing we want to perform:
|
589
|
+
|
590
|
+
```rb
|
591
|
+
# Gemfile
|
592
|
+
gem "image_processing", "~> 1.8"
|
593
|
+
```
|
594
|
+
```rb
|
595
|
+
# config/routes.rb (Rails)
|
596
|
+
Rails.application.routes.draw do
|
597
|
+
# ...
|
598
|
+
mount ImageUploader.derivation_endpoint => "/derivations/image"
|
599
|
+
end
|
600
|
+
```
|
601
|
+
```rb
|
602
|
+
require "image_processing/mini_magick"
|
603
|
+
|
604
|
+
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
|
608
|
+
|
609
|
+
derivation :thumbnail do |file, width, height|
|
610
|
+
ImageProcessing::MiniMagick
|
611
|
+
.source(file)
|
612
|
+
.resize_to_limit!(width.to_i, height.to_i)
|
613
|
+
end
|
614
|
+
end
|
615
|
+
```
|
616
|
+
|
617
|
+
Now we can generate URLs from attached files that will perform the desired
|
618
|
+
processing:
|
619
|
+
|
620
|
+
```rb
|
621
|
+
photo.image.derivation_url(:thumbnail, 600, 400)
|
622
|
+
#=> "/derivations/image/thumbnail/600/400/eyJpZCI6ImZvbyIsInN0b3JhZ2UiOiJzdG9yZSJ9?signature=..."
|
623
|
+
```
|
624
|
+
|
625
|
+
The on-the-fly processing feature is highly customizable, see the
|
626
|
+
[`derivation_endpoint`][derivation_endpoint plugin] plugin documentation for
|
627
|
+
more details.
|
628
|
+
|
629
|
+
## Validation
|
630
|
+
|
631
|
+
The [`validation`][validation plugin] plugin allows performing validation for
|
632
|
+
attached files. For common validations, the
|
633
|
+
[`validation_helpers`][validation_helpers plugin] plugin provides useful
|
634
|
+
validators for built in metadata:
|
635
|
+
|
636
|
+
```rb
|
637
|
+
Shrine.plugin :validation_helpers
|
638
|
+
```
|
639
|
+
```rb
|
640
|
+
class DocumentUploader < Shrine
|
641
|
+
Attacher.validate do
|
642
|
+
validate_max_size 5*1024*1024, message: "is too large (max is 5 MB)"
|
643
|
+
validate_mime_type %w[application/pdf]
|
644
|
+
end
|
645
|
+
end
|
646
|
+
```
|
647
|
+
|
648
|
+
```rb
|
649
|
+
user = User.new
|
650
|
+
user.cv = File.open("cv.pdf", "rb")
|
651
|
+
user.valid? #=> false
|
652
|
+
user.errors.to_hash #=> {:cv=>["is too large (max is 5 MB)"]}
|
653
|
+
```
|
654
|
+
|
655
|
+
For more details, see the [File Validation] guide and
|
656
|
+
[`validation_helpers`][validation_helpers plugin] plugin docs.
|
657
|
+
|
658
|
+
## Location
|
659
|
+
|
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:
|
665
|
+
|
666
|
+
```rb
|
667
|
+
class ImageUploader < Shrine
|
668
|
+
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
|
672
|
+
|
673
|
+
[type, style, name].compact.join("/")
|
674
|
+
end
|
675
|
+
end
|
676
|
+
```
|
677
|
+
```plaintext
|
678
|
+
uploads/
|
679
|
+
photos/
|
680
|
+
originals/
|
681
|
+
la98lda74j3g.jpg
|
682
|
+
thumbs/
|
683
|
+
95kd8kafg80a.jpg
|
684
|
+
ka8agiaf9gk4.jpg
|
685
|
+
```
|
686
|
+
|
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.
|
690
|
+
|
691
|
+
## Direct uploads
|
692
|
+
|
693
|
+
To improve the user experience, it's recommended to upload files asynchronously
|
694
|
+
as soon as the user selects them. The direct uploads would go to temporary
|
695
|
+
storage, just like in the synchronous flow. Then, instead of attaching a raw
|
696
|
+
file to your model, you assign the cached file JSON data.
|
697
|
+
|
698
|
+
```rb
|
699
|
+
# in the regular synchronous flow
|
700
|
+
photo.image = file
|
701
|
+
|
702
|
+
# in the direct upload flow
|
703
|
+
photo.image = '{"id":"...","storage":"cache","metadata":{...}}'
|
704
|
+
```
|
705
|
+
|
706
|
+
On the client side it's highly recommended to use **[Uppy]**, a very flexible
|
707
|
+
modern JavaScript file upload library that happens to integrate nicely with
|
708
|
+
Shrine.
|
709
|
+
|
710
|
+
### Simple direct upload
|
711
|
+
|
712
|
+
The simplest approach is to upload directly to an endpoint in your app, which
|
713
|
+
forwards uploads to the specified storage. The
|
714
|
+
[`upload_endpoint`][upload_endpoint plugin] Shrine plugin provides a
|
715
|
+
[mountable][Mounting Endpoints] Rack app that implements this endpoint:
|
716
|
+
|
717
|
+
```rb
|
718
|
+
Shrine.plugin :upload_endpoint
|
719
|
+
```
|
720
|
+
```rb
|
721
|
+
# config/routes.rb (Rails)
|
722
|
+
Rails.application.routes.draw do
|
723
|
+
# ...
|
724
|
+
mount ImageUploader.upload_endpoint(:cache) => "/images/upload" # POST /images/upload
|
725
|
+
end
|
726
|
+
```
|
727
|
+
|
728
|
+
Then you can configure Uppy's [XHR Upload][uppy xhr-upload] plugin to upload to
|
729
|
+
this endpoint. See [this walkthrough][Adding Direct App Uploads] for adding
|
730
|
+
simple direct uploads from scratch, it includes a complete JavaScript example
|
731
|
+
(there is also the [Roda][roda demo] / [Rails][rails demo] demo app).
|
732
|
+
|
733
|
+
### Presigned direct upload
|
734
|
+
|
735
|
+
For better performance, you can also upload files directly to your cloud
|
736
|
+
storage service (AWS S3, Google Cloud Storage etc). For this, your temporary
|
737
|
+
storage needs to be your cloud service:
|
738
|
+
|
739
|
+
```rb
|
740
|
+
require "shrine/storage/s3"
|
741
|
+
|
742
|
+
Shrine.storages = {
|
743
|
+
cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
|
744
|
+
store: Shrine::Storage::S3.new(**s3_options)
|
745
|
+
}
|
746
|
+
```
|
747
|
+
|
748
|
+
In this flow, the client needs to first fetch upload parameters from the
|
749
|
+
server, and then use these parameters for the upload to the cloud service.
|
750
|
+
The [`presign_endpoint`][presign_endpoint plugin] Shrine plugin provides a
|
751
|
+
[mountable][Mounting Endpoints] Rack app that generates upload parameters:
|
752
|
+
|
753
|
+
```rb
|
754
|
+
Shrine.plugin :presign_endpoint
|
755
|
+
```
|
756
|
+
```rb
|
757
|
+
# config/routes.rb (Rails)
|
758
|
+
Rails.application.routes.draw do
|
759
|
+
# ...
|
760
|
+
mount Shrine.presign_endpoint(:cache) => "/s3/params" # GET /s3/params
|
761
|
+
end
|
762
|
+
```
|
763
|
+
|
764
|
+
Then you can configure Uppy's [AWS S3][uppy aws-s3] plugin to fetch params from
|
765
|
+
your endpoint before uploading to S3. See [this walkthrough][Adding Direct S3
|
766
|
+
Uploads] for adding direct uploads to S3 from scratch, it includes a complete
|
767
|
+
JavaScript example (there is also the [Roda][roda demo] / [Rails][rails demo]
|
768
|
+
demo). See also the [Direct Uploads to S3] guide for more details.
|
769
|
+
|
770
|
+
### Resumable direct upload
|
771
|
+
|
772
|
+
If your app is accepting large uploads, you can improve resilience by making
|
773
|
+
the uploads **resumable**. This can significantly improve experience for users
|
774
|
+
on slow and flaky internet connections.
|
775
|
+
|
776
|
+
#### Uppy S3 Multipart
|
777
|
+
|
778
|
+
You can achieve resumable uploads directly to S3 with the [AWS S3
|
779
|
+
Multipart][uppy aws-s3-multipart] Uppy plugin, accompanied with
|
780
|
+
`uppy_s3_multipart` Shrine plugin provided by the [uppy-s3_multipart] gem.
|
781
|
+
|
782
|
+
```rb
|
783
|
+
# Gemfile
|
784
|
+
gem "uppy-s3_multipart", "~> 0.3"
|
785
|
+
```
|
786
|
+
```rb
|
787
|
+
Shrine.plugin :uppy_s3_multipart
|
788
|
+
```
|
789
|
+
```rb
|
790
|
+
# config/routes.rb (Rails)
|
791
|
+
Rails.application.routes.draw do
|
792
|
+
# ...
|
793
|
+
mount Shrine.uppy_s3_multipart(:cache) => "/s3/multipart"
|
794
|
+
end
|
795
|
+
```
|
796
|
+
|
797
|
+
See the [uppy-s3_multipart] docs for more details.
|
798
|
+
|
799
|
+
#### Tus protocol
|
800
|
+
|
801
|
+
If you want a more generic approach, you can build your resumable uploads on
|
802
|
+
**[tus]** – an open resumable upload protocol. On the server side you can use
|
803
|
+
the [tus-ruby-server] gem, on the client side Uppy's [Tus][uppy tus] plugin,
|
804
|
+
and the [shrine-tus] gem for the glue.
|
805
|
+
|
806
|
+
```rb
|
807
|
+
# Gemfile
|
808
|
+
gem "tus-server", "~> 2.0"
|
809
|
+
gem "shrine-tus", "~> 2.1"
|
810
|
+
```
|
811
|
+
```rb
|
812
|
+
require "shrine/storage/tus"
|
813
|
+
|
814
|
+
Shrine.storages = {
|
815
|
+
cache: Shrine::Storage::Tus.new, # tus server acts as temporary storage
|
816
|
+
store: ..., # your permanent storage
|
817
|
+
}
|
818
|
+
```
|
819
|
+
```rb
|
820
|
+
# config/routes.rb (Rails)
|
821
|
+
Rails.application.routes.draw do
|
822
|
+
# ...
|
823
|
+
mount Tus::Server => "/files"
|
824
|
+
end
|
825
|
+
```
|
826
|
+
|
827
|
+
See [this walkthrough][Adding Resumable Uploads] for adding tus-powered
|
828
|
+
resumable uploads from scratch, it includes a complete JavaScript example
|
829
|
+
(there is also a [demo app][resumable demo]). See also [shrine-tus] and
|
830
|
+
[tus-ruby-server] docs for more details.
|
831
|
+
|
832
|
+
## Backgrounding
|
833
|
+
|
834
|
+
The [`backgrounding`][backgrounding plugin] allows you to move file promotion
|
835
|
+
and deletion into a background job, using the backgrounding library [of your
|
836
|
+
choice][Backgrounding Libraries]:
|
837
|
+
|
838
|
+
```rb
|
839
|
+
Shrine.plugin :backgrounding
|
840
|
+
Shrine::Attacher.promote_block do
|
841
|
+
PromoteJob.perform_async(self.class.name, record.class.name, record.id, name, file_data)
|
842
|
+
end
|
843
|
+
Shrine::Attacher.destroy_block do
|
844
|
+
DestroyJob.perform_async(self.class.name, data)
|
845
|
+
end
|
846
|
+
```
|
847
|
+
```rb
|
848
|
+
class PromoteJob
|
849
|
+
include Sidekiq::Worker
|
850
|
+
|
851
|
+
def perform(attacher_class, record_class, record.id, name, file_data)
|
852
|
+
attacher_class = Object.const_get(attacher_class)
|
853
|
+
record = Object.const_get(record_class).find(record_id) # if using Active Record
|
854
|
+
|
855
|
+
attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
|
856
|
+
attacher.atomic_promote
|
857
|
+
rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
|
858
|
+
# attachment has changed or the record has been deleted, nothing to do
|
859
|
+
end
|
860
|
+
end
|
861
|
+
```
|
862
|
+
```rb
|
863
|
+
class DestroyJob
|
864
|
+
include Sidekiq::Worker
|
865
|
+
|
866
|
+
def perform(attacher_class, data)
|
867
|
+
attacher_class = Object.const_get(attacher_class)
|
868
|
+
|
869
|
+
attacher = attacher_class.from_data(data)
|
870
|
+
attacher.destroy
|
871
|
+
end
|
872
|
+
end
|
873
|
+
```
|
874
|
+
|
875
|
+
## Clearing cache
|
876
|
+
|
877
|
+
Shrine doesn't automatically delete files uploaded to temporary storage, instead
|
878
|
+
you should set up a separate recurring task that will automatically delete old
|
879
|
+
cached files.
|
880
|
+
|
881
|
+
Most Shrine storage classes come with a `#clear!` method, which you can call in
|
882
|
+
a recurring script. For FileSystem and S3 storage it would look like this:
|
883
|
+
|
884
|
+
```rb
|
885
|
+
# FileSystem storage
|
886
|
+
file_system = Shrine.storages[:cache]
|
887
|
+
file_system.clear! { |path| path.mtime < Time.now - 7*24*60*60 } # delete files older than 1 week
|
888
|
+
```
|
889
|
+
```rb
|
890
|
+
# S3 storage
|
891
|
+
s3 = Shrine.storages[:cache]
|
892
|
+
s3.clear! { |object| object.last_modified < Time.now - 7*24*60*60 } # delete files older than 1 week
|
893
|
+
```
|
894
|
+
|
895
|
+
## Logging
|
896
|
+
|
897
|
+
The [`instrumentation`][instrumentation plugin] plugin sends and logs events for
|
898
|
+
important operations:
|
899
|
+
|
900
|
+
```rb
|
901
|
+
Shrine.plugin :instrumentation, notifications: ActiveSupport::Notifications
|
902
|
+
|
903
|
+
uploaded_file = Shrine.upload(io, :store)
|
904
|
+
uploaded_file.exists?
|
905
|
+
uploaded_file.download
|
906
|
+
uploaded_file.delete
|
907
|
+
```
|
908
|
+
```plaintext
|
909
|
+
Metadata (32ms) – {:storage=>:store, :io=>StringIO, :uploader=>Shrine}
|
910
|
+
Upload (1523ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :io=>StringIO, :upload_options=>{}, :uploader=>Shrine}
|
911
|
+
Exists (755ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}
|
912
|
+
Download (1002ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :download_options=>{}, :uploader=>Shrine}
|
913
|
+
Delete (700ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}
|
914
|
+
```
|
915
|
+
|
916
|
+
Some plugins add their own instrumentation as well when they detect that the
|
917
|
+
`instrumentation` plugin has been loaded. For that to work, the
|
918
|
+
`instrumentation` plugin needs to be loaded *before* any of these plugins.
|
919
|
+
|
920
|
+
| Plugin | Instrumentation |
|
921
|
+
| :----- | :-------------- |
|
922
|
+
| `derivation_endpoint` | instruments file processing |
|
923
|
+
| `derivatives` | instruments file processing |
|
924
|
+
| `determine_mime_type` | instruments analyzing MIME type |
|
925
|
+
| `store_dimensions` | instruments extracting image dimensions |
|
926
|
+
| `signature` | instruments calculating signature |
|
927
|
+
| `infer_extension` | instruments inferring extension |
|
928
|
+
| `remote_url` | instruments remote URL downloading |
|
929
|
+
| `data_uri` | instruments data URI parsing |
|
930
|
+
|
931
|
+
For instrumentation, warnings, and other logging, Shrine uses its internal
|
932
|
+
logger. You can tell Shrine to use a different logger. For example, if you're
|
933
|
+
using Rails, you might want to tell it to use the Rails logger:
|
934
|
+
|
935
|
+
```rb
|
936
|
+
Shrine.logger = Rails.logger
|
937
|
+
```
|
938
|
+
|
939
|
+
In tests you might want to tell Shrine to log only warnings:
|
940
|
+
|
941
|
+
```rb
|
942
|
+
Shrine.logger.level = Logger::WARN
|
943
|
+
```
|
944
|
+
|
945
|
+
[Advantages of Shrine]: https://shrinerb.com/docs/advantages
|
946
|
+
[Creating Plugins]: https://shrinerb.com/docs/creating-plugins
|
947
|
+
[Creating Storages]: https://shrinerb.com/docs/creating-storages
|
948
|
+
[Direct Uploads to S3]: https://shrinerb.com/docs/direct-s3
|
949
|
+
[Extracting Metadata]: https://shrinerb.com/docs/metadata
|
950
|
+
[File Processing]: https://shrinerb.com/docs/processing
|
951
|
+
[File Validation]: https://shrinerb.com/docs/validation
|
952
|
+
[Retrieving Uploads]: https://shrinerb.com/docs/retrieving-uploads
|
953
|
+
[Using Attacher]: https://shrinerb.com/docs/attacher
|
954
|
+
[FileSystem]: https://shrinerb.com/docs/storage/file-system
|
955
|
+
[S3]: https://shrinerb.com/docs/storage/s3
|
956
|
+
[`Shrine::UploadedFile`]: https://shrinerb.com/rdoc/classes/Shrine/UploadedFile/InstanceMethods.html
|
957
|
+
|
958
|
+
[attacher]: #attacher
|
959
|
+
[attachment]: #attachment
|
960
|
+
[backgrounding]: #backgrounding
|
961
|
+
[direct uploads]: #direct-uploads
|
962
|
+
[io abstraction]: #io-abstraction
|
963
|
+
[location]: #location
|
964
|
+
[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
|
+
[presigned upload]: #presigned-direct-upload
|
970
|
+
[resumable upload]: #resumable-direct-upload
|
971
|
+
[storage]: #storage
|
972
|
+
[uploaded file]: #uploaded-file
|
973
|
+
[uploading]: #uploading
|
974
|
+
[uploader]: #uploader
|
975
|
+
[validation]: #validation
|
976
|
+
|
977
|
+
[Adding Direct App Uploads]: https://github.com/shrinerb/shrine/wiki/Adding-Direct-App-Uploads
|
978
|
+
[Adding Resumable Uploads]: https://github.com/shrinerb/shrine/wiki/Adding-Resumable-Uploads
|
979
|
+
[Adding Direct S3 Uploads]: https://github.com/shrinerb/shrine/wiki/Adding-Direct-S3-Uploads
|
980
|
+
[Backgrounding Libraries]: https://github.com/shrinerb/shrine/wiki/Backgrounding-Libraries
|
981
|
+
[Mounting Endpoints]: https://github.com/shrinerb/shrine/wiki/Mounting-Endpoints
|
982
|
+
|
983
|
+
[AWS S3]: https://aws.amazon.com/s3/
|
984
|
+
[MinIO]: https://min.io/
|
985
|
+
[DigitalOcean Spaces]: https://www.digitalocean.com/products/spaces/
|
986
|
+
[Cloudinary]: https://github.com/shrinerb/shrine-cloudinary
|
987
|
+
[GCS]: https://github.com/renchap/shrine-google_cloud_storage
|
988
|
+
[uppy-s3_multipart]: https://github.com/janko/uppy-s3_multipart
|
989
|
+
[tus-ruby-server]: https://github.com/janko/tus-ruby-server
|
990
|
+
|
991
|
+
[Uppy]: https://uppy.io
|
992
|
+
[shrine-tus]: https://github.com/shrinerb/shrine-tus
|
993
|
+
[tus]: https://tus.io
|
994
|
+
[uppy aws-s3-multipart]: https://uppy.io/docs/aws-s3-multipart/
|
995
|
+
[uppy aws-s3]: https://uppy.io/docs/aws-s3/
|
996
|
+
[uppy tus]: https://uppy.io/docs/tus/
|
997
|
+
[uppy xhr-upload]: https://uppy.io/docs/xhr-upload/
|
998
|
+
|
999
|
+
[ImageProcessing]: https://github.com/janko/image_processing
|
1000
|
+
[ImageProcessing::MiniMagick]: https://github.com/janko/image_processing/blob/master/doc/minimagick.md#readme
|
1001
|
+
[ImageProcessing::Vips]: https://github.com/janko/image_processing/blob/master/doc/vips.md#readme
|
1002
|
+
[`file`]: http://linux.die.net/man/1/file
|
1003
|
+
|
1004
|
+
[activerecord plugin]: https://shrinerb.com/docs/plugins/activerecord
|
1005
|
+
[add_metadata plugin]: https://shrinerb.com/docs/plugins/add_metadata
|
1006
|
+
[backgrounding plugin]: https://shrinerb.com/docs/plugins/backgrounding
|
1007
|
+
[derivation_endpoint plugin]: https://shrinerb.com/docs/plugins/derivation_endpoint
|
1008
|
+
[derivatives plugin]: https://shrinerb.com/docs/plugins/derivatives
|
1009
|
+
[determine_mime_type plugin]: https://shrinerb.com/docs/plugins/determine_mime_type
|
1010
|
+
[instrumentation plugin]: https://shrinerb.com/docs/plugins/instrumentation
|
1011
|
+
[hanami plugin]: https://github.com/katafrakt/hanami-shrine
|
1012
|
+
[model plugin]: https://shrinerb.com/docs/plugins/model
|
1013
|
+
[entity plugin]: https://shrinerb.com/docs/plugins/entity
|
1014
|
+
[mongoid plugin]: https://github.com/shrinerb/shrine-mongoid
|
1015
|
+
[presign_endpoint plugin]: https://shrinerb.com/docs/plugins/presign_endpoint
|
1016
|
+
[pretty_location plugin]: https://shrinerb.com/docs/plugins/pretty_location
|
1017
|
+
[rack_file plugin]: https://shrinerb.com/docs/plugins/rack_file
|
1018
|
+
[rom plugin]: https://github.com/shrinerb/shrine-rom
|
1019
|
+
[sequel plugin]: https://shrinerb.com/docs/plugins/sequel
|
1020
|
+
[signature plugin]: https://shrinerb.com/docs/plugins/signature
|
1021
|
+
[store_dimensions plugin]: https://shrinerb.com/docs/plugins/store_dimensions
|
1022
|
+
[upload_endpoint plugin]: https://shrinerb.com/docs/plugins/upload_endpoint
|
1023
|
+
[validation_helpers plugin]: https://shrinerb.com/docs/plugins/validation_helpers
|
1024
|
+
[validation plugin]: https://shrinerb.com/docs/plugins/validation
|
1025
|
+
|
1026
|
+
[rails demo]: https://github.com/erikdahlstrand/shrine-rails-example
|
1027
|
+
[roda demo]: https://github.com/shrinerb/shrine/tree/master/demo
|
1028
|
+
[resumable demo]: https://github.com/shrinerb/shrine-tus-demo
|
1029
|
+
|
1030
|
+
[`#read`]: https://ruby-doc.org/core/IO.html#method-i-read
|
1031
|
+
[`#eof?`]: https://ruby-doc.org/core/IO.html#method-i-eof
|
1032
|
+
[`#rewind`]: https://ruby-doc.org/core/IO.html#method-i-rewind
|
1033
|
+
[`#close`]: https://ruby-doc.org/core/IO.html#method-i-close
|
1034
|
+
[`IO`]: https://ruby-doc.org/core/IO.html
|
1035
|
+
|
1036
|
+
[storages]: https://shrinerb.com/docs/external/extensions#storages
|
1037
|
+
[plugins]: https://shrinerb.com/plugins
|
1038
|
+
[external plugins]: https://shrinerb.com/docs/external/extensions#plugins
|