shrine 2.17.1 → 2.18.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 +19 -1
- data/README.md +527 -502
- data/doc/advantages.md +1 -3
- data/doc/attacher.md +22 -11
- data/doc/carrierwave.md +7 -8
- data/doc/design.md +1 -1
- data/doc/direct_s3.md +10 -15
- data/doc/metadata.md +11 -3
- data/doc/paperclip.md +1 -2
- data/doc/plugins/default_url_options.md +1 -1
- data/doc/plugins/derivation_endpoint.md +2 -1
- data/doc/plugins/download_endpoint.md +5 -13
- data/doc/plugins/parsed_json.md +12 -0
- data/doc/plugins/presign_endpoint.md +37 -8
- data/doc/plugins/upload_endpoint.md +72 -19
- data/doc/plugins/versions.md +1 -1
- data/doc/refile.md +6 -9
- data/doc/release_notes/2.17.0.md +2 -2
- data/doc/release_notes/2.18.0.md +155 -0
- data/doc/retrieving_uploads.md +45 -6
- data/doc/storage/s3.md +11 -12
- data/lib/shrine.rb +19 -5
- data/lib/shrine/attacher.rb +1 -1
- data/lib/shrine/plugins/data_uri.rb +1 -3
- data/lib/shrine/plugins/presign_endpoint.rb +21 -0
- data/lib/shrine/plugins/remote_url.rb +1 -3
- data/lib/shrine/plugins/remove_attachment.rb +1 -3
- data/lib/shrine/plugins/signature.rb +1 -2
- data/lib/shrine/plugins/upload_endpoint.rb +63 -10
- data/lib/shrine/storage/s3.rb +13 -18
- data/lib/shrine/version.rb +2 -2
- metadata +3 -2
data/doc/plugins/versions.md
CHANGED
@@ -61,7 +61,7 @@ The plugin also extends the `Attacher#url` to accept versions:
|
|
61
61
|
|
62
62
|
```rb
|
63
63
|
user.avatar_url(:large)
|
64
|
-
user.avatar_url(:small,
|
64
|
+
user.avatar_url(:small, public: true) # with URL options
|
65
65
|
```
|
66
66
|
|
67
67
|
`Shrine.uploaded_file` will also instantiate a hash of `Shrine::UploadedFile`
|
data/doc/refile.md
CHANGED
@@ -14,18 +14,15 @@ named "storages", it uses the same IO abstraction for uploading and representing
|
|
14
14
|
uploaded files, similar attachment logic, and direct uploads are also supported.
|
15
15
|
|
16
16
|
While in Refile you work with storages directly, Shrine uses *uploaders* which
|
17
|
-
|
17
|
+
wrap storage uploads:
|
18
18
|
|
19
19
|
```rb
|
20
20
|
storage = Shrine.storages[:store]
|
21
|
-
storage #=> #<Shrine::Storage::S3
|
21
|
+
storage #=> #<Shrine::Storage::S3>
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
uploaded_file = uploader.upload(image)
|
28
|
-
uploaded_file #=> #<Shrine::UploadedFile>
|
23
|
+
uploaded_file = Shrine.upload(image, :store)
|
24
|
+
uploaded_file #=> #<Shrine::UploadedFile ...>
|
25
|
+
uploaded_file.storage #=> #<Shrine::Storage::S3>
|
29
26
|
```
|
30
27
|
|
31
28
|
This way Shrine can perform tasks like generating location, extracting
|
@@ -352,7 +349,7 @@ In Shrine equivalents are (private) methods `Shrine#extract_filename` and
|
|
352
349
|
#### `.app_url`
|
353
350
|
|
354
351
|
You should use your framework to generate the URL to your mounted direct
|
355
|
-
|
352
|
+
endpoint.
|
356
353
|
|
357
354
|
#### `.attachment_url`, `.file_url`
|
358
355
|
|
data/doc/release_notes/2.17.0.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
endpoint instance, which override any plugin options.
|
5
5
|
|
6
6
|
```rb
|
7
|
-
Shrine.
|
7
|
+
Shrine.download_endpoint(disposition: "attachment")
|
8
8
|
```
|
9
9
|
|
10
10
|
* The `Shrine::Attacher#assign_remote_url` method in the `remote_url` plugin
|
@@ -119,7 +119,7 @@
|
|
119
119
|
anymore, it's now a PORO whose instance responds to `#call`. This shouldn't
|
120
120
|
affect your code unless you were calling Roda methods on that class.
|
121
121
|
|
122
|
-
* The plugin options of `
|
122
|
+
* The plugin options of `upload_endpoint`, `presign_endpoint`, and
|
123
123
|
`download_endpoint` are now internally stored in a different place in
|
124
124
|
`Shrine.opts`. This shouldn't affect your code unless you were accessing
|
125
125
|
these options directly.
|
@@ -0,0 +1,155 @@
|
|
1
|
+
## New features
|
2
|
+
|
3
|
+
* Added `Shrine.upload_response` to `upload_endpoint` plugin for handling
|
4
|
+
uploads inside a custom controller. This allows authenticating uploads on the
|
5
|
+
controller level:
|
6
|
+
|
7
|
+
```rb
|
8
|
+
# config/routes.rb (Rails)
|
9
|
+
Rails.application.routes.draw do
|
10
|
+
# ...
|
11
|
+
post "/images/upload" => "uploads#image"
|
12
|
+
end
|
13
|
+
```
|
14
|
+
```rb
|
15
|
+
# app/controllers/uploads_controller.rb (Rails)
|
16
|
+
class UploadsController < ApplicationController
|
17
|
+
def image
|
18
|
+
authenticate_user!
|
19
|
+
|
20
|
+
set_rack_response ImageUploader.upload_response(:cache, env)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def set_rack_response((status, headers, body))
|
26
|
+
self.status = status
|
27
|
+
self.headers.merge!(headers)
|
28
|
+
self.response_body = body
|
29
|
+
end
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
* Added `Shrine.presign_response` to `presign_endpoint` plugin for handling
|
34
|
+
uploads inside a custom controller. This allows authenticating uploads on the
|
35
|
+
controller level:
|
36
|
+
|
37
|
+
```rb
|
38
|
+
# config/routes.rb (Rails)
|
39
|
+
Rails.application.routes.draw do
|
40
|
+
# ...
|
41
|
+
post "/images/presign", to: "presigns#image"
|
42
|
+
end
|
43
|
+
```
|
44
|
+
```rb
|
45
|
+
# app/controllers/presigns_controller.rb (Rails)
|
46
|
+
class PresignsController < ApplicationController
|
47
|
+
def image
|
48
|
+
authenticate_user!
|
49
|
+
|
50
|
+
set_rack_response ImageUploader.presign_response(:cache, env)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def set_rack_response((status, headers, body))
|
56
|
+
self.status = status
|
57
|
+
self.headers.merge!(headers)
|
58
|
+
self.response_body = body
|
59
|
+
end
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
* The `:url` option has been added to the `upload_endpoint` plugin for
|
64
|
+
returning the uploaded file URL in the response.
|
65
|
+
|
66
|
+
```rb
|
67
|
+
plugin :upload_endpoint, url: true
|
68
|
+
# or
|
69
|
+
plugin :upload_endpoint, url: { public: true }
|
70
|
+
# or
|
71
|
+
plugin :upload_endpoint, url: -> (uploaded_file, request) {
|
72
|
+
uploaded_file.url(**options)
|
73
|
+
}
|
74
|
+
```
|
75
|
+
```rb
|
76
|
+
{
|
77
|
+
"data": { "id": "...", "storage": "...", "metadata": {...} },
|
78
|
+
"url": "https://example.com/path/to/file"
|
79
|
+
}
|
80
|
+
```
|
81
|
+
|
82
|
+
This will additionally be recognized by Uppy, so e.g. the Dashboard plugin
|
83
|
+
will display preview link to the file.
|
84
|
+
|
85
|
+
```js
|
86
|
+
uppy.on('upload-success', (file, response) => {
|
87
|
+
response.uploadURL // => "https://example.com/path/to/file"
|
88
|
+
})
|
89
|
+
```
|
90
|
+
|
91
|
+
## Other improvements
|
92
|
+
|
93
|
+
* The `upload_endpoint` now accepts the `files[]` array that Uppy's XHR Upload
|
94
|
+
plugin sends by default. This means the `fieldName` parameter can now be
|
95
|
+
omitted.
|
96
|
+
|
97
|
+
```js
|
98
|
+
// BEFORE
|
99
|
+
uppy.use(Uppy.XHRUpload, {
|
100
|
+
endpoint: '/upload',
|
101
|
+
fieldName: 'file',
|
102
|
+
})
|
103
|
+
|
104
|
+
// AFTER
|
105
|
+
uppy.use(Uppy.XHRUpload, {
|
106
|
+
endpoint: '/upload',
|
107
|
+
})
|
108
|
+
```
|
109
|
+
|
110
|
+
* The `Shrine.upload` convenience method has been added, which is a bit shorter
|
111
|
+
when you don't need the uploader instance.
|
112
|
+
|
113
|
+
```rb
|
114
|
+
Shrine.upload(io, :storage)
|
115
|
+
|
116
|
+
# expands to
|
117
|
+
|
118
|
+
uploader = Shrine.new(:storage)
|
119
|
+
uploader.upload(io)
|
120
|
+
```
|
121
|
+
|
122
|
+
* The `Shrine.Attachment(...)` shorthand for `Shrine::Attachment.new(...)` has
|
123
|
+
been added.
|
124
|
+
|
125
|
+
```rb
|
126
|
+
class Photo
|
127
|
+
include Shrine::Attachment(:image) # expands to Shrine::Attachment.new(:image)
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
* The `parsed_json` and `rack_file` plugins now correctly retain the second
|
132
|
+
argument in the `Attacher#assign` method signature.
|
133
|
+
|
134
|
+
## Backwards compatibility
|
135
|
+
|
136
|
+
* The `aws-sdk-s3` version lower than `1.14.0` has been deprecated for
|
137
|
+
`Shrine::Storage::S3` and will be removed in Shrine 3.
|
138
|
+
|
139
|
+
* The `:download` option in `Shrine::Storage::S3#url` has been deprecated and
|
140
|
+
will be removed in Shrine 3. The `:response_content_disposition` option
|
141
|
+
should be used instead.
|
142
|
+
|
143
|
+
```rb
|
144
|
+
# This is deprecated:
|
145
|
+
uploaded_file.url(download: true)
|
146
|
+
|
147
|
+
# Use this:
|
148
|
+
uploaded_file.url(response_content_disposition: "attachment")
|
149
|
+
```
|
150
|
+
|
151
|
+
* `Shrine::Storage::S3#upload` doesn't backfill the `size` metadata value for
|
152
|
+
input IOs with unknown size (e.g. pipes, sockets). This behaviour was not
|
153
|
+
documented and added unnecessary complexity. Moreover, this functionality
|
154
|
+
should be storage agnostic, so if someone requests it we can add it back in
|
155
|
+
form of a plugin.
|
data/doc/retrieving_uploads.md
CHANGED
@@ -25,12 +25,9 @@ uploaded_file.eof? # => false
|
|
25
25
|
uploaded_file.close # closes the underlying IO object (this should be called when you're done)
|
26
26
|
```
|
27
27
|
|
28
|
-
|
29
|
-
`Storage#open` method of the underlying Shrine storage.
|
30
|
-
|
31
|
-
`Shrine::Storage::S3` (and most other remote storages) it will be a
|
32
|
-
[`Down::ChunkedIO`] object. `Storage#open` is implicitly called when any of
|
33
|
-
these IO methods are called for the first time.
|
28
|
+
These methods are simply delegated on the IO object returned by the
|
29
|
+
`Storage#open` method of the underlying Shrine storage. `Storage#open` is
|
30
|
+
implicitly called when any of these IO methods are called for the first time.
|
34
31
|
|
35
32
|
```rb
|
36
33
|
uploaded_file.read(10) # calls `Storage#open` and assigns result to an instance variable
|
@@ -45,6 +42,48 @@ You can retrieve the underlying IO object returned by `Storage#open` with
|
|
45
42
|
uploaded_file.to_io # the underlying IO object returned by `Storage#open`
|
46
43
|
```
|
47
44
|
|
45
|
+
## `Storage#open`
|
46
|
+
|
47
|
+
The underlying IO object that `Shrine::UploadedFile` will use depends on the
|
48
|
+
storage. The `FileSystem` storage will return a `File` object, while `S3` and
|
49
|
+
most other remote storages will return [`Down::ChunkedIO`] that downloads file
|
50
|
+
content on-demand.
|
51
|
+
|
52
|
+
```rb
|
53
|
+
Shrine.storages = {
|
54
|
+
file_system: Shrine::Storage::FileSystem.new(...),
|
55
|
+
s3: Shrine::Storage::S3.new(...),
|
56
|
+
}
|
57
|
+
|
58
|
+
local_file = Shrine.upload(file, :file_system)
|
59
|
+
local_file.to_io #=> #<File:/path/to/file>
|
60
|
+
|
61
|
+
remote_file = Shrine.upload(file, :s3)
|
62
|
+
remote_file.to_io #=> #<Down::ChunkedIO> (opens HTTP connection)
|
63
|
+
remote_file.read(1*1024*1024) # downloads first 1MB
|
64
|
+
remote_file.read(1*1024*1024) # downloads next 1MB
|
65
|
+
remote_file.close # closes HTTP connection
|
66
|
+
```
|
67
|
+
|
68
|
+
The `Down::ChunkedIO` object will cache downloaded content to disk in order to
|
69
|
+
be rewindable, which is used in a places such as metadata extraction.
|
70
|
+
|
71
|
+
```rb
|
72
|
+
remote_file.read(1*1024*1024) # downloads and caches first 1MB
|
73
|
+
remote_file.rewind
|
74
|
+
remote_file.read(1*1024*1024) # reads first 1MB from the cache
|
75
|
+
remote_file.read(1*1024*1024) # downloads and caches next 1MB
|
76
|
+
```
|
77
|
+
|
78
|
+
If you want to turn off caching to disk, most storages allow you to pass
|
79
|
+
`:rewindable` to `Storage#open`:
|
80
|
+
|
81
|
+
```rb
|
82
|
+
remote_file.open(rewindable: false)
|
83
|
+
remote_file.read(1*1024*1024) # downloads first 1MB (no caching to disk)
|
84
|
+
remote_file.rewind #~> IOError: this Down::ChunkedIO is not rewindable
|
85
|
+
```
|
86
|
+
|
48
87
|
## Opening
|
49
88
|
|
50
89
|
The `Shrine::UploadedFile#open` method can be used to open the uploaded file
|
data/doc/storage/s3.md
CHANGED
@@ -4,7 +4,7 @@ The S3 storage handles uploads to Amazon S3 service, using the [aws-sdk-s3]
|
|
4
4
|
gem:
|
5
5
|
|
6
6
|
```rb
|
7
|
-
gem "aws-sdk-s3", "~> 1.
|
7
|
+
gem "aws-sdk-s3", "~> 1.14"
|
8
8
|
```
|
9
9
|
|
10
10
|
It can be initialized by providing the bucket name and credentials:
|
@@ -101,18 +101,16 @@ are generated using the storage directly.
|
|
101
101
|
|
102
102
|
## URL options
|
103
103
|
|
104
|
-
|
105
|
-
|
104
|
+
Other than [`:host`](#url-host) and [`:public`](#public-uploads) URL options,
|
105
|
+
all additional options are forwarded to [`Aws::S3::Object#presigned_url`].
|
106
106
|
|
107
107
|
```rb
|
108
|
-
s3.url(
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
```rb
|
115
|
-
s3.url(expires_in: 15, response_content_disposition: "...")
|
108
|
+
s3.url(
|
109
|
+
expires_in: 15,
|
110
|
+
response_content_disposition: ContentDisposition.attachment("my-filename"),
|
111
|
+
response_content_type: "foo/bar",
|
112
|
+
# ...
|
113
|
+
)
|
116
114
|
```
|
117
115
|
|
118
116
|
## URL Host
|
@@ -258,7 +256,7 @@ To use Amazon S3's [Transfer Acceleration] feature, set
|
|
258
256
|
`:use_accelerate_endpoint` to `true` when initializing the storage:
|
259
257
|
|
260
258
|
```rb
|
261
|
-
Shrine::Storage::S3.new(
|
259
|
+
Shrine::Storage::S3.new(use_accelerate_endpoint: true, **other_options)
|
262
260
|
```
|
263
261
|
|
264
262
|
## Clearing cache
|
@@ -285,6 +283,7 @@ can scale exponentially by using more prefixes.
|
|
285
283
|
[uploading]: http://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#put-instance_method
|
286
284
|
[copying]: http://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#copy_from-instance_method
|
287
285
|
[presigning]: http://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#presigned_post-instance_method
|
286
|
+
[`Aws::S3::Object#presigned_url`]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#presigned_url-instance_method
|
288
287
|
[aws-sdk-s3]: https://github.com/aws/aws-sdk-ruby/tree/master/gems/aws-sdk-s3
|
289
288
|
[Transfer Acceleration]: http://docs.aws.amazon.com/AmazonS3/latest/dev/transfer-acceleration.html
|
290
289
|
[object lifecycle]: http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html
|
data/lib/shrine.rb
CHANGED
@@ -91,10 +91,9 @@ class Shrine
|
|
91
91
|
end
|
92
92
|
|
93
93
|
# Retrieves the storage under the given identifier (can be a Symbol or
|
94
|
-
# a String),
|
94
|
+
# a String), raising Shrine::Error if the storage is missing.
|
95
95
|
def find_storage(name)
|
96
|
-
storages.
|
97
|
-
raise Error, "storage #{name.inspect} isn't registered on #{self}"
|
96
|
+
storages[name.to_sym] or fail Error, "storage #{name.inspect} isn't registered on #{self}"
|
98
97
|
end
|
99
98
|
|
100
99
|
# Generates an instance of Shrine::Attachment to be included in the
|
@@ -103,10 +102,18 @@ class Shrine
|
|
103
102
|
# class Photo
|
104
103
|
# include Shrine.attachment(:image) # creates a Shrine::Attachment object
|
105
104
|
# end
|
106
|
-
def
|
105
|
+
def Attachment(name, *args)
|
107
106
|
self::Attachment.new(name, *args)
|
108
107
|
end
|
109
|
-
alias
|
108
|
+
alias attachment Attachment
|
109
|
+
alias [] Attachment
|
110
|
+
|
111
|
+
# Uploads the file to the specified storage. It delegates to `Shrine#upload`.
|
112
|
+
#
|
113
|
+
# Shrine.upload(io, :store) #=> #<Shrine::UploadedFile>
|
114
|
+
def upload(io, storage, context = {})
|
115
|
+
new(storage).upload(io, context)
|
116
|
+
end
|
110
117
|
|
111
118
|
# Instantiates a Shrine::UploadedFile from a hash, and optionally
|
112
119
|
# yields the returned object.
|
@@ -161,6 +168,8 @@ class Shrine
|
|
161
168
|
attr_reader :storage
|
162
169
|
|
163
170
|
# Accepts a storage symbol registered in `Shrine.storages`.
|
171
|
+
#
|
172
|
+
# Shrine.new(:store)
|
164
173
|
def initialize(storage_key)
|
165
174
|
@storage = self.class.find_storage(storage_key)
|
166
175
|
@storage_key = storage_key.to_sym
|
@@ -176,6 +185,11 @@ class Shrine
|
|
176
185
|
# optional context hash (used internally by Shrine::Attacher). It calls
|
177
186
|
# user-defined #process, and afterwards it calls #store. The `io` is
|
178
187
|
# closed after upload.
|
188
|
+
#
|
189
|
+
# uploader.upload(io)
|
190
|
+
# uploader.upload(io, metadata: { "foo" => "bar" }) # add metadata
|
191
|
+
# uploader.upload(io, location: "path/to/file") # specify location
|
192
|
+
# uploader.upload(io, upload_options: { acl: "public-read" }) # add upload options
|
179
193
|
def upload(io, context = {})
|
180
194
|
io = processed(io, context) || io
|
181
195
|
store(io, context)
|
data/lib/shrine/attacher.rb
CHANGED
@@ -52,7 +52,7 @@ class Shrine
|
|
52
52
|
def initialize(record, name, cache: :cache, store: :store)
|
53
53
|
@cache = shrine_class.new(cache)
|
54
54
|
@store = shrine_class.new(store)
|
55
|
-
@context = {record: record, name: name}
|
55
|
+
@context = { record: record, name: name }
|
56
56
|
@errors = []
|
57
57
|
end
|
58
58
|
|
@@ -31,6 +31,27 @@ class Shrine
|
|
31
31
|
**options,
|
32
32
|
)
|
33
33
|
end
|
34
|
+
|
35
|
+
# Calls the presign endpoint passing the request information, and
|
36
|
+
# returns the Rack response triple.
|
37
|
+
#
|
38
|
+
# It performs the same mounting logic that Rack and other web
|
39
|
+
# frameworks use, and is meant for cases where statically mounting the
|
40
|
+
# endpoint in the router isn't enough.
|
41
|
+
def presign_response(storage_key, env, **options)
|
42
|
+
script_name = env["SCRIPT_NAME"]
|
43
|
+
path_info = env["PATH_INFO"]
|
44
|
+
|
45
|
+
begin
|
46
|
+
env["SCRIPT_NAME"] += path_info
|
47
|
+
env["PATH_INFO"] = ""
|
48
|
+
|
49
|
+
presign_endpoint(storage_key, **options).call(env)
|
50
|
+
ensure
|
51
|
+
env["SCRIPT_NAME"] = script_name
|
52
|
+
env["PATH_INFO"] = path_info
|
53
|
+
end
|
54
|
+
end
|
34
55
|
end
|
35
56
|
end
|
36
57
|
|
@@ -17,11 +17,9 @@ class Shrine
|
|
17
17
|
end
|
18
18
|
|
19
19
|
module AttachmentMethods
|
20
|
-
def initialize(
|
20
|
+
def initialize(name, **options)
|
21
21
|
super
|
22
22
|
|
23
|
-
name = attachment_name
|
24
|
-
|
25
23
|
define_method :"#{name}_remote_url=" do |url|
|
26
24
|
send(:"#{name}_attacher").remote_url = url
|
27
25
|
end
|