faraday 0.7.4 → 1.0.1

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 (119) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +276 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +40 -153
  5. data/Rakefile +4 -139
  6. data/examples/client_spec.rb +65 -0
  7. data/examples/client_test.rb +79 -0
  8. data/lib/faraday/adapter/em_http.rb +286 -0
  9. data/lib/faraday/adapter/em_http_ssl_patch.rb +62 -0
  10. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +69 -0
  11. data/lib/faraday/adapter/em_synchrony.rb +120 -36
  12. data/lib/faraday/adapter/excon.rb +108 -12
  13. data/lib/faraday/adapter/httpclient.rb +152 -0
  14. data/lib/faraday/adapter/net_http.rb +187 -43
  15. data/lib/faraday/adapter/net_http_persistent.rb +91 -0
  16. data/lib/faraday/adapter/patron.rb +106 -10
  17. data/lib/faraday/adapter/rack.rb +75 -0
  18. data/lib/faraday/adapter/test.rb +160 -61
  19. data/lib/faraday/adapter/typhoeus.rb +7 -46
  20. data/lib/faraday/adapter.rb +105 -33
  21. data/lib/faraday/adapter_registry.rb +30 -0
  22. data/lib/faraday/autoload.rb +95 -0
  23. data/lib/faraday/connection.rb +525 -157
  24. data/lib/faraday/dependency_loader.rb +37 -0
  25. data/lib/faraday/encoders/flat_params_encoder.rb +98 -0
  26. data/lib/faraday/encoders/nested_params_encoder.rb +171 -0
  27. data/lib/faraday/error.rb +122 -30
  28. data/lib/faraday/file_part.rb +128 -0
  29. data/lib/faraday/logging/formatter.rb +105 -0
  30. data/lib/faraday/middleware.rb +14 -22
  31. data/lib/faraday/middleware_registry.rb +129 -0
  32. data/lib/faraday/options/connection_options.rb +22 -0
  33. data/lib/faraday/options/env.rb +181 -0
  34. data/lib/faraday/options/proxy_options.rb +28 -0
  35. data/lib/faraday/options/request_options.rb +22 -0
  36. data/lib/faraday/options/ssl_options.rb +59 -0
  37. data/lib/faraday/options.rb +222 -0
  38. data/lib/faraday/param_part.rb +53 -0
  39. data/lib/faraday/parameters.rb +5 -0
  40. data/lib/faraday/rack_builder.rb +248 -0
  41. data/lib/faraday/request/authorization.rb +55 -0
  42. data/lib/faraday/request/basic_authentication.rb +20 -0
  43. data/lib/faraday/request/instrumentation.rb +54 -0
  44. data/lib/faraday/request/multipart.rb +84 -48
  45. data/lib/faraday/request/retry.rb +239 -0
  46. data/lib/faraday/request/token_authentication.rb +20 -0
  47. data/lib/faraday/request/url_encoded.rb +46 -27
  48. data/lib/faraday/request.rb +112 -50
  49. data/lib/faraday/response/logger.rb +24 -25
  50. data/lib/faraday/response/raise_error.rb +40 -11
  51. data/lib/faraday/response.rb +44 -35
  52. data/lib/faraday/utils/headers.rb +139 -0
  53. data/lib/faraday/utils/params_hash.rb +61 -0
  54. data/lib/faraday/utils.rb +72 -117
  55. data/lib/faraday.rb +142 -64
  56. data/spec/external_adapters/faraday_specs_setup.rb +14 -0
  57. data/spec/faraday/adapter/em_http_spec.rb +47 -0
  58. data/spec/faraday/adapter/em_synchrony_spec.rb +16 -0
  59. data/spec/faraday/adapter/excon_spec.rb +49 -0
  60. data/spec/faraday/adapter/httpclient_spec.rb +73 -0
  61. data/spec/faraday/adapter/net_http_persistent_spec.rb +57 -0
  62. data/spec/faraday/adapter/net_http_spec.rb +64 -0
  63. data/spec/faraday/adapter/patron_spec.rb +18 -0
  64. data/spec/faraday/adapter/rack_spec.rb +8 -0
  65. data/spec/faraday/adapter/typhoeus_spec.rb +7 -0
  66. data/spec/faraday/adapter_registry_spec.rb +28 -0
  67. data/spec/faraday/adapter_spec.rb +55 -0
  68. data/spec/faraday/composite_read_io_spec.rb +80 -0
  69. data/spec/faraday/connection_spec.rb +691 -0
  70. data/spec/faraday/error_spec.rb +45 -0
  71. data/spec/faraday/middleware_spec.rb +26 -0
  72. data/spec/faraday/options/env_spec.rb +70 -0
  73. data/spec/faraday/options/options_spec.rb +297 -0
  74. data/spec/faraday/options/proxy_options_spec.rb +37 -0
  75. data/spec/faraday/options/request_options_spec.rb +19 -0
  76. data/spec/faraday/params_encoders/flat_spec.rb +34 -0
  77. data/spec/faraday/params_encoders/nested_spec.rb +134 -0
  78. data/spec/faraday/rack_builder_spec.rb +196 -0
  79. data/spec/faraday/request/authorization_spec.rb +88 -0
  80. data/spec/faraday/request/instrumentation_spec.rb +76 -0
  81. data/spec/faraday/request/multipart_spec.rb +274 -0
  82. data/spec/faraday/request/retry_spec.rb +242 -0
  83. data/spec/faraday/request/url_encoded_spec.rb +83 -0
  84. data/spec/faraday/request_spec.rb +109 -0
  85. data/spec/faraday/response/logger_spec.rb +220 -0
  86. data/spec/faraday/response/middleware_spec.rb +68 -0
  87. data/spec/faraday/response/raise_error_spec.rb +106 -0
  88. data/spec/faraday/response_spec.rb +75 -0
  89. data/spec/faraday/utils/headers_spec.rb +82 -0
  90. data/spec/faraday/utils_spec.rb +56 -0
  91. data/spec/faraday_spec.rb +37 -0
  92. data/spec/spec_helper.rb +132 -0
  93. data/spec/support/disabling_stub.rb +14 -0
  94. data/spec/support/fake_safe_buffer.rb +15 -0
  95. data/spec/support/helper_methods.rb +133 -0
  96. data/spec/support/shared_examples/adapter.rb +104 -0
  97. data/spec/support/shared_examples/params_encoder.rb +18 -0
  98. data/spec/support/shared_examples/request_method.rb +234 -0
  99. data/spec/support/streaming_response_checker.rb +35 -0
  100. data/spec/support/webmock_rack_app.rb +68 -0
  101. metadata +126 -126
  102. data/Gemfile +0 -29
  103. data/config.ru +0 -6
  104. data/faraday.gemspec +0 -92
  105. data/lib/faraday/adapter/action_dispatch.rb +0 -29
  106. data/lib/faraday/builder.rb +0 -160
  107. data/lib/faraday/request/json.rb +0 -35
  108. data/lib/faraday/upload_io.rb +0 -23
  109. data/test/adapters/live_test.rb +0 -205
  110. data/test/adapters/logger_test.rb +0 -37
  111. data/test/adapters/net_http_test.rb +0 -33
  112. data/test/adapters/test_middleware_test.rb +0 -70
  113. data/test/connection_test.rb +0 -254
  114. data/test/env_test.rb +0 -158
  115. data/test/helper.rb +0 -41
  116. data/test/live_server.rb +0 -45
  117. data/test/middleware_stack_test.rb +0 -118
  118. data/test/request_middleware_test.rb +0 -116
  119. data/test/response_middleware_test.rb +0 -74
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ # Multipart value used to POST data with a content type.
5
+ class ParamPart
6
+ # @param value [String] Uploaded content as a String.
7
+ # @param content_type [String] String content type of the value.
8
+ # @param content_id [String] Optional String of this value's Content-ID.
9
+ #
10
+ # @return [Faraday::ParamPart]
11
+ def initialize(value, content_type, content_id = nil)
12
+ @value = value
13
+ @content_type = content_type
14
+ @content_id = content_id
15
+ end
16
+
17
+ # Converts this value to a form part.
18
+ #
19
+ # @param boundary [String] String multipart boundary that must not exist in
20
+ # the content exactly.
21
+ # @param key [String] String key name for this value.
22
+ #
23
+ # @return [Faraday::Parts::Part]
24
+ def to_part(boundary, key)
25
+ Faraday::Parts::Part.new(boundary, key, value, headers)
26
+ end
27
+
28
+ # Returns a Hash of String key/value pairs.
29
+ #
30
+ # @return [Hash]
31
+ def headers
32
+ {
33
+ 'Content-Type' => content_type,
34
+ 'Content-ID' => content_id
35
+ }
36
+ end
37
+
38
+ # The content to upload.
39
+ #
40
+ # @return [String]
41
+ attr_reader :value
42
+
43
+ # The value's content type.
44
+ #
45
+ # @return [String]
46
+ attr_reader :content_type
47
+
48
+ # The value's content ID, if given.
49
+ #
50
+ # @return [String, nil]
51
+ attr_reader :content_id
52
+ end
53
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+ require 'faraday/encoders/nested_params_encoder'
5
+ require 'faraday/encoders/flat_params_encoder'
@@ -0,0 +1,248 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday/adapter_registry'
4
+
5
+ module Faraday
6
+ # A Builder that processes requests into responses by passing through an inner
7
+ # middleware stack (heavily inspired by Rack).
8
+ #
9
+ # @example
10
+ # Faraday::Connection.new(url: 'http://sushi.com') do |builder|
11
+ # builder.request :url_encoded # Faraday::Request::UrlEncoded
12
+ # builder.adapter :net_http # Faraday::Adapter::NetHttp
13
+ # end
14
+ class RackBuilder
15
+ # Used to detect missing arguments
16
+ NO_ARGUMENT = Object.new
17
+
18
+ attr_accessor :handlers
19
+
20
+ # Error raised when trying to modify the stack after calling `lock!`
21
+ class StackLocked < RuntimeError; end
22
+
23
+ # borrowed from ActiveSupport::Dependencies::Reference &
24
+ # ActionDispatch::MiddlewareStack::Middleware
25
+ class Handler
26
+ REGISTRY = Faraday::AdapterRegistry.new
27
+
28
+ attr_reader :name
29
+
30
+ def initialize(klass, *args, &block)
31
+ @name = klass.to_s
32
+ REGISTRY.set(klass) if klass.respond_to?(:name)
33
+ @args = args
34
+ @block = block
35
+ end
36
+
37
+ def klass
38
+ REGISTRY.get(@name)
39
+ end
40
+
41
+ def inspect
42
+ @name
43
+ end
44
+
45
+ def ==(other)
46
+ if other.is_a? Handler
47
+ name == other.name
48
+ elsif other.respond_to? :name
49
+ klass == other
50
+ else
51
+ @name == other.to_s
52
+ end
53
+ end
54
+
55
+ def build(app = nil)
56
+ klass.new(app, *@args, &@block)
57
+ end
58
+ end
59
+
60
+ def initialize(handlers = [], adapter = nil, &block)
61
+ @adapter = adapter
62
+ @handlers = handlers
63
+ if block_given?
64
+ build(&block)
65
+ elsif @handlers.empty?
66
+ # default stack, if nothing else is configured
67
+ request :url_encoded
68
+ self.adapter Faraday.default_adapter
69
+ end
70
+ end
71
+
72
+ def build(options = {})
73
+ raise_if_locked
74
+ @handlers.clear unless options[:keep]
75
+ yield(self) if block_given?
76
+ adapter(Faraday.default_adapter) unless @adapter
77
+ end
78
+
79
+ def [](idx)
80
+ @handlers[idx]
81
+ end
82
+
83
+ # Locks the middleware stack to ensure no further modifications are made.
84
+ def lock!
85
+ @handlers.freeze
86
+ end
87
+
88
+ def locked?
89
+ @handlers.frozen?
90
+ end
91
+
92
+ def use(klass, *args, &block)
93
+ if klass.is_a? Symbol
94
+ use_symbol(Faraday::Middleware, klass, *args, &block)
95
+ else
96
+ raise_if_locked
97
+ raise_if_adapter(klass)
98
+ @handlers << self.class::Handler.new(klass, *args, &block)
99
+ end
100
+ end
101
+
102
+ def request(key, *args, &block)
103
+ use_symbol(Faraday::Request, key, *args, &block)
104
+ end
105
+
106
+ def response(key, *args, &block)
107
+ use_symbol(Faraday::Response, key, *args, &block)
108
+ end
109
+
110
+ def adapter(klass = NO_ARGUMENT, *args, &block)
111
+ return @adapter if klass == NO_ARGUMENT
112
+
113
+ klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol)
114
+ @adapter = self.class::Handler.new(klass, *args, &block)
115
+ end
116
+
117
+ ## methods to push onto the various positions in the stack:
118
+
119
+ def insert(index, *args, &block)
120
+ raise_if_locked
121
+ index = assert_index(index)
122
+ handler = self.class::Handler.new(*args, &block)
123
+ @handlers.insert(index, handler)
124
+ end
125
+
126
+ alias insert_before insert
127
+
128
+ def insert_after(index, *args, &block)
129
+ index = assert_index(index)
130
+ insert(index + 1, *args, &block)
131
+ end
132
+
133
+ def swap(index, *args, &block)
134
+ raise_if_locked
135
+ index = assert_index(index)
136
+ @handlers.delete_at(index)
137
+ insert(index, *args, &block)
138
+ end
139
+
140
+ def delete(handler)
141
+ raise_if_locked
142
+ @handlers.delete(handler)
143
+ end
144
+
145
+ # Processes a Request into a Response by passing it through this Builder's
146
+ # middleware stack.
147
+ #
148
+ # @param connection [Faraday::Connection]
149
+ # @param request [Faraday::Request]
150
+ #
151
+ # @return [Faraday::Response]
152
+ def build_response(connection, request)
153
+ app.call(build_env(connection, request))
154
+ end
155
+
156
+ # The "rack app" wrapped in middleware. All requests are sent here.
157
+ #
158
+ # The builder is responsible for creating the app object. After this,
159
+ # the builder gets locked to ensure no further modifications are made
160
+ # to the middleware stack.
161
+ #
162
+ # Returns an object that responds to `call` and returns a Response.
163
+ def app
164
+ @app ||= begin
165
+ lock!
166
+ to_app
167
+ end
168
+ end
169
+
170
+ def to_app
171
+ # last added handler is the deepest and thus closest to the inner app
172
+ # adapter is always the last one
173
+ @handlers.reverse.inject(@adapter.build) do |app, handler|
174
+ handler.build(app)
175
+ end
176
+ end
177
+
178
+ def ==(other)
179
+ other.is_a?(self.class) &&
180
+ @handlers == other.handlers &&
181
+ @adapter == other.adapter
182
+ end
183
+
184
+ def dup
185
+ self.class.new(@handlers.dup, @adapter.dup)
186
+ end
187
+
188
+ # ENV Keys
189
+ # :method - a symbolized request method (:get, :post)
190
+ # :body - the request body that will eventually be converted to a string.
191
+ # :url - URI instance for the current request.
192
+ # :status - HTTP response status code
193
+ # :request_headers - hash of HTTP Headers to be sent to the server
194
+ # :response_headers - Hash of HTTP headers from the server
195
+ # :parallel_manager - sent if the connection is in parallel mode
196
+ # :request - Hash of options for configuring the request.
197
+ # :timeout - open/read timeout Integer in seconds
198
+ # :open_timeout - read timeout Integer in seconds
199
+ # :proxy - Hash of proxy options
200
+ # :uri - Proxy Server URI
201
+ # :user - Proxy server username
202
+ # :password - Proxy server password
203
+ # :ssl - Hash of options for configuring SSL requests.
204
+ def build_env(connection, request)
205
+ exclusive_url = connection.build_exclusive_url(
206
+ request.path, request.params,
207
+ request.options.params_encoder
208
+ )
209
+
210
+ Env.new(request.method, request.body, exclusive_url,
211
+ request.options, request.headers, connection.ssl,
212
+ connection.parallel_manager)
213
+ end
214
+
215
+ private
216
+
217
+ LOCK_ERR = "can't modify middleware stack after making a request"
218
+
219
+ def raise_if_locked
220
+ raise StackLocked, LOCK_ERR if locked?
221
+ end
222
+
223
+ def raise_if_adapter(klass)
224
+ return unless is_adapter?(klass)
225
+
226
+ raise 'Adapter should be set using the `adapter` method, not `use`'
227
+ end
228
+
229
+ def adapter_set?
230
+ !@adapter.nil?
231
+ end
232
+
233
+ def is_adapter?(klass) # rubocop:disable Naming/PredicateName
234
+ klass.ancestors.include?(Faraday::Adapter)
235
+ end
236
+
237
+ def use_symbol(mod, key, *args, &block)
238
+ use(mod.lookup_middleware(key), *args, &block)
239
+ end
240
+
241
+ def assert_index(index)
242
+ idx = index.is_a?(Integer) ? index : @handlers.index(index)
243
+ raise "No such handler: #{index.inspect}" unless idx
244
+
245
+ idx
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ class Request
5
+ # Request middleware for the Authorization HTTP header
6
+ class Authorization < Faraday::Middleware
7
+ unless defined?(::Faraday::Request::Authorization::KEY)
8
+ KEY = 'Authorization'
9
+ end
10
+
11
+ # @param type [String, Symbol]
12
+ # @param token [String, Symbol, Hash]
13
+ # @return [String] a header value
14
+ def self.header(type, token)
15
+ case token
16
+ when String, Symbol
17
+ "#{type} #{token}"
18
+ when Hash
19
+ build_hash(type.to_s, token)
20
+ else
21
+ raise ArgumentError,
22
+ "Can't build an Authorization #{type}" \
23
+ "header from #{token.inspect}"
24
+ end
25
+ end
26
+
27
+ # @param type [String]
28
+ # @param hash [Hash]
29
+ # @return [String] type followed by comma-separated key=value pairs
30
+ # @api private
31
+ def self.build_hash(type, hash)
32
+ comma = ', '
33
+ values = []
34
+ hash.each do |key, value|
35
+ values << "#{key}=#{value.to_s.inspect}"
36
+ end
37
+ "#{type} #{values * comma}"
38
+ end
39
+
40
+ # @param app [#call]
41
+ # @param type [String, Symbol] Type of Authorization
42
+ # @param token [String, Symbol, Hash] Token value for the Authorization
43
+ def initialize(app, type, token)
44
+ @header_value = self.class.header(type, token)
45
+ super(app)
46
+ end
47
+
48
+ # @param env [Faraday::Env]
49
+ def call(env)
50
+ env.request_headers[KEY] = @header_value unless env.request_headers[KEY]
51
+ @app.call(env)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+
5
+ module Faraday
6
+ class Request
7
+ # Authorization middleware for Basic Authentication.
8
+ class BasicAuthentication < load_middleware(:authorization)
9
+ # @param login [String]
10
+ # @param pass [String]
11
+ #
12
+ # @return [String] a Basic Authentication header line
13
+ def self.header(login, pass)
14
+ value = Base64.encode64([login, pass].join(':'))
15
+ value.delete!("\n")
16
+ super(:Basic, value)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ class Request
5
+ # Middleware for instrumenting Requests.
6
+ class Instrumentation < Faraday::Middleware
7
+ # Options class used in Request::Instrumentation class.
8
+ class Options < Faraday::Options.new(:name, :instrumenter)
9
+ # @return [String]
10
+ def name
11
+ self[:name] ||= 'request.faraday'
12
+ end
13
+
14
+ # @return [Class]
15
+ def instrumenter
16
+ self[:instrumenter] ||= ActiveSupport::Notifications
17
+ end
18
+ end
19
+
20
+ # Instruments requests using Active Support.
21
+ #
22
+ # Measures time spent only for synchronous requests.
23
+ #
24
+ # @example Using ActiveSupport::Notifications to measure time spent
25
+ # for Faraday requests.
26
+ # ActiveSupport::Notifications
27
+ # .subscribe('request.faraday') do |name, starts, ends, _, env|
28
+ # url = env[:url]
29
+ # http_method = env[:method].to_s.upcase
30
+ # duration = ends - starts
31
+ # $stderr.puts '[%s] %s %s (%.3f s)' %
32
+ # [url.host, http_method, url.request_uri, duration]
33
+ # end
34
+ # @param app [#call]
35
+ # @param options [nil, Hash] Options hash
36
+ # @option options [String] :name ('request.faraday')
37
+ # Name of the instrumenter
38
+ # @option options [Class] :instrumenter (ActiveSupport::Notifications)
39
+ # Active Support instrumenter class.
40
+ def initialize(app, options = nil)
41
+ super(app)
42
+ @name, @instrumenter = Options.from(options)
43
+ .values_at(:name, :instrumenter)
44
+ end
45
+
46
+ # @param env [Faraday::Env]
47
+ def call(env)
48
+ @instrumenter.instrument(@name, env) do
49
+ @app.call(env)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,61 +1,97 @@
1
- module Faraday
2
- class Request::Multipart < Request::UrlEncoded
3
- self.mime_type = 'multipart/form-data'.freeze
4
- DEFAULT_BOUNDARY = "-----------RubyMultipartPost".freeze
5
-
6
- def call(env)
7
- match_content_type(env) do |params|
8
- env[:request] ||= {}
9
- env[:request][:boundary] ||= DEFAULT_BOUNDARY
10
- env[:request_headers][CONTENT_TYPE] += ";boundary=#{env[:request][:boundary]}"
11
- env[:body] = create_multipart(env, params)
12
- end
13
- @app.call env
14
- end
1
+ # frozen_string_literal: true
15
2
 
16
- def process_request?(env)
17
- type = request_type(env)
18
- env[:body].respond_to?(:each_key) and !env[:body].empty? and (
19
- (type.empty? and has_multipart?(env[:body])) or
20
- type == self.class.mime_type
21
- )
22
- end
3
+ require File.expand_path('url_encoded', __dir__)
4
+ require 'securerandom'
5
+
6
+ module Faraday
7
+ class Request
8
+ # Middleware for supporting multi-part requests.
9
+ class Multipart < UrlEncoded
10
+ self.mime_type = 'multipart/form-data'
11
+ unless defined?(::Faraday::Request::Multipart::DEFAULT_BOUNDARY_PREFIX)
12
+ DEFAULT_BOUNDARY_PREFIX = '-----------RubyMultipartPost'
13
+ end
23
14
 
24
- def has_multipart?(body)
25
- body.values.each do |val|
26
- if val.respond_to?(:content_type)
27
- return true
28
- elsif val.respond_to?(:values)
29
- return true if has_multipart?(val)
15
+ # Checks for files in the payload, otherwise leaves everything untouched.
16
+ #
17
+ # @param env [Faraday::Env]
18
+ def call(env)
19
+ match_content_type(env) do |params|
20
+ env.request.boundary ||= unique_boundary
21
+ env.request_headers[CONTENT_TYPE] +=
22
+ "; boundary=#{env.request.boundary}"
23
+ env.body = create_multipart(env, params)
30
24
  end
25
+ @app.call env
31
26
  end
32
- false
33
- end
34
27
 
35
- def create_multipart(env, params)
36
- boundary = env[:request][:boundary]
37
- parts = process_params(params) do |key, value|
38
- Faraday::Parts::Part.new(boundary, key, value)
28
+ # @param env [Faraday::Env]
29
+ def process_request?(env)
30
+ type = request_type(env)
31
+ env.body.respond_to?(:each_key) && !env.body.empty? && (
32
+ (type.empty? && has_multipart?(env.body)) ||
33
+ (type == self.class.mime_type)
34
+ )
39
35
  end
40
- parts << Faraday::Parts::EpiloguePart.new(boundary)
41
36
 
42
- body = Faraday::CompositeReadIO.new(parts)
43
- env[:request_headers]['Content-Length'] = body.length.to_s
44
- return body
45
- end
37
+ # Returns true if obj is an enumerable with values that are multipart.
38
+ #
39
+ # @param obj [Object]
40
+ # @return [Boolean]
41
+ def has_multipart?(obj) # rubocop:disable Naming/PredicateName
42
+ if obj.respond_to?(:each)
43
+ (obj.respond_to?(:values) ? obj.values : obj).each do |val|
44
+ return true if val.respond_to?(:content_type) || has_multipart?(val)
45
+ end
46
+ end
47
+ false
48
+ end
46
49
 
47
- def process_params(params, prefix = nil, pieces = nil, &block)
48
- params.inject(pieces || []) do |all, (key, value)|
49
- key = "#{prefix}[#{key}]" if prefix
50
+ # @param env [Faraday::Env]
51
+ # @param params [Hash]
52
+ def create_multipart(env, params)
53
+ boundary = env.request.boundary
54
+ parts = process_params(params) do |key, value|
55
+ part(boundary, key, value)
56
+ end
57
+ parts << Faraday::Parts::EpiloguePart.new(boundary)
58
+
59
+ body = Faraday::CompositeReadIO.new(parts)
60
+ env.request_headers[Faraday::Env::ContentLength] = body.length.to_s
61
+ body
62
+ end
50
63
 
51
- case value
52
- when Array
53
- values = value.inject([]) { |a,v| a << [nil, v] }
54
- process_params(values, key, all, &block)
55
- when Hash
56
- process_params(value, key, all, &block)
64
+ def part(boundary, key, value)
65
+ if value.respond_to?(:to_part)
66
+ value.to_part(boundary, key)
57
67
  else
58
- all << block.call(key, value)
68
+ Faraday::Parts::Part.new(boundary, key, value)
69
+ end
70
+ end
71
+
72
+ # @return [String]
73
+ def unique_boundary
74
+ "#{DEFAULT_BOUNDARY_PREFIX}-#{SecureRandom.hex}"
75
+ end
76
+
77
+ # @param params [Hash]
78
+ # @param prefix [String]
79
+ # @param pieces [Array]
80
+ def process_params(params, prefix = nil, pieces = nil, &block)
81
+ params.inject(pieces || []) do |all, (key, value)|
82
+ key = "#{prefix}[#{key}]" if prefix
83
+
84
+ case value
85
+ when Array
86
+ values = value.inject([]) { |a, v| a << [nil, v] }
87
+ process_params(values, key, all, &block)
88
+ when Hash
89
+ process_params(value, key, all, &block)
90
+ else
91
+ # rubocop:disable Performance/RedundantBlockCall
92
+ all << block.call(key, value)
93
+ # rubocop:enable Performance/RedundantBlockCall
94
+ end
59
95
  end
60
96
  end
61
97
  end