Skip to content

Commit

Permalink
Add support for non-file parts with Content-Type
Browse files Browse the repository at this point in the history
Also allow to create file parts without filename.
Fixes httprb#5, httprb#6
  • Loading branch information
Andrei Botalov committed Jan 21, 2017
1 parent 1d15f41 commit b3fac90
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 29 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ socket << "\r\n"
socket << form.to_s
```

It's also possible to create a non-file part with Content-Type:

``` ruby
form = HTTP::FormData.create({
:username => HTTP::FormData::Part.new('{"a": 1}', content_type: 'application/json'),
:avatar_file => HTTP::FormData::File.new("/home/ixti/avatar.png")
})
```

## Supported Ruby Versions

Expand Down
1 change: 1 addition & 0 deletions lib/http/form_data.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require "http/form_data/part"
require "http/form_data/file"
require "http/form_data/multipart"
require "http/form_data/urlencoded"
Expand Down
4 changes: 1 addition & 3 deletions lib/http/form_data/file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@ module FormData
# @example Usage with pathname
#
# FormData::File.new "/home/ixti/avatar.png"
class File
class File < Part
# Default MIME type
DEFAULT_MIME = "application/octet-stream"

attr_reader :mime_type, :filename

# @see DEFAULT_MIME
# @param [String, StringIO, File] file_or_io Filename or IO instance.
# @param [#to_h] opts
Expand Down
45 changes: 19 additions & 26 deletions lib/http/form_data/multipart/param.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,27 @@ module FormData
class Multipart
# Utility class to represent multi-part chunks
class Param
CONTENT_DISPOSITION = ""

# @param [#to_s] name
# @param [FormData::File, #to_s] value
# @param [FormData::File, FormData::Part, #to_s] value
def initialize(name, value)
@name = name.to_s
@value = value
@header = "Content-Disposition: form-data; name=#{@name.inspect}"
@name = name.to_s

@part =
if value.is_a?(FormData::Part)
value
else
FormData::Part.new(value)
end

return unless file?
parameters = { :name => @name }
parameters[:filename] = @part.filename if @part.is_a?(FormData::File)
parameters = parameters.map { |k, v| "#{k}=#{v.inspect}" }.join("; ")

@header = "#{@header}; filename=#{value.filename.inspect}#{CRLF}" \
"Content-Type: #{value.mime_type}"
@header = "Content-Disposition: form-data; #{parameters}"

return unless @part.mime_type

@header += "#{CRLF}Content-Type: #{@part.mime_type}"
end

# Returns body part with headers and data.
Expand All @@ -37,20 +45,14 @@ def initialize(name, value)
#
# @return [String]
def to_s
"#{@header}#{CRLF * 2}#{@value}"
"#{@header}#{CRLF * 2}#{@part}"
end

# Calculates size of a part (headers + body).
#
# @return [Fixnum]
def size
size = @header.bytesize + (CRLF.bytesize * 2)

if file?
size + @value.size
else
size + @value.to_s.bytesize
end
@header.bytesize + (CRLF.bytesize * 2) + @part.size
end

# Flattens given `data` Hash into an array of `Param`'s.
Expand All @@ -70,15 +72,6 @@ def self.coerce(data)

params
end

private

# Tells whenever value is a {FormData::File} or not.
#
# @return [Boolean]
def file?
@value.is_a? FormData::File
end
end
end
end
Expand Down
32 changes: 32 additions & 0 deletions lib/http/form_data/part.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

module HTTP
module FormData
class Part
attr_reader :mime_type, :filename

# @param [#to_s] body
# @param [String] :mime_type
# @param [String] :filename
def initialize(body, mime_type: nil, filename: nil)
@body = body.to_s
@mime_type = mime_type
@filename = filename
end

# Returns content size.
#
# @return [Integer]
def size
@body.bytesize
end

# Returns content of a file of IO.
#
# @return [String]
def to_s
@body
end
end
end
end
46 changes: 46 additions & 0 deletions spec/lib/http/form_data/part_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# frozen_string_literal: true

RSpec.describe HTTP::FormData::Part do
let(:body) { "" }
let(:opts) { {} }

describe "#size" do
subject { described_class.new(body, opts).size }

context "when body given as a String" do
let(:body) { "привет мир!" }
it { is_expected.to eq 20 }
end
end

describe "#to_s" do
subject { described_class.new(body, opts).to_s }

context "when body given as String" do
let(:body) { "привет мир!" }
it { is_expected.to eq "привет мир!" }
end
end

describe "#filename" do
subject { described_class.new(body, opts).filename }

it { is_expected.to eq nil }

context "when it was given with options" do
let(:opts) { { :filename => "foobar.txt" } }
it { is_expected.to eq "foobar.txt" }
end
end

describe "#mime_type" do
subject { described_class.new(body, opts).mime_type }

it { is_expected.to eq nil }

context "when it was given with options" do
let(:opts) { { :mime_type => "application/json" } }
it { is_expected.to eq "application/json" }
end
end
end

0 comments on commit b3fac90

Please sign in to comment.