faraday 0.11.0 → 1.4.3

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