faraday 0.15.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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +276 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +18 -344
  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/em_http.rb +144 -101
  9. data/lib/faraday/adapter/em_http_ssl_patch.rb +24 -18
  10. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +18 -15
  11. data/lib/faraday/adapter/em_synchrony.rb +104 -60
  12. data/lib/faraday/adapter/excon.rb +98 -56
  13. data/lib/faraday/adapter/httpclient.rb +83 -59
  14. data/lib/faraday/adapter/net_http.rb +130 -63
  15. data/lib/faraday/adapter/net_http_persistent.rb +51 -28
  16. data/lib/faraday/adapter/patron.rb +80 -43
  17. data/lib/faraday/adapter/rack.rb +30 -13
  18. data/lib/faraday/adapter/test.rb +86 -53
  19. data/lib/faraday/adapter/typhoeus.rb +4 -1
  20. data/lib/faraday/adapter.rb +82 -22
  21. data/lib/faraday/adapter_registry.rb +30 -0
  22. data/lib/faraday/autoload.rb +47 -36
  23. data/lib/faraday/connection.rb +312 -182
  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 +103 -37
  28. data/lib/faraday/file_part.rb +128 -0
  29. data/lib/faraday/logging/formatter.rb +105 -0
  30. data/lib/faraday/middleware.rb +12 -28
  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 +35 -186
  38. data/lib/faraday/param_part.rb +53 -0
  39. data/lib/faraday/parameters.rb +4 -197
  40. data/lib/faraday/rack_builder.rb +67 -56
  41. data/lib/faraday/request/authorization.rb +44 -30
  42. data/lib/faraday/request/basic_authentication.rb +14 -7
  43. data/lib/faraday/request/instrumentation.rb +45 -27
  44. data/lib/faraday/request/multipart.rb +79 -48
  45. data/lib/faraday/request/retry.rb +198 -169
  46. data/lib/faraday/request/token_authentication.rb +15 -10
  47. data/lib/faraday/request/url_encoded.rb +43 -23
  48. data/lib/faraday/request.rb +68 -36
  49. data/lib/faraday/response/logger.rb +22 -69
  50. data/lib/faraday/response/raise_error.rb +38 -14
  51. data/lib/faraday/response.rb +27 -17
  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 +36 -245
  55. data/lib/faraday.rb +94 -176
  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 +78 -9
  102. data/lib/faraday/upload_io.rb +0 -67
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Faraday
2
- # Used to setup urls, params, headers, and the request body in a sane manner.
4
+ # Used to setup URLs, params, headers, and the request body in a sane manner.
3
5
  #
6
+ # @example
4
7
  # @connection.post do |req|
5
8
  # req.url 'http://localhost', 'a' => '1' # 'http://localhost?a=1'
6
9
  # req.headers['b'] = '2' # Header
@@ -9,25 +12,53 @@ module Faraday
9
12
  # req.body = 'abc'
10
13
  # end
11
14
  #
15
+ # @!attribute method
16
+ # @return [Symbol] the HTTP method of the Request
17
+ # @!attribute path
18
+ # @return [URI, String] the path
19
+ # @!attribute params
20
+ # @return [Hash] query parameters
21
+ # @!attribute headers
22
+ # @return [Faraday::Utils::Headers] headers
23
+ # @!attribute body
24
+ # @return [Hash] body
25
+ # @!attribute options
26
+ # @return [RequestOptions] options
27
+ #
28
+ # rubocop:disable Style/StructInheritance
12
29
  class Request < Struct.new(:method, :path, :params, :headers, :body, :options)
30
+ # rubocop:enable Style/StructInheritance
31
+
13
32
  extend MiddlewareRegistry
14
33
 
15
- register_middleware File.expand_path('../request', __FILE__),
16
- :url_encoded => [:UrlEncoded, 'url_encoded'],
17
- :multipart => [:Multipart, 'multipart'],
18
- :retry => [:Retry, 'retry'],
19
- :authorization => [:Authorization, 'authorization'],
20
- :basic_auth => [:BasicAuthentication, 'basic_authentication'],
21
- :token_auth => [:TokenAuthentication, 'token_authentication'],
22
- :instrumentation => [:Instrumentation, 'instrumentation']
34
+ register_middleware File.expand_path('request', __dir__),
35
+ url_encoded: [:UrlEncoded, 'url_encoded'],
36
+ multipart: [:Multipart, 'multipart'],
37
+ retry: [:Retry, 'retry'],
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']
23
48
 
49
+ # @param request_method [String]
50
+ # @yield [request] for block customization, if block given
51
+ # @yieldparam request [Request]
52
+ # @return [Request]
24
53
  def self.create(request_method)
25
54
  new(request_method).tap do |request|
26
55
  yield(request) if block_given?
27
56
  end
28
57
  end
29
58
 
30
- # Public: Replace params, preserving the existing hash type
59
+ # Replace params, preserving the existing hash type.
60
+ #
61
+ # @param hash [Hash] new params
31
62
  def params=(hash)
32
63
  if params
33
64
  params.replace hash
@@ -36,7 +67,9 @@ module Faraday
36
67
  end
37
68
  end
38
69
 
39
- # Public: Replace request headers, preserving the existing hash type
70
+ # Replace request headers, preserving the existing hash type.
71
+ #
72
+ # @param hash [Hash] new headers
40
73
  def headers=(hash)
41
74
  if headers
42
75
  headers.replace hash
@@ -45,9 +78,14 @@ module Faraday
45
78
  end
46
79
  end
47
80
 
81
+ # Update path and params.
82
+ #
83
+ # @param path [URI, String]
84
+ # @param params [Hash, nil]
85
+ # @return [void]
48
86
  def url(path, params = nil)
49
87
  if path.respond_to? :query
50
- if query = path.query
88
+ if (query = path.query)
51
89
  path = path.dup
52
90
  path.query = nil
53
91
  end
@@ -61,25 +99,35 @@ module Faraday
61
99
  self.params.update(params) if params
62
100
  end
63
101
 
102
+ # @param key [Object] key to look up in headers
103
+ # @return [Object] value of the given header name
64
104
  def [](key)
65
105
  headers[key]
66
106
  end
67
107
 
108
+ # @param key [Object] key of header to write
109
+ # @param value [Object] value of header
68
110
  def []=(key, value)
69
111
  headers[key] = value
70
112
  end
71
113
 
114
+ # Marshal serialization support.
115
+ #
116
+ # @return [Hash] the hash ready to be serialized in Marshal.
72
117
  def marshal_dump
73
118
  {
74
- :method => method,
75
- :body => body,
76
- :headers => headers,
77
- :path => path,
78
- :params => params,
79
- :options => options
119
+ method: method,
120
+ body: body,
121
+ headers: headers,
122
+ path: path,
123
+ params: params,
124
+ options: options
80
125
  }
81
126
  end
82
127
 
128
+ # Marshal serialization support.
129
+ # Restores the instance variables according to the +serialised+.
130
+ # @param serialised [Hash] the serialised object.
83
131
  def marshal_load(serialised)
84
132
  self.method = serialised[:method]
85
133
  self.body = serialised[:body]
@@ -89,26 +137,10 @@ module Faraday
89
137
  self.options = serialised[:options]
90
138
  end
91
139
 
92
- # ENV Keys
93
- # :method - a symbolized request method (:get, :post)
94
- # :body - the request body that will eventually be converted to a string.
95
- # :url - URI instance for the current request.
96
- # :status - HTTP response status code
97
- # :request_headers - hash of HTTP Headers to be sent to the server
98
- # :response_headers - Hash of HTTP headers from the server
99
- # :parallel_manager - sent if the connection is in parallel mode
100
- # :request - Hash of options for configuring the request.
101
- # :timeout - open/read timeout Integer in seconds
102
- # :open_timeout - read timeout Integer in seconds
103
- # :proxy - Hash of proxy options
104
- # :uri - Proxy Server URI
105
- # :user - Proxy server username
106
- # :password - Proxy server password
107
- # :ssl - Hash of options for configuring SSL requests.
140
+ # @return [Env] the Env for this Request
108
141
  def to_env(connection)
109
142
  Env.new(method, body, connection.build_exclusive_url(path, params),
110
- options, headers, connection.ssl, connection.parallel_manager)
143
+ options, headers, connection.ssl, connection.parallel_manager)
111
144
  end
112
145
  end
113
146
  end
114
-
@@ -1,80 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
4
+ require 'faraday/logging/formatter'
2
5
 
3
6
  module Faraday
4
- class Response::Logger < Response::Middleware
5
- extend Forwardable
6
-
7
- DEFAULT_OPTIONS = { :headers => true, :bodies => false }
8
-
9
- def initialize(app, logger = nil, options = {})
10
- super(app)
11
- @logger = logger || begin
12
- require 'logger'
13
- ::Logger.new($stdout)
14
- end
15
- @filter = []
16
- @options = DEFAULT_OPTIONS.merge(options)
17
- yield self if block_given?
18
- end
19
-
20
- def_delegators :@logger, :debug, :info, :warn, :error, :fatal
21
-
22
- def call(env)
23
- info('request') { "#{env.method.upcase} #{apply_filters(env.url.to_s)}" }
24
- debug('request') { apply_filters( dump_headers env.request_headers ) } if log_headers?(:request)
25
- debug('request') { apply_filters( dump_body(env[:body]) ) } if env[:body] && log_body?(:request)
26
- super
27
- end
28
-
29
- def on_complete(env)
30
- info('response') { "Status #{env.status.to_s}" }
31
- debug('response') { apply_filters( dump_headers env.response_headers ) } if log_headers?(:response)
32
- debug('response') { apply_filters( dump_body env[:body] ) } if env[:body] && log_body?(:response)
33
- end
34
-
35
- def filter(filter_word, filter_replacement)
36
- @filter.push([ filter_word, filter_replacement ])
37
- end
38
-
39
- private
40
-
41
- def dump_headers(headers)
42
- headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
43
- end
44
-
45
- def dump_body(body)
46
- if body.respond_to?(:to_str)
47
- body.to_str
48
- else
49
- pretty_inspect(body)
50
- end
51
- end
52
-
53
- def pretty_inspect(body)
54
- require 'pp' unless body.respond_to?(:pretty_inspect)
55
- body.pretty_inspect
56
- end
57
-
58
- def log_headers?(type)
59
- case @options[:headers]
60
- when Hash then @options[:headers][type]
61
- else @options[:headers]
7
+ class Response
8
+ # Logger is a middleware that logs internal events in the HTTP request
9
+ # lifecycle to a given Logger object. By default, this logs to STDOUT. See
10
+ # Faraday::Logging::Formatter to see specifically what is logged.
11
+ class Logger < Middleware
12
+ def initialize(app, logger = nil, options = {})
13
+ super(app)
14
+ logger ||= begin
15
+ require 'logger'
16
+ ::Logger.new($stdout)
17
+ end
18
+ formatter_class = options.delete(:formatter) || Logging::Formatter
19
+ @formatter = formatter_class.new(logger: logger, options: options)
20
+ yield @formatter if block_given?
62
21
  end
63
- end
64
22
 
65
- def log_body?(type)
66
- case @options[:bodies]
67
- when Hash then @options[:bodies][type]
68
- else @options[:bodies]
23
+ def call(env)
24
+ @formatter.request(env)
25
+ super
69
26
  end
70
- end
71
27
 
72
- def apply_filters(output)
73
- @filter.each do |pattern, replacement|
74
- output = output.to_s.gsub(pattern, replacement)
28
+ def on_complete(env)
29
+ @formatter.response(env)
75
30
  end
76
- output
77
31
  end
78
-
79
32
  end
80
33
  end
@@ -1,21 +1,45 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Faraday
2
- class Response::RaiseError < Response::Middleware
3
- ClientErrorStatuses = 400...600
4
+ class Response
5
+ # RaiseError is a Faraday middleware that raises exceptions on common HTTP
6
+ # client or server error responses.
7
+ class RaiseError < Middleware
8
+ # rubocop:disable Naming/ConstantName
9
+ ClientErrorStatuses = (400...500).freeze
10
+ ServerErrorStatuses = (500...600).freeze
11
+ # rubocop:enable Naming/ConstantName
4
12
 
5
- def on_complete(env)
6
- case env[:status]
7
- when 404
8
- raise Faraday::Error::ResourceNotFound, response_values(env)
9
- when 407
10
- # mimic the behavior that we get with proxy requests with HTTPS
11
- raise Faraday::Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
12
- when ClientErrorStatuses
13
- raise Faraday::Error::ClientError, response_values(env)
13
+ def on_complete(env)
14
+ case env[:status]
15
+ when 400
16
+ raise Faraday::BadRequestError, response_values(env)
17
+ when 401
18
+ raise Faraday::UnauthorizedError, response_values(env)
19
+ when 403
20
+ raise Faraday::ForbiddenError, response_values(env)
21
+ when 404
22
+ raise Faraday::ResourceNotFound, response_values(env)
23
+ when 407
24
+ # mimic the behavior that we get with proxy requests with HTTPS
25
+ msg = %(407 "Proxy Authentication Required")
26
+ raise Faraday::ProxyAuthError.new(msg, response_values(env))
27
+ when 409
28
+ raise Faraday::ConflictError, response_values(env)
29
+ when 422
30
+ raise Faraday::UnprocessableEntityError, response_values(env)
31
+ when ClientErrorStatuses
32
+ raise Faraday::ClientError, response_values(env)
33
+ when ServerErrorStatuses
34
+ raise Faraday::ServerError, response_values(env)
35
+ when nil
36
+ raise Faraday::NilStatusError, response_values(env)
37
+ end
14
38
  end
15
- end
16
39
 
17
- def response_values(env)
18
- {:status => env.status, :headers => env.response_headers, :body => env.body}
40
+ def response_values(env)
41
+ { status: env.status, headers: env.response_headers, body: env.body }
42
+ end
19
43
  end
20
44
  end
21
45
  end
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
 
3
5
  module Faraday
6
+ # Response represents an HTTP response from making an HTTP request.
4
7
  class Response
5
8
  # Used for simple response middleware.
6
9
  class Middleware < Faraday::Middleware
@@ -12,17 +15,20 @@ module Faraday
12
15
 
13
16
  # Override this to modify the environment after the response has finished.
14
17
  # Calls the `parse` method if defined
18
+ # `parse` method can be defined as private, public and protected
15
19
  def on_complete(env)
16
- env.body = parse(env.body) if respond_to?(:parse) && env.parse_body?
20
+ return unless respond_to?(:parse, true) && env.parse_body?
21
+
22
+ env.body = parse(env.body)
17
23
  end
18
24
  end
19
25
 
20
26
  extend Forwardable
21
27
  extend MiddlewareRegistry
22
28
 
23
- register_middleware File.expand_path('../response', __FILE__),
24
- :raise_error => [:RaiseError, 'raise_error'],
25
- :logger => [:Logger, 'logger']
29
+ register_middleware File.expand_path('response', __dir__),
30
+ raise_error: [:RaiseError, 'raise_error'],
31
+ logger: [:Logger, 'logger']
26
32
 
27
33
  def initialize(env = nil)
28
34
  @env = Env.from(env) if env
@@ -31,8 +37,6 @@ module Faraday
31
37
 
32
38
  attr_reader :env
33
39
 
34
- def_delegators :env, :to_hash
35
-
36
40
  def status
37
41
  finished? ? env.status : nil
38
42
  end
@@ -54,32 +58,37 @@ module Faraday
54
58
  !!env
55
59
  end
56
60
 
57
- def on_complete
58
- if not finished?
59
- @on_complete_callbacks << Proc.new
61
+ def on_complete(&block)
62
+ if !finished?
63
+ @on_complete_callbacks << block
60
64
  else
61
65
  yield(env)
62
66
  end
63
- return self
67
+ self
64
68
  end
65
69
 
66
70
  def finish(env)
67
- raise "response already finished" if finished?
71
+ raise 'response already finished' if finished?
72
+
68
73
  @env = env.is_a?(Env) ? env : Env.from(env)
69
74
  @on_complete_callbacks.each { |callback| callback.call(@env) }
70
- return self
75
+ self
71
76
  end
72
77
 
73
78
  def success?
74
79
  finished? && env.success?
75
80
  end
76
81
 
82
+ def to_hash
83
+ {
84
+ status: env.status, body: env.body,
85
+ response_headers: env.response_headers
86
+ }
87
+ end
88
+
77
89
  # because @on_complete_callbacks cannot be marshalled
78
90
  def marshal_dump
79
- !finished? ? nil : {
80
- :status => @env.status, :body => @env.body,
81
- :response_headers => @env.response_headers
82
- }
91
+ finished? ? to_hash : nil
83
92
  end
84
93
 
85
94
  def marshal_load(env)
@@ -90,8 +99,9 @@ module Faraday
90
99
  # Useful for applying request params after restoring a marshalled Response.
91
100
  def apply_request(request_env)
92
101
  raise "response didn't finish yet" unless finished?
102
+
93
103
  @env = Env.from(request_env).update(@env)
94
- return self
104
+ self
95
105
  end
96
106
  end
97
107
  end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ module Utils
5
+ # A case-insensitive Hash that preserves the original case of a header
6
+ # when set.
7
+ #
8
+ # Adapted from Rack::Utils::HeaderHash
9
+ class Headers < ::Hash
10
+ def self.from(value)
11
+ new(value)
12
+ end
13
+
14
+ def self.allocate
15
+ new_self = super
16
+ new_self.initialize_names
17
+ new_self
18
+ end
19
+
20
+ def initialize(hash = nil)
21
+ super()
22
+ @names = {}
23
+ update(hash || {})
24
+ end
25
+
26
+ def initialize_names
27
+ @names = {}
28
+ end
29
+
30
+ # on dup/clone, we need to duplicate @names hash
31
+ def initialize_copy(other)
32
+ super
33
+ @names = other.names.dup
34
+ end
35
+
36
+ # need to synchronize concurrent writes to the shared KeyMap
37
+ keymap_mutex = Mutex.new
38
+
39
+ # symbol -> string mapper + cache
40
+ KeyMap = Hash.new do |map, key|
41
+ value = if key.respond_to?(:to_str)
42
+ key
43
+ else
44
+ key.to_s.split('_') # user_agent: %w(user agent)
45
+ .each(&:capitalize!) # => %w(User Agent)
46
+ .join('-') # => "User-Agent"
47
+ end
48
+ keymap_mutex.synchronize { map[key] = value }
49
+ end
50
+ KeyMap[:etag] = 'ETag'
51
+
52
+ def [](key)
53
+ key = KeyMap[key]
54
+ super(key) || super(@names[key.downcase])
55
+ end
56
+
57
+ def []=(key, val)
58
+ key = KeyMap[key]
59
+ key = (@names[key.downcase] ||= key)
60
+ # join multiple values with a comma
61
+ val = val.to_ary.join(', ') if val.respond_to?(:to_ary)
62
+ super(key, val)
63
+ end
64
+
65
+ def fetch(key, *args, &block)
66
+ key = KeyMap[key]
67
+ key = @names.fetch(key.downcase, key)
68
+ super(key, *args, &block)
69
+ end
70
+
71
+ def delete(key)
72
+ key = KeyMap[key]
73
+ key = @names[key.downcase]
74
+ return unless key
75
+
76
+ @names.delete key.downcase
77
+ super(key)
78
+ end
79
+
80
+ def include?(key)
81
+ @names.include? key.downcase
82
+ end
83
+
84
+ alias has_key? include?
85
+ alias member? include?
86
+ alias key? include?
87
+
88
+ def merge!(other)
89
+ other.each { |k, v| self[k] = v }
90
+ self
91
+ end
92
+
93
+ alias update merge!
94
+
95
+ def merge(other)
96
+ hash = dup
97
+ hash.merge! other
98
+ end
99
+
100
+ def replace(other)
101
+ clear
102
+ @names.clear
103
+ update other
104
+ self
105
+ end
106
+
107
+ def to_hash
108
+ ::Hash.new.update(self)
109
+ end
110
+
111
+ def parse(header_string)
112
+ return unless header_string && !header_string.empty?
113
+
114
+ headers = header_string.split(/\r\n/)
115
+
116
+ # Find the last set of response headers.
117
+ start_index = headers.rindex { |x| x.match(%r{^HTTP/}) } || 0
118
+ last_response = headers.slice(start_index, headers.size)
119
+
120
+ last_response
121
+ .tap { |a| a.shift if a.first.start_with?('HTTP/') }
122
+ .map { |h| h.split(/:\s*/, 2) } # split key and value
123
+ .reject { |p| p[0].nil? } # ignore blank lines
124
+ .each { |key, value| add_parsed(key, value) }
125
+ end
126
+
127
+ protected
128
+
129
+ attr_reader :names
130
+
131
+ private
132
+
133
+ # Join multiple values with a comma.
134
+ def add_parsed(key, value)
135
+ self[key] ? self[key] << ', ' << value : self[key] = value
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ module Utils
5
+ # A hash with stringified keys.
6
+ class ParamsHash < Hash
7
+ def [](key)
8
+ super(convert_key(key))
9
+ end
10
+
11
+ def []=(key, value)
12
+ super(convert_key(key), value)
13
+ end
14
+
15
+ def delete(key)
16
+ super(convert_key(key))
17
+ end
18
+
19
+ def include?(key)
20
+ super(convert_key(key))
21
+ end
22
+
23
+ alias has_key? include?
24
+ alias member? include?
25
+ alias key? include?
26
+
27
+ def update(params)
28
+ params.each do |key, value|
29
+ self[key] = value
30
+ end
31
+ self
32
+ end
33
+ alias merge! update
34
+
35
+ def merge(params)
36
+ dup.update(params)
37
+ end
38
+
39
+ def replace(other)
40
+ clear
41
+ update(other)
42
+ end
43
+
44
+ def merge_query(query, encoder = nil)
45
+ return self unless query && !query.empty?
46
+
47
+ update((encoder || Utils.default_params_encoder).decode(query))
48
+ end
49
+
50
+ def to_query(encoder = nil)
51
+ (encoder || Utils.default_params_encoder).encode(self)
52
+ end
53
+
54
+ private
55
+
56
+ def convert_key(key)
57
+ key.to_s
58
+ end
59
+ end
60
+ end
61
+ end