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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +161 -0
- data/LICENSE.txt +1 -1
- data/README.md +11 -10
- data/http-form_data.gemspec +28 -14
- data/lib/http/form_data/composite_io.rb +46 -28
- data/lib/http/form_data/file.rb +44 -19
- data/lib/http/form_data/multipart/param.rb +18 -48
- data/lib/http/form_data/multipart.rb +59 -12
- data/lib/http/form_data/part.rb +24 -2
- data/lib/http/form_data/readable.rb +24 -6
- data/lib/http/form_data/urlencoded.rb +100 -10
- data/lib/http/form_data/version.rb +1 -1
- data/lib/http/form_data.rb +38 -18
- data/sig/http/form_data/composite_io.rbs +32 -0
- data/sig/http/form_data/file.rbs +23 -0
- data/sig/http/form_data/multipart/param.rbs +23 -0
- data/sig/http/form_data/multipart.rbs +40 -0
- data/sig/http/form_data/part.rbs +16 -0
- data/sig/http/form_data/readable.rbs +19 -0
- data/sig/http/form_data/urlencoded.rbs +46 -0
- data/sig/http/form_data/version.rbs +5 -0
- data/sig/http/form_data.rbs +30 -0
- metadata +24 -43
- data/.editorconfig +0 -9
- data/.gitignore +0 -15
- data/.rspec +0 -2
- data/.rubocop.yml +0 -64
- data/.travis.yml +0 -34
- data/.yardopts +0 -2
- data/CHANGES.md +0 -96
- data/Gemfile +0 -26
- data/Guardfile +0 -16
- data/Rakefile +0 -24
- data/appveyor.yml +0 -8
- data/spec/fixtures/expected-multipart-body.tpl +0 -0
- data/spec/fixtures/the-http-gem.info +0 -1
- data/spec/lib/http/form_data/composite_io_spec.rb +0 -109
- data/spec/lib/http/form_data/file_spec.rb +0 -217
- data/spec/lib/http/form_data/multipart_spec.rb +0 -157
- data/spec/lib/http/form_data/part_spec.rb +0 -74
- data/spec/lib/http/form_data/urlencoded_spec.rb +0 -78
- data/spec/lib/http/form_data_spec.rb +0 -50
- data/spec/spec_helper.rb +0 -83
- data/spec/support/fixtures_helper.rb +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3a38607435f5ac942c5b4f3d1e28c5910d151ccb978fbcaff0dc8d51a0932400
|
|
4
|
+
data.tar.gz: fe8f7bf0309d6f8dd178283aa40d2708b69d89a2c2535bf253370759b6b28b07
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
data/README.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# HTTP::FormData
|
|
2
2
|
|
|
3
3
|
[](http://rubygems.org/gems/http-form_data)
|
|
4
|
-
[](https://github.com/httprb/form_data/actions/workflows/test.yml)
|
|
5
|
+
[](https://github.com/httprb/form_data/actions/workflows/lint.yml)
|
|
6
|
+
[](https://github.com/httprb/form_data/actions/workflows/typecheck.yml)
|
|
7
|
+
[](https://github.com/httprb/form_data/actions/workflows/mutant.yml)
|
|
8
|
+
[](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
|
|
62
|
-
* Ruby
|
|
63
|
-
* Ruby
|
|
64
|
-
* Ruby
|
|
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-
|
|
93
|
+
Copyright (c) 2015-2026 Alexey V Zapparov.
|
|
93
94
|
See [LICENSE.txt][license] for further details.
|
|
94
95
|
|
|
95
96
|
|
|
96
|
-
[ci]:
|
|
97
|
+
[ci]: https://github.com/httprb/form_data/actions
|
|
97
98
|
[license]: https://github.com/httprb/form_data.rb/blob/master/LICENSE.txt
|
data/http-form_data.gemspec
CHANGED
|
@@ -1,25 +1,39 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
15
|
-
spec.description =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
spec.
|
|
22
|
-
spec.
|
|
23
|
-
spec.
|
|
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
|
|
12
|
-
@
|
|
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
|
-
|
|
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
|
|
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.
|
|
57
|
+
@size ||= @ios.sum(&:size)
|
|
43
58
|
end
|
|
44
59
|
|
|
45
|
-
# Rewinds all IO objects and
|
|
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
|
-
|
|
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
|
|
84
|
+
remaining = length - chunk.bytesize
|
|
85
|
+
break if remaining.zero?
|
|
61
86
|
|
|
62
|
-
|
|
87
|
+
length = remaining
|
|
63
88
|
end
|
|
64
89
|
end
|
|
65
90
|
|
|
66
|
-
# Reads chunk from current IO with length up to `max_length
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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
|
data/lib/http/form_data/file.rb
CHANGED
|
@@ -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, :
|
|
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
|
-
#
|
|
26
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
|
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
|
-
# @
|
|
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 = ""
|
|
66
|
-
header << "Content-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|