faraday 1.10.1 → 2.12.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +198 -4
  3. data/LICENSE.md +1 -1
  4. data/README.md +34 -20
  5. data/Rakefile +6 -1
  6. data/examples/client_spec.rb +41 -19
  7. data/examples/client_test.rb +48 -22
  8. data/lib/faraday/adapter/test.rb +62 -13
  9. data/lib/faraday/adapter.rb +6 -10
  10. data/lib/faraday/connection.rb +72 -150
  11. data/lib/faraday/encoders/nested_params_encoder.rb +14 -7
  12. data/lib/faraday/error.rb +24 -5
  13. data/lib/faraday/logging/formatter.rb +28 -15
  14. data/lib/faraday/middleware.rb +43 -2
  15. data/lib/faraday/middleware_registry.rb +17 -63
  16. data/lib/faraday/options/connection_options.rb +7 -6
  17. data/lib/faraday/options/env.rb +85 -62
  18. data/lib/faraday/options/proxy_options.rb +7 -3
  19. data/lib/faraday/options/request_options.rb +7 -6
  20. data/lib/faraday/options/ssl_options.rb +59 -45
  21. data/lib/faraday/options.rb +7 -6
  22. data/lib/faraday/rack_builder.rb +23 -21
  23. data/lib/faraday/request/authorization.rb +33 -41
  24. data/lib/faraday/request/instrumentation.rb +5 -1
  25. data/lib/faraday/request/json.rb +18 -3
  26. data/lib/faraday/request/url_encoded.rb +5 -1
  27. data/lib/faraday/request.rb +15 -31
  28. data/lib/faraday/response/json.rb +25 -5
  29. data/lib/faraday/response/logger.rb +6 -0
  30. data/lib/faraday/response/raise_error.rb +45 -18
  31. data/lib/faraday/response.rb +9 -21
  32. data/lib/faraday/utils/headers.rb +15 -4
  33. data/lib/faraday/utils.rb +11 -7
  34. data/lib/faraday/version.rb +1 -1
  35. data/lib/faraday.rb +8 -44
  36. data/spec/faraday/adapter/test_spec.rb +65 -0
  37. data/spec/faraday/connection_spec.rb +165 -93
  38. data/spec/faraday/error_spec.rb +31 -6
  39. data/spec/faraday/middleware_registry_spec.rb +31 -0
  40. data/spec/faraday/middleware_spec.rb +161 -0
  41. data/spec/faraday/options/env_spec.rb +8 -2
  42. data/spec/faraday/options/options_spec.rb +1 -1
  43. data/spec/faraday/options/proxy_options_spec.rb +8 -0
  44. data/spec/faraday/params_encoders/nested_spec.rb +10 -1
  45. data/spec/faraday/rack_builder_spec.rb +26 -54
  46. data/spec/faraday/request/authorization_spec.rb +50 -28
  47. data/spec/faraday/request/instrumentation_spec.rb +5 -7
  48. data/spec/faraday/request/json_spec.rb +88 -0
  49. data/spec/faraday/request/url_encoded_spec.rb +12 -2
  50. data/spec/faraday/request_spec.rb +5 -15
  51. data/spec/faraday/response/json_spec.rb +93 -6
  52. data/spec/faraday/response/logger_spec.rb +38 -0
  53. data/spec/faraday/response/raise_error_spec.rb +111 -5
  54. data/spec/faraday/response_spec.rb +3 -1
  55. data/spec/faraday/utils/headers_spec.rb +31 -4
  56. data/spec/faraday/utils_spec.rb +64 -1
  57. data/spec/faraday_spec.rb +10 -4
  58. data/spec/spec_helper.rb +5 -6
  59. data/spec/support/fake_safe_buffer.rb +1 -1
  60. data/spec/support/faraday_middleware_subclasses.rb +18 -0
  61. data/spec/support/helper_methods.rb +0 -37
  62. data/spec/support/shared_examples/adapter.rb +2 -2
  63. data/spec/support/shared_examples/request_method.rb +22 -21
  64. metadata +24 -145
  65. data/lib/faraday/adapter/typhoeus.rb +0 -15
  66. data/lib/faraday/autoload.rb +0 -87
  67. data/lib/faraday/dependency_loader.rb +0 -37
  68. data/lib/faraday/deprecate.rb +0 -109
  69. data/lib/faraday/request/basic_authentication.rb +0 -20
  70. data/lib/faraday/request/token_authentication.rb +0 -20
  71. data/spec/faraday/adapter/em_http_spec.rb +0 -49
  72. data/spec/faraday/adapter/em_synchrony_spec.rb +0 -18
  73. data/spec/faraday/adapter/excon_spec.rb +0 -49
  74. data/spec/faraday/adapter/httpclient_spec.rb +0 -73
  75. data/spec/faraday/adapter/net_http_spec.rb +0 -64
  76. data/spec/faraday/adapter/patron_spec.rb +0 -18
  77. data/spec/faraday/adapter/rack_spec.rb +0 -8
  78. data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
  79. data/spec/faraday/composite_read_io_spec.rb +0 -80
  80. data/spec/faraday/deprecate_spec.rb +0 -147
  81. data/spec/faraday/response/middleware_spec.rb +0 -68
  82. data/spec/support/webmock_rack_app.rb +0 -68
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Faraday
4
- # RequestOptions contains the configurable properties for a Faraday request.
5
- class RequestOptions < Options.new(:params_encoder, :proxy, :bind,
6
- :timeout, :open_timeout, :read_timeout,
7
- :write_timeout, :boundary, :oauth,
8
- :context, :on_data)
9
-
4
+ # @!parse
5
+ # # RequestOptions contains the configurable properties for a Faraday request.
6
+ # class RequestOptions < Options; end
7
+ RequestOptions = Options.new(:params_encoder, :proxy, :bind,
8
+ :timeout, :open_timeout, :read_timeout,
9
+ :write_timeout, :boundary, :oauth,
10
+ :context, :on_data) do
10
11
  def []=(key, value)
11
12
  if key && key.to_sym == :proxy
12
13
  super(key, value ? ProxyOptions.from(value) : nil)
@@ -1,51 +1,60 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Faraday
4
- # SSL-related options.
5
- #
6
- # @!attribute verify
7
- # @return [Boolean] whether to verify SSL certificates or not
8
- #
9
- # @!attribute ca_file
10
- # @return [String] CA file
11
- #
12
- # @!attribute ca_path
13
- # @return [String] CA path
14
- #
15
- # @!attribute verify_mode
16
- # @return [Integer] Any `OpenSSL::SSL::` constant (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL.html)
17
- #
18
- # @!attribute cert_store
19
- # @return [OpenSSL::X509::Store] certificate store
20
- #
21
- # @!attribute client_cert
22
- # @return [String, OpenSSL::X509::Certificate] client certificate
23
- #
24
- # @!attribute client_key
25
- # @return [String, OpenSSL::PKey::RSA, OpenSSL::PKey::DSA] client key
26
- #
27
- # @!attribute certificate
28
- # @return [OpenSSL::X509::Certificate] certificate (Excon only)
29
- #
30
- # @!attribute private_key
31
- # @return [OpenSSL::PKey::RSA, OpenSSL::PKey::DSA] private key (Excon only)
32
- #
33
- # @!attribute verify_depth
34
- # @return [Integer] maximum depth for the certificate chain verification
35
- #
36
- # @!attribute version
37
- # @return [String, Symbol] SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-ssl_version-3D)
38
- #
39
- # @!attribute min_version
40
- # @return [String, Symbol] minimum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D)
41
- #
42
- # @!attribute max_version
43
- # @return [String, Symbol] maximum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D)
44
- class SSLOptions < Options.new(:verify, :ca_file, :ca_path, :verify_mode,
45
- :cert_store, :client_cert, :client_key,
46
- :certificate, :private_key, :verify_depth,
47
- :version, :min_version, :max_version)
48
-
4
+ # @!parse
5
+ # # SSL-related options.
6
+ # #
7
+ # # @!attribute verify
8
+ # # @return [Boolean] whether to verify SSL certificates or not
9
+ # #
10
+ # # @!attribute verify_hostname
11
+ # # @return [Boolean] whether to enable hostname verification on server certificates
12
+ # # during the handshake or not (see https://github.com/ruby/openssl/pull/60)
13
+ # #
14
+ # # @!attribute ca_file
15
+ # # @return [String] CA file
16
+ # #
17
+ # # @!attribute ca_path
18
+ # # @return [String] CA path
19
+ # #
20
+ # # @!attribute verify_mode
21
+ # # @return [Integer] Any `OpenSSL::SSL::` constant (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL.html)
22
+ # #
23
+ # # @!attribute cert_store
24
+ # # @return [OpenSSL::X509::Store] certificate store
25
+ # #
26
+ # # @!attribute client_cert
27
+ # # @return [String, OpenSSL::X509::Certificate] client certificate
28
+ # #
29
+ # # @!attribute client_key
30
+ # # @return [String, OpenSSL::PKey::RSA, OpenSSL::PKey::DSA] client key
31
+ # #
32
+ # # @!attribute certificate
33
+ # # @return [OpenSSL::X509::Certificate] certificate (Excon only)
34
+ # #
35
+ # # @!attribute private_key
36
+ # # @return [OpenSSL::PKey::RSA, OpenSSL::PKey::DSA] private key (Excon only)
37
+ # #
38
+ # # @!attribute verify_depth
39
+ # # @return [Integer] maximum depth for the certificate chain verification
40
+ # #
41
+ # # @!attribute version
42
+ # # @return [String, Symbol] SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-ssl_version-3D)
43
+ # #
44
+ # # @!attribute min_version
45
+ # # @return [String, Symbol] minimum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D)
46
+ # #
47
+ # # @!attribute max_version
48
+ # # @return [String, Symbol] maximum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D)
49
+ # #
50
+ # # @!attribute ciphers
51
+ # # @return [String] cipher list in OpenSSL format (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-ciphers-3D)
52
+ # class SSLOptions < Options; end
53
+ SSLOptions = Options.new(:verify, :verify_hostname,
54
+ :ca_file, :ca_path, :verify_mode,
55
+ :cert_store, :client_cert, :client_key,
56
+ :certificate, :private_key, :verify_depth,
57
+ :version, :min_version, :max_version, :ciphers) do
49
58
  # @return [Boolean] true if should verify
50
59
  def verify?
51
60
  verify != false
@@ -55,5 +64,10 @@ module Faraday
55
64
  def disable?
56
65
  !verify?
57
66
  end
67
+
68
+ # @return [Boolean] true if should verify_hostname
69
+ def verify_hostname?
70
+ verify_hostname != false
71
+ end
58
72
  end
59
73
  end
@@ -30,7 +30,7 @@ module Faraday
30
30
  new_value = value
31
31
  end
32
32
 
33
- send("#{key}=", new_value) unless new_value.nil?
33
+ send(:"#{key}=", new_value) unless new_value.nil?
34
34
  end
35
35
  self
36
36
  end
@@ -38,7 +38,7 @@ module Faraday
38
38
  # Public
39
39
  def delete(key)
40
40
  value = send(key)
41
- send("#{key}=", nil)
41
+ send(:"#{key}=", nil)
42
42
  value
43
43
  end
44
44
 
@@ -57,7 +57,7 @@ module Faraday
57
57
  else
58
58
  other_value
59
59
  end
60
- send("#{key}=", new_value) unless new_value.nil?
60
+ send(:"#{key}=", new_value) unless new_value.nil?
61
61
  end
62
62
  self
63
63
  end
@@ -104,7 +104,7 @@ module Faraday
104
104
 
105
105
  # Public
106
106
  def each_key(&block)
107
- return to_enum(:each_key) unless block_given?
107
+ return to_enum(:each_key) unless block
108
108
 
109
109
  keys.each(&block)
110
110
  end
@@ -118,7 +118,7 @@ module Faraday
118
118
 
119
119
  # Public
120
120
  def each_value(&block)
121
- return to_enum(:each_value) unless block_given?
121
+ return to_enum(:each_value) unless block
122
122
 
123
123
  values.each(&block)
124
124
  end
@@ -168,12 +168,13 @@ module Faraday
168
168
  end
169
169
 
170
170
  def self.memoized(key, &block)
171
- unless block_given?
171
+ unless block
172
172
  raise ArgumentError, '#memoized must be called with a block'
173
173
  end
174
174
 
175
175
  memoized_attributes[key.to_sym] = block
176
176
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
177
+ remove_method(key) if method_defined?(key, false)
177
178
  def #{key}() self[:#{key}]; end
178
179
  RUBY
179
180
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ruby2_keywords'
4
3
  require 'faraday/adapter_registry'
5
4
 
6
5
  module Faraday
@@ -8,7 +7,7 @@ module Faraday
8
7
  # middleware stack (heavily inspired by Rack).
9
8
  #
10
9
  # @example
11
- # Faraday::Connection.new(url: 'http://sushi.com') do |builder|
10
+ # Faraday::Connection.new(url: 'http://httpbingo.org') do |builder|
12
11
  # builder.request :url_encoded # Faraday::Request::UrlEncoded
13
12
  # builder.adapter :net_http # Faraday::Adapter::NetHttp
14
13
  # end
@@ -58,23 +57,22 @@ module Faraday
58
57
  end
59
58
  end
60
59
 
61
- def initialize(handlers = [], adapter = nil, &block)
62
- @adapter = adapter
63
- @handlers = handlers
64
- if block_given?
65
- build(&block)
66
- elsif @handlers.empty?
67
- # default stack, if nothing else is configured
68
- request :url_encoded
69
- self.adapter Faraday.default_adapter
70
- end
60
+ def initialize(&block)
61
+ @adapter = nil
62
+ @handlers = []
63
+ build(&block)
64
+ end
65
+
66
+ def initialize_dup(original)
67
+ super
68
+ @adapter = original.adapter
69
+ @handlers = original.handlers.dup
71
70
  end
72
71
 
73
- def build(options = {})
72
+ def build
74
73
  raise_if_locked
75
- @handlers.clear unless options[:keep]
76
- yield(self) if block_given?
77
- adapter(Faraday.default_adapter) unless @adapter
74
+ block_given? ? yield(self) : request(:url_encoded)
75
+ adapter(Faraday.default_adapter, **Faraday.default_adapter_options) unless @adapter
78
76
  end
79
77
 
80
78
  def [](idx)
@@ -109,7 +107,7 @@ module Faraday
109
107
  end
110
108
 
111
109
  ruby2_keywords def adapter(klass = NO_ARGUMENT, *args, &block)
112
- return @adapter if klass == NO_ARGUMENT
110
+ return @adapter if klass == NO_ARGUMENT || klass.nil?
113
111
 
114
112
  klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol)
115
113
  @adapter = self.class::Handler.new(klass, *args, &block)
@@ -164,6 +162,7 @@ module Faraday
164
162
  def app
165
163
  @app ||= begin
166
164
  lock!
165
+ ensure_adapter!
167
166
  to_app
168
167
  end
169
168
  end
@@ -182,10 +181,6 @@ module Faraday
182
181
  @adapter == other.adapter
183
182
  end
184
183
 
185
- def dup
186
- self.class.new(@handlers.dup, @adapter.dup)
187
- end
188
-
189
184
  # ENV Keys
190
185
  # :http_method - a symbolized request HTTP method (:get, :post)
191
186
  # :body - the request body that will eventually be converted to a string.
@@ -216,6 +211,9 @@ module Faraday
216
211
  private
217
212
 
218
213
  LOCK_ERR = "can't modify middleware stack after making a request"
214
+ MISSING_ADAPTER_ERROR = "An attempt to run a request with a Faraday::Connection without adapter has been made.\n" \
215
+ "Please set Faraday.default_adapter or provide one when initializing the connection.\n" \
216
+ 'For more info, check https://lostisland.github.io/faraday/usage/.'
219
217
 
220
218
  def raise_if_locked
221
219
  raise StackLocked, LOCK_ERR if locked?
@@ -227,6 +225,10 @@ module Faraday
227
225
  raise 'Adapter should be set using the `adapter` method, not `use`'
228
226
  end
229
227
 
228
+ def ensure_adapter!
229
+ raise MISSING_ADAPTER_ERROR unless @adapter
230
+ end
231
+
230
232
  def adapter_set?
231
233
  !@adapter.nil?
232
234
  end
@@ -1,53 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'base64'
4
-
5
3
  module Faraday
6
4
  class Request
7
5
  # Request middleware for the Authorization HTTP header
8
6
  class Authorization < Faraday::Middleware
9
- unless defined?(::Faraday::Request::Authorization::KEY)
10
- KEY = 'Authorization'
11
- end
12
-
13
- # @param type [String, Symbol]
14
- # @param token [String, Symbol, Hash]
15
- # @return [String] a header value
16
- def self.header(type, token)
17
- case token
18
- when String, Symbol, Proc
19
- token = token.call if token.is_a?(Proc)
20
- "#{type} #{token}"
21
- when Hash
22
- build_hash(type.to_s, token)
23
- else
24
- raise ArgumentError,
25
- "Can't build an Authorization #{type}" \
26
- "header from #{token.inspect}"
27
- end
28
- end
29
-
30
- # @param type [String]
31
- # @param hash [Hash]
32
- # @return [String] type followed by comma-separated key=value pairs
33
- # @api private
34
- def self.build_hash(type, hash)
35
- comma = ', '
36
- values = []
37
- hash.each do |key, value|
38
- value = value.call if value.is_a?(Proc)
39
- values << "#{key}=#{value.to_s.inspect}"
40
- end
41
- "#{type} #{values * comma}"
42
- end
7
+ KEY = 'Authorization'
43
8
 
44
9
  # @param app [#call]
45
10
  # @param type [String, Symbol] Type of Authorization
46
- # @param param [String, Symbol, Hash, Proc] parameter to build the Authorization header.
47
- # This value can be a proc, in which case it will be invoked on each request.
48
- def initialize(app, type, param)
11
+ # @param params [Array<String, Proc, #call>] parameters to build the Authorization header.
12
+ # If the type is `:basic`, then these can be a login and password pair.
13
+ # Otherwise, a single value is expected that will be appended after the type.
14
+ # This value can be a proc or an object responding to `.call`, in which case
15
+ # it will be invoked on each request.
16
+ def initialize(app, type, *params)
49
17
  @type = type
50
- @param = param
18
+ @params = params
51
19
  super(app)
52
20
  end
53
21
 
@@ -55,8 +23,32 @@ module Faraday
55
23
  def on_request(env)
56
24
  return if env.request_headers[KEY]
57
25
 
58
- env.request_headers[KEY] = self.class.header(@type, @param)
26
+ env.request_headers[KEY] = header_from(@type, env, *@params)
27
+ end
28
+
29
+ private
30
+
31
+ # @param type [String, Symbol]
32
+ # @param env [Faraday::Env]
33
+ # @param params [Array]
34
+ # @return [String] a header value
35
+ def header_from(type, env, *params)
36
+ if type.to_s.casecmp('basic').zero? && params.size == 2
37
+ Utils.basic_header_from(*params)
38
+ elsif params.size != 1
39
+ raise ArgumentError, "Unexpected params received (got #{params.size} instead of 1)"
40
+ else
41
+ value = params.first
42
+ if (value.is_a?(Proc) && value.arity == 1) || (value.respond_to?(:call) && value.method(:call).arity == 1)
43
+ value = value.call(env)
44
+ elsif value.is_a?(Proc) || value.respond_to?(:call)
45
+ value = value.call
46
+ end
47
+ "#{type} #{value}"
48
+ end
59
49
  end
60
50
  end
61
51
  end
62
52
  end
53
+
54
+ Faraday::Request.register_middleware(authorization: Faraday::Request::Authorization)
@@ -5,12 +5,14 @@ module Faraday
5
5
  # Middleware for instrumenting Requests.
6
6
  class Instrumentation < Faraday::Middleware
7
7
  # Options class used in Request::Instrumentation class.
8
- class Options < Faraday::Options.new(:name, :instrumenter)
8
+ Options = Faraday::Options.new(:name, :instrumenter) do
9
+ remove_method :name
9
10
  # @return [String]
10
11
  def name
11
12
  self[:name] ||= 'request.faraday'
12
13
  end
13
14
 
15
+ remove_method :instrumenter
14
16
  # @return [Class]
15
17
  def instrumenter
16
18
  self[:instrumenter] ||= ActiveSupport::Notifications
@@ -52,3 +54,5 @@ module Faraday
52
54
  end
53
55
  end
54
56
  end
57
+
58
+ Faraday::Request.register_middleware(instrumentation: Faraday::Request::Instrumentation)
@@ -13,7 +13,7 @@ module Faraday
13
13
  # Doesn't try to encode bodies that already are in string form.
14
14
  class Json < Middleware
15
15
  MIME_TYPE = 'application/json'
16
- MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}.freeze
16
+ MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}
17
17
 
18
18
  def on_request(env)
19
19
  match_content_type(env) do |data|
@@ -24,7 +24,13 @@ module Faraday
24
24
  private
25
25
 
26
26
  def encode(data)
27
- ::JSON.generate(data)
27
+ if options[:encoder].is_a?(Array) && options[:encoder].size >= 2
28
+ options[:encoder][0].public_send(options[:encoder][1], data)
29
+ elsif options[:encoder].respond_to?(:dump)
30
+ options[:encoder].dump(data)
31
+ else
32
+ ::JSON.generate(data)
33
+ end
28
34
  end
29
35
 
30
36
  def match_content_type(env)
@@ -40,7 +46,16 @@ module Faraday
40
46
  end
41
47
 
42
48
  def body?(env)
43
- (body = env[:body]) && !(body.respond_to?(:to_str) && body.empty?)
49
+ body = env[:body]
50
+ case body
51
+ when true, false
52
+ true
53
+ when nil
54
+ # NOTE: nil can be converted to `"null"`, but this middleware doesn't process `nil` for the compatibility.
55
+ false
56
+ else
57
+ !(body.respond_to?(:to_str) && body.empty?)
58
+ end
44
59
  end
45
60
 
46
61
  def request_type(env)
@@ -31,7 +31,9 @@ module Faraday
31
31
  return unless process_request?(env)
32
32
 
33
33
  env.request_headers[CONTENT_TYPE] ||= self.class.mime_type
34
- yield(env.body) unless env.body.respond_to?(:to_str)
34
+ return if env.body.respond_to?(:to_str) || env.body.respond_to?(:read)
35
+
36
+ yield(env.body)
35
37
  end
36
38
 
37
39
  # @param env [Faraday::Env]
@@ -54,3 +56,5 @@ module Faraday
54
56
  end
55
57
  end
56
58
  end
59
+
60
+ Faraday::Request.register_middleware(url_encoded: Faraday::Request::UrlEncoded)
@@ -21,31 +21,16 @@ module Faraday
21
21
  # @!attribute headers
22
22
  # @return [Faraday::Utils::Headers] headers
23
23
  # @!attribute body
24
- # @return [Hash] body
24
+ # @return [String] body
25
25
  # @!attribute options
26
26
  # @return [RequestOptions] options
27
- #
28
- # rubocop:disable Style/StructInheritance
29
- class Request < Struct.new(
30
- :http_method, :path, :params, :headers, :body, :options
31
- )
32
- # rubocop:enable Style/StructInheritance
33
-
27
+ Request = Struct.new(:http_method, :path, :params, :headers, :body, :options) do
34
28
  extend MiddlewareRegistry
35
29
 
36
- register_middleware File.expand_path('request', __dir__),
37
- url_encoded: [:UrlEncoded, 'url_encoded'],
38
- authorization: [:Authorization, 'authorization'],
39
- basic_auth: [
40
- :BasicAuthentication,
41
- 'basic_authentication'
42
- ],
43
- token_auth: [
44
- :TokenAuthentication,
45
- 'token_authentication'
46
- ],
47
- instrumentation: [:Instrumentation, 'instrumentation'],
48
- json: [:Json, 'json']
30
+ alias_method :member_get, :[]
31
+ private :member_get
32
+ alias_method :member_set, :[]=
33
+ private :member_set
49
34
 
50
35
  # @param request_method [String]
51
36
  # @yield [request] for block customization, if block given
@@ -57,14 +42,7 @@ module Faraday
57
42
  end
58
43
  end
59
44
 
60
- def method
61
- warn <<~TEXT
62
- WARNING: `Faraday::Request##{__method__}` is deprecated; use `#http_method` instead. It will be removed in or after version 2.0.
63
- `Faraday::Request##{__method__}` called from #{caller_locations(1..1).first}
64
- TEXT
65
- http_method
66
- end
67
-
45
+ remove_method :params=
68
46
  # Replace params, preserving the existing hash type.
69
47
  #
70
48
  # @param hash [Hash] new params
@@ -72,10 +50,11 @@ module Faraday
72
50
  if params
73
51
  params.replace hash
74
52
  else
75
- super
53
+ member_set(:params, hash)
76
54
  end
77
55
  end
78
56
 
57
+ remove_method :headers=
79
58
  # Replace request headers, preserving the existing hash type.
80
59
  #
81
60
  # @param hash [Hash] new headers
@@ -83,7 +62,7 @@ module Faraday
83
62
  if headers
84
63
  headers.replace hash
85
64
  else
86
- super
65
+ member_set(:headers, hash)
87
66
  end
88
67
  end
89
68
 
@@ -153,3 +132,8 @@ module Faraday
153
132
  end
154
133
  end
155
134
  end
135
+
136
+ require 'faraday/request/authorization'
137
+ require 'faraday/request/instrumentation'
138
+ require 'faraday/request/json'
139
+ require 'faraday/request/url_encoded'
@@ -6,11 +6,13 @@ module Faraday
6
6
  class Response
7
7
  # Parse response bodies as JSON.
8
8
  class Json < Middleware
9
- def initialize(app = nil, options = {})
9
+ def initialize(app = nil, parser_options: nil, content_type: /\bjson$/, preserve_raw: false)
10
10
  super(app)
11
- @parser_options = options[:parser_options]
12
- @content_types = Array(options[:content_type] || /\bjson$/)
13
- @preserve_raw = options[:preserve_raw]
11
+ @parser_options = parser_options
12
+ @content_types = Array(content_type)
13
+ @preserve_raw = preserve_raw
14
+
15
+ process_parser_options
14
16
  end
15
17
 
16
18
  def on_complete(env)
@@ -27,7 +29,11 @@ module Faraday
27
29
  end
28
30
 
29
31
  def parse(body)
30
- ::JSON.parse(body, @parser_options || {}) unless body.strip.empty?
32
+ return if body.strip.empty?
33
+
34
+ decoder, method_name = @decoder_options
35
+
36
+ decoder.public_send(method_name, body, @parser_options || {})
31
37
  end
32
38
 
33
39
  def parse_response?(env)
@@ -47,6 +53,20 @@ module Faraday
47
53
  type = type.split(';', 2).first if type.index(';')
48
54
  type
49
55
  end
56
+
57
+ def process_parser_options
58
+ @decoder_options = @parser_options&.delete(:decoder)
59
+
60
+ @decoder_options =
61
+ if @decoder_options.is_a?(Array) && @decoder_options.size >= 2
62
+ @decoder_options.slice(0, 2)
63
+ elsif @decoder_options&.respond_to?(:load) # rubocop:disable Lint/RedundantSafeNavigation
64
+ # In some versions of Rails, `nil` responds to `load` - hence the safe navigation check above
65
+ [@decoder_options, :load]
66
+ else
67
+ [::JSON, :parse]
68
+ end
69
+ end
50
70
  end
51
71
  end
52
72
  end
@@ -26,6 +26,12 @@ module Faraday
26
26
  def on_complete(env)
27
27
  @formatter.response(env)
28
28
  end
29
+
30
+ def on_error(exc)
31
+ @formatter.exception(exc) if @formatter.respond_to?(:exception)
32
+ end
29
33
  end
30
34
  end
31
35
  end
36
+
37
+ Faraday::Response.register_middleware(logger: Faraday::Response::Logger)