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.

@@ -7,11 +7,9 @@ class Shrine
7
7
  # [doc/plugins/remove_attachment.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/remove_attachment.md
8
8
  module RemoveAttachment
9
9
  module AttachmentMethods
10
- def initialize(*)
10
+ def initialize(name, **options)
11
11
  super
12
12
 
13
- name = attachment_name
14
-
15
13
  define_method :"remove_#{name}=" do |value|
16
14
  send(:"#{name}_attacher").remove = value
17
15
  end
@@ -10,8 +10,7 @@ class Shrine
10
10
  # Calculates `algorithm` hash of the contents of the IO object, and
11
11
  # encodes it into `format`.
12
12
  def calculate_signature(io, algorithm, format: :hex)
13
- algorithm = algorithm.downcase # support uppercase algorithm names like :MD5
14
- SignatureCalculator.new(algorithm, format: format).call(io)
13
+ SignatureCalculator.new(algorithm.downcase, format: format).call(io)
15
14
  end
16
15
  end
17
16
 
@@ -36,6 +36,27 @@ class Shrine
36
36
  **options,
37
37
  )
38
38
  end
39
+
40
+ # Calls the upload endpoint passing the request information, and
41
+ # returns the Rack response triple.
42
+ #
43
+ # It performs the same mounting logic that Rack and other web
44
+ # frameworks use, and is meant for cases where statically mounting the
45
+ # endpoint in the router isn't enough.
46
+ def upload_response(storage_key, env, **options)
47
+ script_name = env["SCRIPT_NAME"]
48
+ path_info = env["PATH_INFO"]
49
+
50
+ begin
51
+ env["SCRIPT_NAME"] += path_info
52
+ env["PATH_INFO"] = ""
53
+
54
+ upload_endpoint(storage_key, **options).call(env)
55
+ ensure
56
+ env["SCRIPT_NAME"] = script_name
57
+ env["PATH_INFO"] = path_info
58
+ end
59
+ end
39
60
  end
40
61
  end
41
62
 
@@ -95,18 +116,30 @@ class Shrine
95
116
  make_response(uploaded_file, request)
96
117
  end
97
118
 
98
- # Retrieves the "file" multipart request parameter, and returns an
99
- # IO-like object that can be passed to `Shrine#upload`.
119
+ # Retrieves the upload from the request and verifies it.
100
120
  def get_io(request)
101
- file = request.params["file"]
121
+ file = get_multipart_upload(request)
122
+
123
+ verify_size!(file, request)
124
+ verify_checksum!(file, request)
102
125
 
103
- error!(400, "Upload Not Found") if file.nil?
104
- error!(400, "Upload Not Valid") unless file.is_a?(Hash) && file[:tempfile]
105
- error!(413, "Upload Too Large") if @max_size && file[:tempfile].size > @max_size
126
+ file
127
+ end
128
+
129
+ # Retrieves the file from "file" or "files[]" multipart POST param, and
130
+ # converts it into an IO-like object that can be passed to `Shrine#upload`.
131
+ def get_multipart_upload(request)
132
+ if request.params.key?("file")
133
+ value = request.params["file"]
134
+ elsif request.params["files"].is_a?(Array)
135
+ error!(400, "Too Many Files") if request.params["files"].count > 1
136
+ value = request.params["files"].first
137
+ end
106
138
 
107
- verify_checksum!(file[:tempfile], request.env["HTTP_CONTENT_MD5"]) if request.env["HTTP_CONTENT_MD5"]
139
+ error!(400, "Upload Not Found") if value.nil?
140
+ error!(400, "Upload Not Valid") unless value.is_a?(Hash) && value[:tempfile]
108
141
 
109
- @shrine_class.rack_file(file)
142
+ @shrine_class.rack_file(value)
110
143
  end
111
144
 
112
145
  # Returns a hash of information containing `:action` and `:request`
@@ -137,12 +170,32 @@ class Shrine
137
170
  if @rack_response
138
171
  @rack_response.call(object, request)
139
172
  else
140
- [200, { "Content-Type" => CONTENT_TYPE_JSON }, [object.to_json]]
173
+ if @url
174
+ url = case @url
175
+ when true then object.url
176
+ when Hash then object.url(**@url)
177
+ else @url.call(object, request)
178
+ end
179
+
180
+ body = { data: object, url: url }.to_json
181
+ else
182
+ body = object.to_json
183
+ end
184
+
185
+ [200, { "Content-Type" => CONTENT_TYPE_JSON }, [body]]
141
186
  end
142
187
  end
143
188
 
189
+ def verify_size!(file, request)
190
+ error!(413, "Upload Too Large") if @max_size && file.size > @max_size
191
+ end
192
+
144
193
  # Verifies the provided checksum against the received file.
145
- def verify_checksum!(file, provided_checksum)
194
+ def verify_checksum!(file, request)
195
+ return unless request.env.key?("HTTP_CONTENT_MD5")
196
+
197
+ provided_checksum = request.env["HTTP_CONTENT_MD5"]
198
+
146
199
  error!(400, "The Content-MD5 you specified was invalid") if provided_checksum.length != 24
147
200
 
148
201
  calculated_checksum = Digest::MD5.file(file.path).base64digest
@@ -5,11 +5,13 @@ begin
5
5
  require "aws-sdk-s3"
6
6
  if Gem::Version.new(Aws::S3::GEM_VERSION) < Gem::Version.new("1.2.0")
7
7
  raise "Shrine::Storage::S3 requires aws-sdk-s3 version 1.2.0 or above"
8
+ elsif Gem::Version.new(Aws::S3::GEM_VERSION) < Gem::Version.new("1.14.0")
9
+ Shrine.deprecation("Using aws-sdk-s3 < 1.14 is deprecated and support for it will be removed in Shrine 3. Update to aws-sdk-s3 version 1.14 or higher")
8
10
  end
9
11
  rescue LoadError => exception
10
12
  begin
11
13
  require "aws-sdk"
12
- Shrine.deprecation("Using aws-sdk 2.x is deprecated and support for it will be removed in Shrine 3, use the new aws-sdk-s3 gem instead.")
14
+ Shrine.deprecation("Using aws-sdk 2.x is deprecated and support for it will be removed in Shrine 3. Use the new aws-sdk-s3 gem instead.")
13
15
  Aws.eager_autoload!(services: ["S3"])
14
16
  rescue LoadError
15
17
  raise exception
@@ -115,8 +117,7 @@ class Shrine
115
117
  if copyable?(io)
116
118
  copy(io, id, **options)
117
119
  else
118
- bytes_uploaded = put(io, id, **options)
119
- shrine_metadata["size"] ||= bytes_uploaded
120
+ put(io, id, **options)
120
121
  end
121
122
  end
122
123
 
@@ -153,10 +154,9 @@ class Shrine
153
154
  # typically useful for setting CDN hosts (e.g.
154
155
  # `http://abc123.cloudfront.net`)
155
156
  #
156
- # :download
157
- # : If set to `true`, creates a "forced download" link, which means that
158
- # the browser will never display the file and always ask the user to
159
- # download it.
157
+ # :public
158
+ # : Returns the unsigned URL to the S3 object. This requires the S3
159
+ # object to be public.
160
160
  #
161
161
  # All other options are forwarded to [`Aws::S3::Object#presigned_url`] or
162
162
  # [`Aws::S3::Object#public_url`].
@@ -164,7 +164,10 @@ class Shrine
164
164
  # [`Aws::S3::Object#presigned_url`]: http://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#presigned_url-instance_method
165
165
  # [`Aws::S3::Object#public_url`]: http://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Object.html#public_url-instance_method
166
166
  def url(id, download: nil, public: self.public, host: self.host, **options)
167
- options[:response_content_disposition] ||= "attachment" if download
167
+ if download
168
+ Shrine.deprecation("The :download option in Shrine::Storage::S3#url is deprecated and will be removed in Shrine 3. Use the :response_content_disposition option directly, e.g. `response_content_disposition: \"attachment\"`.")
169
+ options[:response_content_disposition] ||= "attachment"
170
+ end
168
171
  options[:response_content_disposition] = encode_content_disposition(options[:response_content_disposition]) if options[:response_content_disposition]
169
172
 
170
173
  if public || signer
@@ -285,35 +288,27 @@ class Shrine
285
288
 
286
289
  # Uploads the file to S3. Uses multipart upload for large files.
287
290
  def put(io, id, **options)
288
- bytes_uploaded = nil
289
-
290
291
  if (path = extract_path(io))
291
292
  # use `upload_file` for files because it can do multipart upload
292
293
  options = { multipart_threshold: @multipart_threshold[:upload] }.merge!(options)
293
294
  object(id).upload_file(path, **options)
294
- bytes_uploaded = File.size(path)
295
295
  else
296
296
  io.to_io if io.is_a?(UploadedFile) # open if not already opened
297
297
 
298
298
  if io.respond_to?(:size) && io.size && (io.size <= @multipart_threshold[:upload] || !object(id).respond_to?(:upload_stream))
299
299
  object(id).put(body: io, **options)
300
- bytes_uploaded = io.size
301
300
  elsif object(id).respond_to?(:upload_stream)
302
301
  # `upload_stream` uses multipart upload
303
302
  object(id).upload_stream(tempfile: true, **options) do |write_stream|
304
- bytes_uploaded = IO.copy_stream(io, write_stream)
303
+ IO.copy_stream(io, write_stream)
305
304
  end
306
305
  else
307
- Shrine.deprecation "Uploading a file of unknown size with aws-sdk-s3 older than 1.14 is deprecated and will be removed in Shrine 3. Update to aws-sdk-s3 1.14 or higher."
308
-
309
306
  Tempfile.create("shrine-s3", binmode: true) do |file|
310
- bytes_uploaded = IO.copy_stream(io, file.path)
307
+ IO.copy_stream(io, file.path)
311
308
  object(id).upload_file(file.path, **options)
312
309
  end
313
310
  end
314
311
  end
315
-
316
- bytes_uploaded
317
312
  end
318
313
 
319
314
  # Aws::S3::Object#load doesn't support passing options to #head_object,
@@ -7,8 +7,8 @@ class Shrine
7
7
 
8
8
  module VERSION
9
9
  MAJOR = 2
10
- MINOR = 17
11
- TINY = 1
10
+ MINOR = 18
11
+ TINY = 0
12
12
  PRE = nil
13
13
 
14
14
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shrine
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.17.1
4
+ version: 2.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Janko Marohnić
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-15 00:00:00.000000000 Z
11
+ date: 2019-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: down
@@ -433,6 +433,7 @@ files:
433
433
  - doc/release_notes/2.15.0.md
434
434
  - doc/release_notes/2.16.0.md
435
435
  - doc/release_notes/2.17.0.md
436
+ - doc/release_notes/2.18.0.md
436
437
  - doc/release_notes/2.2.0.md
437
438
  - doc/release_notes/2.3.0.md
438
439
  - doc/release_notes/2.3.1.md