shrine 2.19.3 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +523 -41
- data/LICENSE.txt +1 -1
- data/README.md +83 -979
- data/doc/advantages.md +231 -204
- data/doc/attacher.md +304 -153
- data/doc/carrierwave.md +297 -226
- data/doc/changing_derivatives.md +308 -0
- data/doc/changing_location.md +103 -21
- data/doc/changing_storage.md +110 -0
- data/doc/creating_persistence_plugins.md +132 -0
- data/doc/creating_plugins.md +43 -23
- data/doc/creating_storages.md +19 -5
- data/doc/design.md +147 -97
- data/doc/direct_s3.md +38 -28
- data/doc/external/articles.md +63 -0
- data/doc/external/extensions.md +53 -0
- data/doc/external/misc.md +32 -0
- data/doc/getting_started.md +1156 -0
- data/doc/metadata.md +190 -109
- data/doc/multiple_files.md +93 -30
- data/doc/paperclip.md +384 -262
- data/doc/plugins/activerecord.md +177 -46
- data/doc/plugins/add_metadata.md +139 -38
- data/doc/plugins/atomic_helpers.md +217 -0
- data/doc/plugins/backgrounding.md +156 -98
- data/doc/plugins/cached_attachment_data.md +7 -5
- data/doc/plugins/column.md +121 -0
- data/doc/plugins/data_uri.md +23 -22
- data/doc/plugins/default_storage.md +36 -10
- data/doc/plugins/default_url.md +30 -13
- data/doc/plugins/delete_raw.md +4 -2
- data/doc/plugins/derivation_endpoint.md +186 -101
- data/doc/plugins/derivatives.md +839 -0
- data/doc/plugins/determine_mime_type.md +4 -2
- data/doc/plugins/download_endpoint.md +64 -8
- data/doc/plugins/dynamic_storage.md +5 -3
- data/doc/plugins/entity.md +263 -0
- data/doc/plugins/form_assign.md +55 -0
- data/doc/plugins/included.md +31 -8
- data/doc/plugins/infer_extension.md +21 -10
- data/doc/plugins/instrumentation.md +38 -16
- data/doc/plugins/keep_files.md +16 -17
- data/doc/plugins/metadata_attributes.md +42 -13
- data/doc/plugins/mirroring.md +118 -0
- data/doc/plugins/model.md +210 -0
- data/doc/plugins/module_include.md +4 -2
- data/doc/plugins/multi_cache.md +24 -0
- data/doc/plugins/persistence.md +101 -0
- data/doc/plugins/presign_endpoint.md +9 -4
- data/doc/plugins/pretty_location.md +16 -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 +49 -9
- data/doc/plugins/remote_url.md +84 -47
- data/doc/plugins/remove_attachment.md +27 -6
- data/doc/plugins/remove_invalid.md +21 -6
- data/doc/plugins/restore_cached_data.md +11 -3
- data/doc/plugins/sequel.md +159 -35
- data/doc/plugins/signature.md +16 -5
- data/doc/plugins/store_dimensions.md +14 -2
- data/doc/plugins/tempfile.md +4 -2
- data/doc/plugins/type_predicates.md +96 -0
- data/doc/plugins/upload_endpoint.md +13 -13
- data/doc/plugins/upload_options.md +6 -4
- data/doc/plugins/{default_url_options.md → url_options.md} +9 -7
- data/doc/plugins/validation.md +97 -0
- data/doc/plugins/validation_helpers.md +16 -13
- data/doc/plugins/versions.md +15 -19
- data/doc/processing.md +438 -221
- data/doc/refile.md +188 -170
- data/doc/release_notes/1.0.0.md +4 -0
- data/doc/release_notes/1.1.0.md +6 -2
- 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 +5 -1
- 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 +11 -7
- 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 +6 -3
- 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 +981 -0
- data/doc/release_notes/3.0.1.md +22 -0
- data/doc/release_notes/3.1.0.md +73 -0
- data/doc/release_notes/3.2.0.md +96 -0
- data/doc/release_notes/3.2.1.md +31 -0
- data/doc/release_notes/3.2.2.md +14 -0
- data/doc/release_notes/3.3.0.md +105 -0
- data/doc/release_notes/3.4.0.md +35 -0
- data/doc/release_notes/3.5.0.md +63 -0
- data/doc/release_notes/3.6.0.md +23 -0
- data/doc/retrieving_uploads.md +5 -2
- data/doc/securing_uploads.md +60 -37
- data/doc/storage/file_system.md +20 -3
- data/doc/storage/memory.md +19 -0
- data/doc/storage/s3.md +122 -78
- data/doc/testing.md +141 -133
- data/doc/upgrading_to_3.md +708 -0
- data/doc/validation.md +54 -90
- data/lib/shrine/attacher.rb +292 -169
- data/lib/shrine/attachment.rb +13 -46
- data/lib/shrine/plugins/_persistence.rb +93 -0
- data/lib/shrine/plugins/activerecord.rb +77 -34
- data/lib/shrine/plugins/add_metadata.rb +25 -17
- data/lib/shrine/plugins/atomic_helpers.rb +119 -0
- data/lib/shrine/plugins/backgrounding.rb +77 -113
- data/lib/shrine/plugins/cached_attachment_data.rb +6 -15
- data/lib/shrine/plugins/column.rb +102 -0
- data/lib/shrine/plugins/data_uri.rb +38 -36
- data/lib/shrine/plugins/default_storage.rb +45 -15
- data/lib/shrine/plugins/default_url.rb +12 -24
- data/lib/shrine/plugins/default_url_options.rb +3 -30
- data/lib/shrine/plugins/delete_raw.rb +10 -16
- data/lib/shrine/plugins/derivation_endpoint.rb +130 -171
- data/lib/shrine/plugins/derivatives.rb +645 -0
- data/lib/shrine/plugins/determine_mime_type.rb +9 -21
- data/lib/shrine/plugins/download_endpoint.rb +118 -133
- data/lib/shrine/plugins/dynamic_storage.rb +5 -11
- data/lib/shrine/plugins/entity.rb +158 -0
- data/lib/shrine/plugins/form_assign.rb +108 -0
- data/lib/shrine/plugins/included.rb +6 -6
- data/lib/shrine/plugins/infer_extension.rb +17 -20
- data/lib/shrine/plugins/instrumentation.rb +59 -43
- data/lib/shrine/plugins/keep_files.rb +3 -15
- data/lib/shrine/plugins/metadata_attributes.rb +28 -19
- data/lib/shrine/plugins/mirroring.rb +142 -0
- data/lib/shrine/plugins/model.rb +160 -0
- data/lib/shrine/plugins/module_include.rb +3 -3
- data/lib/shrine/plugins/multi_cache.rb +27 -0
- data/lib/shrine/plugins/presign_endpoint.rb +27 -28
- data/lib/shrine/plugins/pretty_location.rb +15 -9
- data/lib/shrine/plugins/processing.rb +22 -9
- data/lib/shrine/plugins/rack_file.rb +2 -42
- data/lib/shrine/plugins/rack_response.rb +21 -10
- data/lib/shrine/plugins/recache.rb +6 -5
- data/lib/shrine/plugins/refresh_metadata.rb +13 -11
- data/lib/shrine/plugins/remote_url.rb +49 -49
- data/lib/shrine/plugins/remove_attachment.rb +12 -6
- data/lib/shrine/plugins/remove_invalid.rb +19 -8
- data/lib/shrine/plugins/restore_cached_data.rb +13 -7
- data/lib/shrine/plugins/sequel.rb +86 -36
- data/lib/shrine/plugins/signature.rb +10 -16
- data/lib/shrine/plugins/store_dimensions.rb +35 -40
- data/lib/shrine/plugins/tempfile.rb +1 -3
- data/lib/shrine/plugins/type_predicates.rb +113 -0
- data/lib/shrine/plugins/upload_endpoint.rb +28 -24
- data/lib/shrine/plugins/upload_options.rb +14 -15
- data/lib/shrine/plugins/url_options.rb +31 -0
- data/lib/shrine/plugins/validation.rb +80 -0
- data/lib/shrine/plugins/validation_helpers.rb +35 -58
- data/lib/shrine/plugins/versions.rb +107 -87
- data/lib/shrine/plugins.rb +22 -0
- data/lib/shrine/storage/file_system.rb +46 -64
- data/lib/shrine/storage/linter.rb +42 -7
- data/lib/shrine/storage/memory.rb +49 -0
- data/lib/shrine/storage/s3.rb +173 -160
- data/lib/shrine/uploaded_file.rb +32 -32
- data/lib/shrine/version.rb +3 -3
- data/lib/shrine.rb +87 -150
- data/shrine.gemspec +11 -12
- metadata +92 -82
- data/doc/migrating_storage.md +0 -76
- data/doc/plugins/backup.md +0 -31
- data/doc/plugins/copy.md +0 -24
- data/doc/plugins/delete_promoted.md +0 -12
- data/doc/plugins/direct_upload.md +0 -172
- data/doc/plugins/hooks.md +0 -58
- data/doc/plugins/logging.md +0 -42
- data/doc/plugins/migration_helpers.md +0 -60
- data/doc/plugins/moving.md +0 -19
- data/doc/plugins/multi_delete.md +0 -20
- data/doc/plugins/parallelize.md +0 -16
- data/doc/plugins/parsed_json.md +0 -23
- data/doc/regenerating_versions.md +0 -143
- data/lib/shrine/plugins/background_helpers.rb +0 -5
- data/lib/shrine/plugins/backup.rb +0 -90
- data/lib/shrine/plugins/copy.rb +0 -50
- data/lib/shrine/plugins/delete_promoted.rb +0 -20
- data/lib/shrine/plugins/direct_upload.rb +0 -217
- data/lib/shrine/plugins/hooks.rb +0 -90
- data/lib/shrine/plugins/logging.rb +0 -142
- data/lib/shrine/plugins/migration_helpers.rb +0 -70
- data/lib/shrine/plugins/moving.rb +0 -57
- data/lib/shrine/plugins/multi_delete.rb +0 -32
- data/lib/shrine/plugins/parallelize.rb +0 -78
- data/lib/shrine/plugins/parsed_json.rb +0 -29
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,
|
|
97
|
-
uploader.plugin :
|
|
110
|
+
def self.load_dependencies(uploader, **opts)
|
|
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
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
id: creating-storages
|
|
3
|
+
title: Writing a Storage
|
|
4
|
+
---
|
|
2
5
|
|
|
3
6
|
Shrine ships with the FileSystem and S3 storages, but it's also easy to create
|
|
4
7
|
your own. A storage is a class which needs to implement `#upload`, `#url`,
|
|
@@ -122,6 +125,8 @@ The storage can support additional options to customize how the file will be
|
|
|
122
125
|
opened, `Shrine::UploadedFile#open` and `Shrine::UploadedFile#download` will
|
|
123
126
|
forward any given options to `#open`.
|
|
124
127
|
|
|
128
|
+
When file is not found, `Shrine::FileNotFound` exception should be raised.
|
|
129
|
+
|
|
125
130
|
## Url
|
|
126
131
|
|
|
127
132
|
The `#url` storage method is called by `Shrine::UploadedFile#url`, it accepts a
|
|
@@ -204,15 +209,24 @@ The storage can support additional options to customize how the presign will be
|
|
|
204
209
|
generated, those can be forwarded via the `:presign_options` option on the
|
|
205
210
|
`presign_endpoint` plugin.
|
|
206
211
|
|
|
207
|
-
## Clear
|
|
212
|
+
## Delete Prefixed and Clear
|
|
213
|
+
|
|
214
|
+
There are two methods that are not currently used by shrine, but which it's good
|
|
215
|
+
for storages to provide to allow client code to delete files from storage. If
|
|
216
|
+
storages provide these conventional methods, then clients can delete files using
|
|
217
|
+
consistent API for any storage.
|
|
208
218
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
219
|
+
`#clear!` deletes all files from storage, and `#delete_prefixed` will delete all
|
|
220
|
+
files in a given directory/prefix/path. While not strictly required for shrine storage
|
|
221
|
+
service functionality, storages should usually implement if possible.
|
|
212
222
|
|
|
213
223
|
```rb
|
|
214
224
|
class MyStorage
|
|
215
225
|
# ...
|
|
226
|
+
def delete_prefixed(prefix_path)
|
|
227
|
+
# deletes all files under the supplied argument prefix
|
|
228
|
+
end
|
|
229
|
+
|
|
216
230
|
def clear!
|
|
217
231
|
# deletes all files in the storage
|
|
218
232
|
end
|
data/doc/design.md
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: The Design of Shrine
|
|
3
|
+
---
|
|
2
4
|
|
|
3
|
-
*If you want an in-depth walkthrough through the Shrine codebase, see [Notes on
|
|
5
|
+
*If you want an in-depth walkthrough through the Shrine codebase, see [Notes on
|
|
6
|
+
study of shrine implementation] article by Jonathan Rochkind.*
|
|
4
7
|
|
|
5
|
-
There are five main types of
|
|
8
|
+
There are five main types of classes that you deal with in Shrine:
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
| Class | Description |
|
|
11
|
+
| :---- | :---------- |
|
|
12
|
+
| [`Shrine::Storage::*`](#Storage) | Manages files on a particular storage service |
|
|
13
|
+
| [`Shrine`](#Shrine) | Wraps uploads and handles loading plugins |
|
|
14
|
+
| [`Shrine::UploadedFile`](#shrineuploadedfile) | Represents a file uploaded to a storage |
|
|
15
|
+
| [`Shrine::Attacher`](#shrineattacher) | Handles file attachment logic |
|
|
16
|
+
| [`Shrine::Attachment`](#shrineattachment) | Provides convenience model attachment interface |
|
|
12
17
|
|
|
13
18
|
## Storage
|
|
14
19
|
|
|
15
20
|
On the lowest level we have a storage. A storage class encapsulates file
|
|
16
21
|
management logic on a particular service. It is what actually performs uploads,
|
|
17
22
|
generation of URLs, deletions and similar. By convention it is namespaced under
|
|
18
|
-
`Shrine::Storage
|
|
23
|
+
`Shrine::Storage::*`.
|
|
19
24
|
|
|
20
25
|
```rb
|
|
21
26
|
filesystem = Shrine::Storage::FileSystem.new("uploads")
|
|
@@ -24,7 +29,7 @@ filesystem.url("foo") #=> "uploads/foo"
|
|
|
24
29
|
filesystem.delete("foo")
|
|
25
30
|
```
|
|
26
31
|
|
|
27
|
-
A storage is a PORO which
|
|
32
|
+
A storage is a PORO which implements the following interface:
|
|
28
33
|
|
|
29
34
|
```rb
|
|
30
35
|
class Shrine
|
|
@@ -34,7 +39,7 @@ class Shrine
|
|
|
34
39
|
# uploads `io` to the location `id`
|
|
35
40
|
end
|
|
36
41
|
|
|
37
|
-
def open(id)
|
|
42
|
+
def open(id, **options)
|
|
38
43
|
# returns the remote file as an IO-like object
|
|
39
44
|
end
|
|
40
45
|
|
|
@@ -46,7 +51,7 @@ class Shrine
|
|
|
46
51
|
# deletes the file from the storage
|
|
47
52
|
end
|
|
48
53
|
|
|
49
|
-
def url(id, options
|
|
54
|
+
def url(id, **options)
|
|
50
55
|
# URL to the remote file, accepts options for customizing the URL
|
|
51
56
|
end
|
|
52
57
|
end
|
|
@@ -54,24 +59,24 @@ class Shrine
|
|
|
54
59
|
end
|
|
55
60
|
```
|
|
56
61
|
|
|
57
|
-
Storages are typically not used directly, but through `Shrine
|
|
62
|
+
Storages are typically not used directly, but through [`Shrine`](#shrine) and
|
|
63
|
+
[`Shrine::UploadedFile`](#shrine-uploadedfile) classes.
|
|
58
64
|
|
|
59
65
|
## `Shrine`
|
|
60
66
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
name:
|
|
67
|
+
The `Shrine` class (also called an "uploader") primarily provides a wrapper
|
|
68
|
+
method around `Storage#upload`. First, the storage needs to be registered under
|
|
69
|
+
a name:
|
|
64
70
|
|
|
65
71
|
```rb
|
|
66
|
-
Shrine.storages[:
|
|
72
|
+
Shrine.storages[:disk] = Shrine::Storage::FileSystem.new("uploads")
|
|
67
73
|
```
|
|
68
74
|
|
|
69
|
-
Now we can
|
|
75
|
+
Now we can upload files to the registered storage:
|
|
70
76
|
|
|
71
77
|
```rb
|
|
72
|
-
|
|
73
|
-
uploaded_file =
|
|
74
|
-
uploaded_file #=> #<Shrine::UploadedFile>
|
|
78
|
+
uploaded_file = Shrine.upload(file, :disk)
|
|
79
|
+
uploaded_file #=> #<Shrine::UploadedFile storage=:disk id="6a9fb596cc554efb" ...>
|
|
75
80
|
```
|
|
76
81
|
|
|
77
82
|
The argument to `Shrine#upload` must be an IO-like object. The method does the
|
|
@@ -83,63 +88,97 @@ following:
|
|
|
83
88
|
* closes the file
|
|
84
89
|
* creates a `Shrine::UploadedFile` from the data
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
plugin
|
|
91
|
+
### Plugins
|
|
92
|
+
|
|
93
|
+
The `Shrine` class is also used for loading plugins, which provide additional
|
|
94
|
+
functionality by extending core classes.
|
|
95
|
+
|
|
96
|
+
```rb
|
|
97
|
+
Shrine.plugin :derivatives
|
|
98
|
+
|
|
99
|
+
Shrine::UploadedFile.ancestors #=> [..., Shrine::Plugins::Derivatives::FileMethods, Shrine::UploadedFile::InstanceMethods, ...]
|
|
100
|
+
Shrine::Attacher.ancestors #=> [..., Shrine::Plugins::Derivatives::AttacherMethods, Shrine::Attacher::InstanceMethods, ...]
|
|
101
|
+
Shrine::Attachment.ancestors #=> [..., Shrine::Plugins::Derivatives::AttachmentMethods, Shrine::Attachment::InstanceMethods, ...]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The plugins store their configuration in `Shrine.opts`:
|
|
105
|
+
|
|
106
|
+
```rb
|
|
107
|
+
Shrine.plugin :derivation_endpoint, secret_key: "foo"
|
|
108
|
+
Shrine.plugin :default_storage, store: :other_store
|
|
109
|
+
Shrine.plugin :activerecord
|
|
110
|
+
|
|
111
|
+
Shrine.opts #=>
|
|
112
|
+
# { derivation_endpoint: { options: { secret_key: "foo" }, derivations: {} },
|
|
113
|
+
# default_storage: { store: :other_store },
|
|
114
|
+
# column: { serializer: Shrine::Plugins::Column::JsonSerializer },
|
|
115
|
+
# model: { cache: true },
|
|
116
|
+
# activerecord: { callbacks: true, validations: true } }
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Each `Shrine` subclass has its own copy of the core classes, storages and
|
|
120
|
+
options, which makes it possible to customize attachment logic per uploader.
|
|
121
|
+
|
|
122
|
+
```rb
|
|
123
|
+
MyUploader = Class.new(Shrine)
|
|
124
|
+
MyUploader::UploadedFile.superclass #=> Shrine::UploadedFile
|
|
125
|
+
MyUploader::Attacher.superclass #=> Shrine::Attacher
|
|
126
|
+
MyUploader::Attachment.superclass #=> Shrine::Attachment
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
See [Creating a New Plugin] guide and the [Plugin system of Sequel and Roda]
|
|
130
|
+
article for more details on the design of Shrine's plugin system.
|
|
93
131
|
|
|
94
132
|
## `Shrine::UploadedFile`
|
|
95
133
|
|
|
96
|
-
`Shrine::UploadedFile` represents a file that was uploaded to a
|
|
97
|
-
|
|
98
|
-
|
|
134
|
+
A `Shrine::UploadedFile` object represents a file that was uploaded to a
|
|
135
|
+
storage, containing upload location, storage, and any metadata extracted during
|
|
136
|
+
the upload.
|
|
99
137
|
|
|
100
138
|
```rb
|
|
101
|
-
uploaded_file
|
|
102
|
-
|
|
139
|
+
uploaded_file #=> #<Shrine::UploadedFile id="949sdjg834.jpg" storage=:store metadata={...}>
|
|
140
|
+
|
|
141
|
+
uploaded_file.id #=> "949sdjg834.jpg"
|
|
142
|
+
uploaded_file.storage_key #=> :store
|
|
143
|
+
uploaded_file.storage #=> #<Shrine::Storage::S3>
|
|
144
|
+
uploaded_file.metadata #=> {...}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
It has convenience methods for accessing metadata:
|
|
148
|
+
|
|
149
|
+
```rb
|
|
150
|
+
uploaded_file.metadata #=>
|
|
103
151
|
# {
|
|
104
|
-
# "
|
|
105
|
-
# "
|
|
106
|
-
# "
|
|
107
|
-
# "filename" => "resume.pdf",
|
|
108
|
-
# "mime_type" => "application/pdf",
|
|
109
|
-
# "size" => 983294,
|
|
110
|
-
# },
|
|
152
|
+
# "filename" => "matrix.mp4",
|
|
153
|
+
# "mime_type" => "video/mp4",
|
|
154
|
+
# "size" => 345993,
|
|
111
155
|
# }
|
|
156
|
+
|
|
157
|
+
uploaded_file.original_filename #=> "matrix.mp4"
|
|
158
|
+
uploaded_file.extension #=> "mp4"
|
|
159
|
+
uploaded_file.mime_type #=> "video/mp4"
|
|
160
|
+
uploaded_file.size #=> 345993
|
|
112
161
|
```
|
|
113
162
|
|
|
114
|
-
|
|
115
|
-
some metadata: original filename, MIME type and filesize. The
|
|
116
|
-
`Shrine::UploadedFile` object has handy methods which use this data:
|
|
163
|
+
It also has methods that delegate to the storage:
|
|
117
164
|
|
|
118
165
|
```rb
|
|
119
|
-
|
|
120
|
-
uploaded_file.
|
|
121
|
-
uploaded_file.
|
|
122
|
-
uploaded_file.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
# storage methods
|
|
126
|
-
uploaded_file.url
|
|
127
|
-
uploaded_file.exists?
|
|
128
|
-
uploaded_file.open
|
|
129
|
-
uploaded_file.download
|
|
130
|
-
uploaded_file.delete
|
|
131
|
-
# ...
|
|
166
|
+
uploaded_file.url #=> "https://my-bucket.s3.amazonaws.com/949sdjg834.jpg"
|
|
167
|
+
uploaded_file.open { |io| ... } # opens the uploaded file stream
|
|
168
|
+
uploaded_file.download { |file| ... } # downloads the uploaded file to disk
|
|
169
|
+
uploaded_file.stream(destination) # streams uploaded content into a writable destination
|
|
170
|
+
uploaded_file.exists? #=> true
|
|
171
|
+
uploaded_file.delete # deletes the uploaded file from the storage
|
|
132
172
|
```
|
|
133
173
|
|
|
134
|
-
A `Shrine::UploadedFile` is itself an IO-like object (
|
|
135
|
-
|
|
174
|
+
A `Shrine::UploadedFile` is itself an IO-like object (built on top of
|
|
175
|
+
`Storage#open`), so it can be passed to `Shrine#upload` as well.
|
|
136
176
|
|
|
137
177
|
## `Shrine::Attacher`
|
|
138
178
|
|
|
139
179
|
We usually want to treat uploaded files as *attachments* to records, saving
|
|
140
|
-
their data into a database column. This is
|
|
141
|
-
|
|
142
|
-
`Shrine::UploadedFile` objects internally.
|
|
180
|
+
their data into a database column. This is done by `Shrine::Attacher`, which
|
|
181
|
+
internally uses `Shrine` and `Shrine::UploadedFile` classes.
|
|
143
182
|
|
|
144
183
|
The attaching process requires a temporary and a permanent storage to be
|
|
145
184
|
registered (by default that's `:cache` and `:store`):
|
|
@@ -151,71 +190,82 @@ Shrine.storages = {
|
|
|
151
190
|
}
|
|
152
191
|
```
|
|
153
192
|
|
|
154
|
-
A `Shrine::Attacher`
|
|
155
|
-
|
|
193
|
+
A `Shrine::Attacher` can be initialized standalone and handle the common
|
|
194
|
+
attachment flow, which includes dirty tracking (promoting cached file to
|
|
195
|
+
permanent storage, deleting previously attached file), validation, processing,
|
|
196
|
+
serialization etc.
|
|
156
197
|
|
|
157
198
|
```rb
|
|
158
|
-
attacher = Shrine::Attacher.new
|
|
199
|
+
attacher = Shrine::Attacher.new
|
|
159
200
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
attacher.
|
|
201
|
+
# ... user uploads a file ...
|
|
202
|
+
|
|
203
|
+
attacher.assign(io) # uploads to temporary storage
|
|
204
|
+
attacher.file #=> #<Shrine::UploadedFile storage=:cache ...>
|
|
205
|
+
|
|
206
|
+
# ... handle file validations ...
|
|
163
207
|
|
|
164
|
-
attacher.
|
|
165
|
-
attacher.
|
|
166
|
-
attacher.record.image_data #=> "{\"storage\":\"store\",\"id\":\"ksdf02lr9sf3la.jpg\",\"metadata\":{...}}"
|
|
208
|
+
attacher.finalize # uploads to permanent storage
|
|
209
|
+
attacher.file #=> #<Shrine::UploadedFile storage=:store ...>
|
|
167
210
|
```
|
|
168
211
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
permanent storage. Behind the scenes a cached `Shrine::UploadedFile` is given
|
|
172
|
-
to `Shrine#upload`, which works because `Shrine::UploadedFile` is an IO-like
|
|
173
|
-
object. After both caching and promoting the data hash of the uploaded file is
|
|
174
|
-
assigned to the record's column as JSON.
|
|
212
|
+
It can also be initialized with a model instance to handle serialization into a
|
|
213
|
+
model attribute:
|
|
175
214
|
|
|
176
|
-
|
|
215
|
+
```rb
|
|
216
|
+
attacher = Shrine::Attacher.from_model(photo, :image)
|
|
217
|
+
|
|
218
|
+
attacher.assign(file)
|
|
219
|
+
photo.image_data #=> "{\"storage\":\"cache\",\"id\":\"9260ea09d8effd.jpg\",\"metadata\":{...}}"
|
|
220
|
+
|
|
221
|
+
attacher.finalize
|
|
222
|
+
photo.image_data #=> "{\"storage\":\"store\",\"id\":\"ksdf02lr9sf3la.jpg\",\"metadata\":{...}}"
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
For more details, see the [Using Attacher] guide and
|
|
226
|
+
[`entity`][entity]/[`model`][model] plugins.
|
|
177
227
|
|
|
178
228
|
## `Shrine::Attachment`
|
|
179
229
|
|
|
180
|
-
`Shrine::Attachment`
|
|
181
|
-
`Shrine::
|
|
182
|
-
|
|
183
|
-
means that an instance of `Shrine::Attachment` is a module:
|
|
230
|
+
A `Shrine::Attachment` module provides a convenience model interface around the
|
|
231
|
+
`Shrine::Attacher` object. The `Shrine::Attachment` class is a subclass of
|
|
232
|
+
`Module`, which means that an instance of `Shrine::Attachment` is a module:
|
|
184
233
|
|
|
185
234
|
```rb
|
|
186
235
|
Shrine::Attachment.new(:image).is_a?(Module) #=> true
|
|
187
|
-
Shrine::Attachment.new(:image).instance_methods #=> [:image=, :image, :image_url, :image_attacher]
|
|
236
|
+
Shrine::Attachment.new(:image).instance_methods #=> [:image=, :image, :image_url, :image_attacher, ...]
|
|
188
237
|
|
|
189
238
|
# equivalents
|
|
190
239
|
Shrine::Attachment.new(:image)
|
|
240
|
+
Shrine::Attachment[:image]
|
|
191
241
|
Shrine::Attachment(:image)
|
|
192
|
-
Shrine[:image]
|
|
193
242
|
```
|
|
194
243
|
|
|
195
|
-
We can include this module
|
|
244
|
+
We can include this module into a model:
|
|
196
245
|
|
|
197
246
|
```rb
|
|
198
|
-
|
|
199
|
-
include Shrine::Attachment.new(:image)
|
|
200
|
-
end
|
|
247
|
+
Photo.include Shrine::Attachment(:image)
|
|
201
248
|
```
|
|
202
249
|
```rb
|
|
203
|
-
photo.image = file
|
|
204
|
-
photo.image
|
|
205
|
-
photo.image_url
|
|
250
|
+
photo.image = file # shorthand for `photo.image_attacher.assign(file)`
|
|
251
|
+
photo.image # shorthand for `photo.image_attacher.get`
|
|
252
|
+
photo.image_url # shorthand for `photo.image_attacher.url`
|
|
206
253
|
|
|
207
|
-
photo.image_attacher #=> #<Shrine::Attacher
|
|
254
|
+
photo.image_attacher #=> #<Shrine::Attacher @cache_key=:cache @store_key=:store ...>
|
|
208
255
|
```
|
|
209
256
|
|
|
210
|
-
When
|
|
211
|
-
automatically:
|
|
257
|
+
When a persistence plugin is loaded ([`activerecord`][activerecord],
|
|
258
|
+
[`sequel`][sequel]), the `Shrine::Attachment` module also automatically:
|
|
212
259
|
|
|
213
260
|
* syncs Shrine's validation errors with the record
|
|
214
261
|
* triggers promoting after record is saved
|
|
215
|
-
* deletes the uploaded file if attachment was replaced
|
|
216
|
-
destroyed
|
|
262
|
+
* deletes the uploaded file if attachment was replaced or the record destroyed
|
|
217
263
|
|
|
218
|
-
[Using Attacher]: /
|
|
264
|
+
[Using Attacher]: https://shrinerb.com/docs/attacher
|
|
219
265
|
[Notes on study of shrine implementation]: https://bibwild.wordpress.com/2018/09/12/notes-on-study-of-shrine-implementation/
|
|
220
|
-
[Creating a New Plugin]: /
|
|
266
|
+
[Creating a New Plugin]: https://shrinerb.com/docs/creating-plugins
|
|
221
267
|
[Plugin system of Sequel and Roda]: https://twin.github.io/the-plugin-system-of-sequel-and-roda/
|
|
268
|
+
[entity]: https://shrinerb.com/docs/plugins/entity
|
|
269
|
+
[model]: https://shrinerb.com/docs/plugins/model
|
|
270
|
+
[activerecord]: https://shrinerb.com/docs/plugins/activerecord
|
|
271
|
+
[sequel]: https://shrinerb.com/docs/plugins/sequel
|
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
|
|
@@ -71,6 +76,7 @@ If you're using [Uppy], this is the recommended CORS configuration for the
|
|
|
71
76
|
<AllowedHeader>x-amz-content-sha256</AllowedHeader>
|
|
72
77
|
<AllowedHeader>content-type</AllowedHeader>
|
|
73
78
|
<AllowedHeader>content-disposition</AllowedHeader>
|
|
79
|
+
<ExposeHeader>ETag</ExposeHeader>
|
|
74
80
|
</CORSRule>
|
|
75
81
|
<CORSRule>
|
|
76
82
|
<AllowedOrigin>*</AllowedOrigin>
|
|
@@ -80,6 +86,31 @@ If you're using [Uppy], this is the recommended CORS configuration for the
|
|
|
80
86
|
</CORSConfiguration>
|
|
81
87
|
```
|
|
82
88
|
|
|
89
|
+
Or in JSON format:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
[
|
|
93
|
+
{
|
|
94
|
+
"AllowedOrigins": [ "https://my-app.com" ],
|
|
95
|
+
"AllowedMethods": [ "GET", "POST", "PUT" ],
|
|
96
|
+
"MaxAgeSeconds": 3000,
|
|
97
|
+
"AllowedHeaders": [
|
|
98
|
+
"Authorization",
|
|
99
|
+
"x-amz-date",
|
|
100
|
+
"x-amz-content-sha256",
|
|
101
|
+
"Content-Type",
|
|
102
|
+
"Content-Disposition"
|
|
103
|
+
],
|
|
104
|
+
"ExposeHeaders": [ "ETag" ]
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"AllowedOrigins": [ "*" ],
|
|
108
|
+
"AllowedMethods": [ "GET" ],
|
|
109
|
+
"MaxAgeSeconds": 3000
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
```
|
|
113
|
+
|
|
83
114
|
Replace `https://my-app.com` with the URL to your app (in development you can
|
|
84
115
|
set this to `*`). Once you've hit "Save", it may take some time for the
|
|
85
116
|
new CORS settings to be applied.
|
|
@@ -247,28 +278,6 @@ additional HTTP requests.
|
|
|
247
278
|
See [this section][metadata direct uploads] or the rationale and instructions
|
|
248
279
|
on how to opt in.
|
|
249
280
|
|
|
250
|
-
## Object data
|
|
251
|
-
|
|
252
|
-
When the cached S3 object is copied to permanent storage, the destination S3
|
|
253
|
-
object will by default inherit any object data that was assigned to the cached
|
|
254
|
-
object via presign parameters. However, S3 will by default also ignore any new
|
|
255
|
-
object parameters that are given to the copy request.
|
|
256
|
-
|
|
257
|
-
Whether object data will be copied or replaced depends on the value of the
|
|
258
|
-
`:metadata_directive` parameter:
|
|
259
|
-
|
|
260
|
-
* `"COPY"` - destination object will inherit source object data and any new data will be ignored (default)
|
|
261
|
-
* `"REPLACE"` - destination object will not inherit any of the source object data and will accept new data
|
|
262
|
-
|
|
263
|
-
You can use the `upload_options` plugin to change the `:metadata_directive`
|
|
264
|
-
option when S3 objects are copied:
|
|
265
|
-
|
|
266
|
-
```rb
|
|
267
|
-
plugin :upload_options, store: -> (io, context) do
|
|
268
|
-
{ metadata_directive: "REPLACE" } if io.is_a?(Shrine::UploadedFile)
|
|
269
|
-
end
|
|
270
|
-
```
|
|
271
|
-
|
|
272
281
|
## Clearing cache
|
|
273
282
|
|
|
274
283
|
Directly uploaded files won't automatically be deleted from your temporary
|
|
@@ -330,8 +339,9 @@ backgrounding library to perform the job with a delay:
|
|
|
330
339
|
```rb
|
|
331
340
|
Shrine.plugin :backgrounding
|
|
332
341
|
|
|
333
|
-
Shrine::Attacher.
|
|
334
|
-
|
|
342
|
+
Shrine::Attacher.promote_block do
|
|
343
|
+
# tells a Sidekiq worker to perform in 3 seconds
|
|
344
|
+
PromoteJob.perform_in(3, self.class.name, record.class.name, record.id, name, file_data)
|
|
335
345
|
end
|
|
336
346
|
```
|
|
337
347
|
|
|
@@ -396,6 +406,6 @@ setup] guide.
|
|
|
396
406
|
[lifecycle Console]: http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html
|
|
397
407
|
[lifecycle API]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#put_bucket_lifecycle_configuration-instance_method
|
|
398
408
|
[Minio]: https://minio.io
|
|
399
|
-
[minio setup]: /
|
|
400
|
-
[metadata direct uploads]: /
|
|
409
|
+
[minio setup]: https://shrinerb.com/docs/testing#minio
|
|
410
|
+
[metadata direct uploads]: https://shrinerb.com/docs/metadata#direct-uploads
|
|
401
411
|
[content_disposition]: https://github.com/shrinerb/content_disposition
|