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