http-form_data 2.3.0 → 3.0.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +161 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +11 -10
  5. data/http-form_data.gemspec +28 -14
  6. data/lib/http/form_data/composite_io.rb +46 -28
  7. data/lib/http/form_data/file.rb +44 -19
  8. data/lib/http/form_data/multipart/param.rb +18 -48
  9. data/lib/http/form_data/multipart.rb +59 -12
  10. data/lib/http/form_data/part.rb +24 -2
  11. data/lib/http/form_data/readable.rb +24 -6
  12. data/lib/http/form_data/urlencoded.rb +100 -10
  13. data/lib/http/form_data/version.rb +1 -1
  14. data/lib/http/form_data.rb +38 -18
  15. data/sig/http/form_data/composite_io.rbs +32 -0
  16. data/sig/http/form_data/file.rbs +23 -0
  17. data/sig/http/form_data/multipart/param.rbs +23 -0
  18. data/sig/http/form_data/multipart.rbs +40 -0
  19. data/sig/http/form_data/part.rbs +16 -0
  20. data/sig/http/form_data/readable.rbs +19 -0
  21. data/sig/http/form_data/urlencoded.rbs +46 -0
  22. data/sig/http/form_data/version.rbs +5 -0
  23. data/sig/http/form_data.rbs +30 -0
  24. metadata +24 -43
  25. data/.editorconfig +0 -9
  26. data/.gitignore +0 -15
  27. data/.rspec +0 -2
  28. data/.rubocop.yml +0 -64
  29. data/.travis.yml +0 -34
  30. data/.yardopts +0 -2
  31. data/CHANGES.md +0 -96
  32. data/Gemfile +0 -26
  33. data/Guardfile +0 -16
  34. data/Rakefile +0 -24
  35. data/appveyor.yml +0 -8
  36. data/spec/fixtures/expected-multipart-body.tpl +0 -0
  37. data/spec/fixtures/the-http-gem.info +0 -1
  38. data/spec/lib/http/form_data/composite_io_spec.rb +0 -109
  39. data/spec/lib/http/form_data/file_spec.rb +0 -217
  40. data/spec/lib/http/form_data/multipart_spec.rb +0 -157
  41. data/spec/lib/http/form_data/part_spec.rb +0 -74
  42. data/spec/lib/http/form_data/urlencoded_spec.rb +0 -78
  43. data/spec/lib/http/form_data_spec.rb +0 -50
  44. data/spec/spec_helper.rb +0 -83
  45. data/spec/support/fixtures_helper.rb +0 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e781be9d32ee208fa4d379569b05bd2d4e9f30c4b29e0895aaea20e8ae193206
4
- data.tar.gz: 8745d97530d549d45e94f9901a50ba6f0ee88177074b2411c56f03f7ac66134c
3
+ metadata.gz: 3a38607435f5ac942c5b4f3d1e28c5910d151ccb978fbcaff0dc8d51a0932400
4
+ data.tar.gz: fe8f7bf0309d6f8dd178283aa40d2708b69d89a2c2535bf253370759b6b28b07
5
5
  SHA512:
6
- metadata.gz: 12b410ec71a443a006e0ec62005bc4b829987bc562b0d9604d527b1e6cbfe82ea2ef73ce55d46ea3d3496159b3d0a2233dd6be5ab42a8766aaf7a5be78479e27
7
- data.tar.gz: 2644255b6eaaf24b38658894134e62ea45b84dbb8802a4cc920dc2c219e8ca67e9bc432a1d53b84b970b4e26fa833a7d7780c95591b9b0b871eb2ab659fcf13d
6
+ metadata.gz: 36a8dc93a9ce4b47f7e35ebf9ea465baa6e0862660bc10b02623be502385102596c8493de46bb5c85890dba8f72f821e93c53ececeffc121d58c53e058a63e57
7
+ data.tar.gz: f387b4d0f46f4df352cda39732050f059ec178223635d2be008838094560291ee9bb1496920d823cbd422c22bcfb05c17844a632e743578dd5486aec672e057d
data/CHANGELOG.md ADDED
@@ -0,0 +1,161 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [3.0.0] - 2026-03-16
9
+
10
+ ### Added
11
+
12
+ - `Multipart` accepts a `content_type:` keyword to support `multipart/related`,
13
+ `multipart/mixed`, and other multipart content types. Defaults to
14
+ `multipart/form-data`.
15
+ ([#1](https://github.com/httprb/form_data/issues/1))
16
+ - `FormData::File#close` for closing file handles opened from String paths or
17
+ Pathnames. When a File is created from an existing IO, `close` is a no-op.
18
+ ([#27](https://github.com/httprb/form_data/issues/27))
19
+ - Accept any Enumerable (not just Hash or Array) as form data input for both
20
+ `Multipart` and `Urlencoded` encoders. This enables lazy enumerators and
21
+ custom collections as input.
22
+ - `FormData.ensure_data` for coercing input to an Enumerable of key-value pairs.
23
+ - Array of pairs input for `Multipart`, allowing duplicate parameter names and
24
+ preserved ordering.
25
+ - Array of pairs input for `Urlencoded`, preserving duplicate keys.
26
+ - RBS type signatures for all public and private APIs.
27
+ - `sig/` directory shipped in the gem for downstream type checking.
28
+ - `homepage_uri`, `source_code_uri`, `bug_tracker_uri`, and `documentation_uri`
29
+ gemspec metadata.
30
+
31
+ ### Changed
32
+
33
+ - Default urlencoded encoder replaced with a custom implementation that supports
34
+ nested Hashes and Arrays (e.g., `{foo: {bar: "baz"}}` encodes as
35
+ `foo[bar]=baz`). Previously used `URI.encode_www_form`.
36
+ - `FormData.ensure_hash` no longer treats `nil` as a special case; `nil.to_h`
37
+ returns `{}` which is used instead.
38
+
39
+ ### Removed
40
+
41
+ - Ruby < 3.2 support.
42
+ - Explicit JRuby support.
43
+ - `FormData::File#mime_type` deprecated alias. Use `#content_type` instead.
44
+ - `:mime_type` option in `FormData::File#initialize`. Use `:content_type` instead.
45
+
46
+ ## [2.3.0] - 2020-03-08
47
+
48
+ ### Added
49
+
50
+ - Per-instance encoder for `HTTP::FormData::Urlencoded`.
51
+ ([#29](https://github.com/httprb/form_data/pull/29) by [@summera])
52
+
53
+ ## [2.2.0] - 2020-01-09
54
+
55
+ ### Fixed
56
+
57
+ - Ruby 2.7 compatibility.
58
+ ([#28](https://github.com/httprb/form_data/pull/28) by [@janko])
59
+
60
+ ## [2.1.1] - 2018-06-01
61
+
62
+ ### Added
63
+
64
+ - Allow overriding urlencoded form data encoder.
65
+ ([#23](https://github.com/httprb/form_data/pull/23) by [@FabienChaynes])
66
+
67
+ ## [2.1.0] - 2018-03-05
68
+
69
+ ### Fixed
70
+
71
+ - Rewind content at the end of `Readable#to_s`.
72
+ ([#21](https://github.com/httprb/form_data/pull/21) by [@janko-m])
73
+ - Buffer encoding.
74
+ ([#19](https://github.com/httprb/form_data/pull/19) by [@HoneyryderChuck])
75
+
76
+ ## [2.0.0] - 2017-10-01
77
+
78
+ ### Fixed
79
+
80
+ - Add CRLF character to end of multipart body.
81
+ ([#17](https://github.com/httprb/form_data/pull/17) by [@mhickman])
82
+
83
+ ## [2.0.0.pre2] - 2017-05-11
84
+
85
+ ### Added
86
+
87
+ - Streaming for urlencoded form data.
88
+ ([#14](https://github.com/httprb/form_data/pull/14) by [@janko-m])
89
+
90
+ ## [2.0.0.pre1] - 2017-05-10
91
+
92
+ ### Added
93
+
94
+ - Form data streaming.
95
+ ([#12](https://github.com/httprb/form_data.rb/pull/12) by [@janko-m])
96
+
97
+ ## [1.0.2] - 2017-05-08
98
+
99
+ ### Added
100
+
101
+ - Allow setting Content-Type on non-file parts.
102
+ ([#5](https://github.com/httprb/form_data.rb/issues/5) by [@abotalov])
103
+ - Creation of file parts without filename.
104
+ ([#6](https://github.com/httprb/form_data.rb/issues/6) by [@abotalov])
105
+
106
+ ### Deprecated
107
+
108
+ - `HTTP::FormData::File#mime_type`. Use `#content_type` instead.
109
+ ([#11](https://github.com/httprb/form_data.rb/pull/11) by [@ixti])
110
+
111
+ ## [1.0.1] - 2015-03-31
112
+
113
+ ### Fixed
114
+
115
+ - Usage of URI module.
116
+
117
+ ## [1.0.0] - 2015-01-04
118
+
119
+ ### Changed
120
+
121
+ - Gem renamed to `http-form_data` as `FormData` is no longer a top-level
122
+ constant: `FormData` → `HTTP::FormData`.
123
+
124
+ ## [0.1.0] - 2015-01-02
125
+
126
+ ### Added
127
+
128
+ - `nil` support to `FormData#ensure_hash`.
129
+
130
+ ### Changed
131
+
132
+ - Moved repo under `httprb` organization on GitHub.
133
+
134
+ ## [0.0.1] - 2014-12-15
135
+
136
+ ### Added
137
+
138
+ - Initial release.
139
+
140
+ [3.0.0]: https://github.com/httprb/form_data/compare/v2.3.0...v3.0.0
141
+ [2.3.0]: https://github.com/httprb/form_data/compare/v2.2.0...v2.3.0
142
+ [2.2.0]: https://github.com/httprb/form_data/compare/v2.1.1...v2.2.0
143
+ [2.1.1]: https://github.com/httprb/form_data/compare/v2.1.0...v2.1.1
144
+ [2.1.0]: https://github.com/httprb/form_data/compare/v2.0.0...v2.1.0
145
+ [2.0.0]: https://github.com/httprb/form_data/compare/v2.0.0.pre2...v2.0.0
146
+ [2.0.0.pre2]: https://github.com/httprb/form_data/compare/v2.0.0.pre1...v2.0.0.pre2
147
+ [2.0.0.pre1]: https://github.com/httprb/form_data/compare/v1.0.2...v2.0.0.pre1
148
+ [1.0.2]: https://github.com/httprb/form_data/compare/v1.0.1...v1.0.2
149
+ [1.0.1]: https://github.com/httprb/form_data/compare/v1.0.0...v1.0.1
150
+ [1.0.0]: https://github.com/httprb/form_data/compare/v0.1.0...v1.0.0
151
+ [0.1.0]: https://github.com/httprb/form_data/compare/v0.0.1...v0.1.0
152
+ [0.0.1]: https://github.com/httprb/form_data/releases/tag/v0.0.1
153
+
154
+ [@abotalov]: https://github.com/abotalov
155
+ [@FabienChaynes]: https://github.com/FabienChaynes
156
+ [@HoneyryderChuck]: https://github.com/HoneyryderChuck
157
+ [@ixti]: https://github.com/ixti
158
+ [@janko]: https://github.com/janko
159
+ [@janko-m]: https://github.com/janko-m
160
+ [@mhickman]: https://github.com/mhickman
161
+ [@summera]: https://github.com/summera
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015-2017 Alexey V Zapparov
1
+ Copyright (c) 2015-2026 Alexey V Zapparov
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,9 +1,11 @@
1
1
  # HTTP::FormData
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/http-form_data.svg)](http://rubygems.org/gems/http-form_data)
4
- [![Build Status](https://secure.travis-ci.org/httprb/form_data.svg?branch=master)](http://travis-ci.org/httprb/form_data)
5
- [![Code Climate](https://codeclimate.com/github/httprb/form_data.svg)](https://codeclimate.com/github/httprb/form_data)
6
- [![Coverage Status](https://coveralls.io/repos/httprb/form_data.rb/badge.svg?branch=master)](https://coveralls.io/r/httprb/form_data.rb)
4
+ [![Test](https://github.com/httprb/form_data/actions/workflows/test.yml/badge.svg)](https://github.com/httprb/form_data/actions/workflows/test.yml)
5
+ [![Lint](https://github.com/httprb/form_data/actions/workflows/lint.yml/badge.svg)](https://github.com/httprb/form_data/actions/workflows/lint.yml)
6
+ [![Type Check](https://github.com/httprb/form_data/actions/workflows/typecheck.yml/badge.svg)](https://github.com/httprb/form_data/actions/workflows/typecheck.yml)
7
+ [![Mutant](https://github.com/httprb/form_data/actions/workflows/mutant.yml/badge.svg)](https://github.com/httprb/form_data/actions/workflows/mutant.yml)
8
+ [![Docs](https://github.com/httprb/form_data/actions/workflows/docs.yml/badge.svg)](https://github.com/httprb/form_data/actions/workflows/docs.yml)
7
9
 
8
10
  Utility-belt to build form data request bodies.
9
11
 
@@ -58,11 +60,10 @@ form = HTTP::FormData.create({
58
60
  This library aims to support and is [tested against][ci] the following Ruby
59
61
  versions:
60
62
 
61
- * Ruby 2.4.x
62
- * Ruby 2.5.x
63
- * Ruby 2.6.x
64
- * Ruby 2.7.x
65
- * JRuby 9.1.x.x
63
+ * Ruby 3.2
64
+ * Ruby 3.3
65
+ * Ruby 3.4
66
+ * Ruby 4.0
66
67
 
67
68
  If something doesn't work on one of these versions, it's a bug.
68
69
 
@@ -89,9 +90,9 @@ dropped.
89
90
 
90
91
  ## Copyright
91
92
 
92
- Copyright (c) 2015-2017 Alexey V Zapparov.
93
+ Copyright (c) 2015-2026 Alexey V Zapparov.
93
94
  See [LICENSE.txt][license] for further details.
94
95
 
95
96
 
96
- [ci]: http://travis-ci.org/httprb/form_data.rb
97
+ [ci]: https://github.com/httprb/form_data/actions
97
98
  [license]: https://github.com/httprb/form_data.rb/blob/master/LICENSE.txt
@@ -1,25 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- lib = File.expand_path("../lib", __FILE__)
4
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require "http/form_data/version"
3
+ require_relative "lib/http/form_data/version"
6
4
 
7
5
  Gem::Specification.new do |spec|
8
6
  spec.name = "http-form_data"
9
7
  spec.version = HTTP::FormData::VERSION
10
- spec.homepage = "https://github.com/httprb/form_data.rb"
11
8
  spec.authors = ["Aleksey V Zapparov"]
12
9
  spec.email = ["ixti@member.fsf.org"]
10
+
11
+ spec.summary = "Build form data request bodies"
12
+ spec.homepage = "https://github.com/httprb/form_data"
13
13
  spec.license = "MIT"
14
- spec.summary = "http-form_data-#{HTTP::FormData::VERSION}"
15
- spec.description = <<-DESC.gsub(/^\s+> /m, "").tr("\n", " ").strip
16
- > Utility-belt to build form data request bodies.
17
- > Provides support for `application/x-www-form-urlencoded` and
18
- > `multipart/form-data` types.
19
- DESC
20
-
21
- spec.files = `git ls-files -z`.split("\x0")
22
- spec.executables = spec.files.grep(%r{^bin\/}).map { |f| File.basename(f) }
23
- spec.test_files = spec.files.grep(%r{^(test|spec|features)\/})
14
+
15
+ spec.description = <<~DESCRIPTION.strip.gsub(/\s+/, " ")
16
+ Utility-belt to build form data request bodies.
17
+ Provides support for `application/x-www-form-urlencoded` and
18
+ `multipart/form-data` types.
19
+ DESCRIPTION
20
+
21
+ spec.metadata["homepage_uri"] = spec.homepage
22
+ spec.metadata["source_code_uri"] = "#{spec.homepage}/tree/v#{spec.version}"
23
+ spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
24
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/v#{spec.version}/CHANGELOG.md"
25
+ spec.metadata["documentation_uri"] = "https://www.rubydoc.info/gems/http-form_data/#{spec.version}"
26
+ spec.metadata["rubygems_mfa_required"] = "true"
27
+
28
+ spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls|
29
+ extras = %w[CHANGELOG.md LICENSE.txt README.md] << File.basename(__FILE__)
30
+
31
+ ls.readlines("\x0", chomp: true).select do |f|
32
+ f.start_with?("lib/", "sig/http/") || extras.include?(f)
33
+ end
34
+ end
35
+
24
36
  spec.require_paths = ["lib"]
37
+
38
+ spec.required_ruby_version = ">= 3.2"
25
39
  end
@@ -6,27 +6,36 @@ module HTTP
6
6
  module FormData
7
7
  # Provides IO interface across multiple IO objects.
8
8
  class CompositeIO
9
+ # Creates a new CompositeIO from an array of IOs
10
+ #
11
+ # @example
12
+ # CompositeIO.new([StringIO.new("hello"), StringIO.new(" world")])
13
+ #
14
+ # @api public
9
15
  # @param [Array<IO>] ios Array of IO objects
10
16
  def initialize(ios)
11
- @index = 0
12
- @buffer = "".b
13
- @ios = ios.map do |io|
17
+ @index = 0
18
+ @ios = ios.map do |io|
14
19
  if io.is_a?(String)
15
20
  StringIO.new(io)
16
21
  elsif io.respond_to?(:read)
17
22
  io
18
23
  else
19
24
  raise ArgumentError,
20
- "#{io.inspect} is neither a String nor an IO object"
25
+ "#{io.inspect} is neither a String nor an IO object"
21
26
  end
22
27
  end
23
28
  end
24
29
 
25
- # Reads and returns partial content acrosss multiple IO objects.
30
+ # Reads and returns content across multiple IO objects
31
+ #
32
+ # @example
33
+ # composite_io.read # => "hello world"
34
+ # composite_io.read(5) # => "hello"
26
35
  #
36
+ # @api public
27
37
  # @param [Integer] length Number of bytes to retrieve
28
38
  # @param [String] outbuf String to be replaced with retrieved data
29
- #
30
39
  # @return [String, nil]
31
40
  def read(length = nil, outbuf = nil)
32
41
  data = outbuf.clear.force_encoding(Encoding::BINARY) if outbuf
@@ -37,12 +46,24 @@ module HTTP
37
46
  data unless length && data.empty?
38
47
  end
39
48
 
40
- # Returns sum of all IO sizes.
49
+ # Returns sum of all IO sizes
50
+ #
51
+ # @example
52
+ # composite_io.size # => 11
53
+ #
54
+ # @api public
55
+ # @return [Integer]
41
56
  def size
42
- @size ||= @ios.map(&:size).inject(0, :+)
57
+ @size ||= @ios.sum(&:size)
43
58
  end
44
59
 
45
- # Rewinds all IO objects and set cursor to the first IO object.
60
+ # Rewinds all IO objects and resets cursor
61
+ #
62
+ # @example
63
+ # composite_io.rewind
64
+ #
65
+ # @api public
66
+ # @return [void]
46
67
  def rewind
47
68
  @ios.each(&:rewind)
48
69
  @index = 0
@@ -50,39 +71,36 @@ module HTTP
50
71
 
51
72
  private
52
73
 
53
- # Yields chunks with total length up to `length`.
54
- def read_chunks(length = nil)
74
+ # Yields chunks with total length up to `length`
75
+ #
76
+ # @api private
77
+ # @return [void]
78
+ def read_chunks(length)
55
79
  while (chunk = readpartial(length))
56
80
  yield chunk.force_encoding(Encoding::BINARY)
57
81
 
58
82
  next if length.nil?
59
83
 
60
- length -= chunk.bytesize
84
+ remaining = length - chunk.bytesize
85
+ break if remaining.zero?
61
86
 
62
- break if length.zero?
87
+ length = remaining
63
88
  end
64
89
  end
65
90
 
66
- # Reads chunk from current IO with length up to `max_length`.
67
- def readpartial(max_length = nil)
68
- while current_io
69
- chunk = current_io.read(max_length, @buffer)
91
+ # Reads chunk from current IO with length up to `max_length`
92
+ #
93
+ # @api private
94
+ # @return [String, nil]
95
+ def readpartial(max_length)
96
+ while (io = @ios.at(@index))
97
+ chunk = io.read(max_length)
70
98
 
71
99
  return chunk if chunk && !chunk.empty?
72
100
 
73
- advance_io
101
+ @index += 1
74
102
  end
75
103
  end
76
-
77
- # Returns IO object under the cursor.
78
- def current_io
79
- @ios[@index]
80
- end
81
-
82
- # Advances cursor to the next IO object.
83
- def advance_io
84
- @index += 1
85
- end
86
104
  end
87
105
  end
88
106
  end
@@ -7,7 +7,7 @@ module HTTP
7
7
  # @example Usage with StringIO
8
8
  #
9
9
  # io = StringIO.new "foo bar baz"
10
- # FormData::File.new io, :filename => "foobar.txt"
10
+ # FormData::File.new io, filename: "foobar.txt"
11
11
  #
12
12
  # @example Usage with IO
13
13
  #
@@ -22,45 +22,70 @@ module HTTP
22
22
  # Default MIME type
23
23
  DEFAULT_MIME = "application/octet-stream"
24
24
 
25
- # @deprecated Use #content_type instead
26
- alias mime_type content_type
27
-
25
+ # Creates a new File from a path or IO object
26
+ #
27
+ # @example
28
+ # File.new("/path/to/file.txt")
29
+ #
30
+ # @api public
28
31
  # @see DEFAULT_MIME
29
- # @param [String, Pathname, IO] path_or_io Filename or IO instance.
32
+ # @param [String, Pathname, IO] path_or_io Filename or IO instance
30
33
  # @param [#to_h] opts
31
34
  # @option opts [#to_s] :content_type (DEFAULT_MIME)
32
35
  # Value of Content-Type header
33
36
  # @option opts [#to_s] :filename
34
37
  # When `path_or_io` is a String, Pathname or File, defaults to basename.
35
- # When `path_or_io` is a IO, defaults to `"stream-{object_id}"`.
36
- def initialize(path_or_io, opts = {})
38
+ # When `path_or_io` is a IO, defaults to `"stream-{object_id}"`
39
+ def initialize(path_or_io, opts = nil) # rubocop:disable Lint/MissingSuper
37
40
  opts = FormData.ensure_hash(opts)
38
41
 
39
- if opts.key? :mime_type
40
- warn "[DEPRECATED] :mime_type option deprecated, use :content_type"
41
- opts[:content_type] = opts[:mime_type]
42
- end
43
-
44
42
  @io = make_io(path_or_io)
43
+ @autoclose = path_or_io.is_a?(String) || path_or_io.is_a?(Pathname)
45
44
  @content_type = opts.fetch(:content_type, DEFAULT_MIME).to_s
46
45
  @filename = opts.fetch(:filename, filename_for(@io))
47
46
  end
48
47
 
48
+ # Closes the underlying IO if it was opened by this instance
49
+ #
50
+ # When the File was created from a String path or Pathname, the
51
+ # underlying file handle is closed. When created from an existing
52
+ # IO object, this is a no-op (the caller is responsible for
53
+ # closing it).
54
+ #
55
+ # @example
56
+ # file = FormData::File.new("/path/to/file.txt")
57
+ # file.to_s
58
+ # file.close
59
+ #
60
+ # @api public
61
+ # @return [void]
62
+ def close
63
+ @io.close if @autoclose
64
+ end
65
+
49
66
  private
50
67
 
68
+ # Wraps path_or_io into an IO object
69
+ #
70
+ # @api private
71
+ # @param [String, Pathname, IO] path_or_io
72
+ # @return [IO]
51
73
  def make_io(path_or_io)
52
- if path_or_io.is_a?(String)
53
- ::File.open(path_or_io, :binmode => true)
54
- elsif defined?(Pathname) && path_or_io.is_a?(Pathname)
55
- path_or_io.open(:binmode => true)
56
- else
57
- path_or_io
74
+ case path_or_io
75
+ when String then ::File.new(path_or_io, binmode: true)
76
+ when Pathname then path_or_io.open(binmode: true)
77
+ else path_or_io
58
78
  end
59
79
  end
60
80
 
81
+ # Determines filename for the given IO
82
+ #
83
+ # @api private
84
+ # @param [IO] io
85
+ # @return [String]
61
86
  def filename_for(io)
62
87
  if io.respond_to?(:path)
63
- ::File.basename io.path
88
+ ::File.basename(io.path)
64
89
  else
65
90
  "stream-#{io.object_id}"
66
91
  end
@@ -10,7 +10,7 @@ module HTTP
10
10
  class Param
11
11
  include Readable
12
12
 
13
- # Initializes body part with headers and data.
13
+ # Initializes body part with headers and data
14
14
  #
15
15
  # @example With {FormData::File} value
16
16
  #
@@ -25,66 +25,36 @@ module HTTP
25
25
  #
26
26
  # ixti
27
27
  #
28
- # @return [String]
28
+ # @api public
29
29
  # @param [#to_s] name
30
30
  # @param [FormData::File, FormData::Part, #to_s] value
31
+ # @return [Param]
31
32
  def initialize(name, value)
32
33
  @name = name.to_s
33
-
34
- @part =
35
- if value.is_a?(FormData::Part)
36
- value
37
- else
38
- FormData::Part.new(value)
39
- end
40
-
41
- @io = CompositeIO.new [header, @part, footer]
42
- end
43
-
44
- # Flattens given `data` Hash into an array of `Param`'s.
45
- # Nested array are unwinded.
46
- # Behavior is similar to `URL.encode_www_form`.
47
- #
48
- # @param [Hash] data
49
- # @return [Array<FormData::MultiPart::Param>]
50
- def self.coerce(data)
51
- params = []
52
-
53
- data.each do |name, values|
54
- Array(values).each do |value|
55
- params << new(name, value)
56
- end
57
- end
58
-
59
- params
34
+ @part = value.is_a?(Part) ? value : Part.new(value)
35
+ @io = CompositeIO.new [header, @part, CRLF]
60
36
  end
61
37
 
62
38
  private
63
39
 
40
+ # Builds the MIME header for this part
41
+ #
42
+ # @api private
43
+ # @return [String]
64
44
  def header
65
- header = "".b
66
- header << "Content-Disposition: form-data; #{parameters}#{CRLF}"
67
- header << "Content-Type: #{content_type}#{CRLF}" if content_type
45
+ header = "Content-Disposition: form-data; #{parameters}#{CRLF}"
46
+ header << "Content-Type: #{@part.content_type}#{CRLF}" if @part.content_type
68
47
  header << CRLF
69
- header
70
48
  end
71
49
 
50
+ # Builds Content-Disposition parameters string
51
+ #
52
+ # @api private
53
+ # @return [String]
72
54
  def parameters
73
- parameters = { :name => @name }
74
- parameters[:filename] = filename if filename
75
- parameters.map { |k, v| "#{k}=#{v.inspect}" }.join("; ")
76
- end
77
-
78
- def content_type
79
- @part.content_type
80
- end
81
-
82
- def filename
83
- @part.filename
84
- end
85
-
86
- def footer
87
- CRLF.dup
55
+ params = "name=#{@name.inspect}"
56
+ params << "; filename=#{@part.filename.inspect}" if @part.filename
57
+ params
88
58
  end
89
59
  end
90
60
  end