faraday 0.16.1 → 0.17.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +232 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +358 -18
  5. data/Rakefile +13 -0
  6. data/lib/faraday.rb +174 -93
  7. data/lib/faraday/adapter.rb +22 -36
  8. data/lib/faraday/adapter/em_http.rb +97 -140
  9. data/lib/faraday/adapter/em_http_ssl_patch.rb +17 -23
  10. data/lib/faraday/adapter/em_synchrony.rb +60 -104
  11. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +15 -18
  12. data/lib/faraday/adapter/excon.rb +55 -100
  13. data/lib/faraday/adapter/httpclient.rb +39 -61
  14. data/lib/faraday/adapter/net_http.rb +51 -103
  15. data/lib/faraday/adapter/net_http_persistent.rb +28 -49
  16. data/lib/faraday/adapter/patron.rb +35 -54
  17. data/lib/faraday/adapter/rack.rb +12 -28
  18. data/lib/faraday/adapter/test.rb +53 -86
  19. data/lib/faraday/adapter/typhoeus.rb +1 -4
  20. data/lib/faraday/autoload.rb +36 -47
  21. data/lib/faraday/connection.rb +179 -321
  22. data/lib/faraday/deprecate.rb +109 -0
  23. data/lib/faraday/error.rb +72 -28
  24. data/lib/faraday/middleware.rb +28 -4
  25. data/lib/faraday/options.rb +183 -32
  26. data/lib/faraday/parameters.rb +197 -4
  27. data/lib/faraday/rack_builder.rb +55 -66
  28. data/lib/faraday/request.rb +36 -68
  29. data/lib/faraday/request/authorization.rb +30 -42
  30. data/lib/faraday/request/basic_authentication.rb +7 -14
  31. data/lib/faraday/request/instrumentation.rb +27 -45
  32. data/lib/faraday/request/multipart.rb +48 -79
  33. data/lib/faraday/request/retry.rb +171 -197
  34. data/lib/faraday/request/token_authentication.rb +10 -15
  35. data/lib/faraday/request/url_encoded.rb +23 -41
  36. data/lib/faraday/response.rb +13 -20
  37. data/lib/faraday/response/logger.rb +69 -22
  38. data/lib/faraday/response/raise_error.rb +18 -36
  39. data/lib/faraday/upload_io.rb +67 -0
  40. data/lib/faraday/utils.rb +245 -28
  41. data/spec/faraday/deprecate_spec.rb +147 -0
  42. data/spec/faraday/error_spec.rb +102 -0
  43. data/spec/faraday/response/raise_error_spec.rb +106 -0
  44. data/spec/spec_helper.rb +105 -0
  45. data/test/adapters/default_test.rb +14 -0
  46. data/test/adapters/em_http_test.rb +30 -0
  47. data/test/adapters/em_synchrony_test.rb +32 -0
  48. data/test/adapters/excon_test.rb +30 -0
  49. data/test/adapters/httpclient_test.rb +34 -0
  50. data/test/adapters/integration.rb +263 -0
  51. data/test/adapters/logger_test.rb +136 -0
  52. data/test/adapters/net_http_persistent_test.rb +114 -0
  53. data/test/adapters/net_http_test.rb +79 -0
  54. data/test/adapters/patron_test.rb +40 -0
  55. data/test/adapters/rack_test.rb +38 -0
  56. data/test/adapters/test_middleware_test.rb +157 -0
  57. data/test/adapters/typhoeus_test.rb +38 -0
  58. data/test/authentication_middleware_test.rb +65 -0
  59. data/test/composite_read_io_test.rb +109 -0
  60. data/test/connection_test.rb +738 -0
  61. data/test/env_test.rb +268 -0
  62. data/test/helper.rb +75 -0
  63. data/test/live_server.rb +67 -0
  64. data/test/middleware/instrumentation_test.rb +88 -0
  65. data/test/middleware/retry_test.rb +282 -0
  66. data/test/middleware_stack_test.rb +260 -0
  67. data/test/multibyte.txt +1 -0
  68. data/test/options_test.rb +333 -0
  69. data/test/parameters_test.rb +157 -0
  70. data/test/request_middleware_test.rb +126 -0
  71. data/test/response_middleware_test.rb +72 -0
  72. data/test/strawberry.rb +2 -0
  73. data/test/utils_test.rb +98 -0
  74. metadata +50 -26
  75. data/lib/faraday/adapter_registry.rb +0 -28
  76. data/lib/faraday/dependency_loader.rb +0 -37
  77. data/lib/faraday/deprecated_constant.rb +0 -53
  78. data/lib/faraday/encoders/flat_params_encoder.rb +0 -94
  79. data/lib/faraday/encoders/nested_params_encoder.rb +0 -171
  80. data/lib/faraday/file_part.rb +0 -128
  81. data/lib/faraday/logging/formatter.rb +0 -92
  82. data/lib/faraday/middleware_registry.rb +0 -129
  83. data/lib/faraday/options/connection_options.rb +0 -22
  84. data/lib/faraday/options/env.rb +0 -181
  85. data/lib/faraday/options/proxy_options.rb +0 -28
  86. data/lib/faraday/options/request_options.rb +0 -21
  87. data/lib/faraday/options/ssl_options.rb +0 -59
  88. data/lib/faraday/param_part.rb +0 -53
  89. data/lib/faraday/utils/headers.rb +0 -139
  90. data/lib/faraday/utils/params_hash.rb +0 -61
  91. data/spec/external_adapters/faraday_specs_setup.rb +0 -14
@@ -1,9 +1,6 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'forwardable'
4
2
 
5
3
  module Faraday
6
- # Response represents an HTTP response from making an HTTP request.
7
4
  class Response
8
5
  # Used for simple response middleware.
9
6
  class Middleware < Faraday::Middleware
@@ -23,9 +20,9 @@ module Faraday
23
20
  extend Forwardable
24
21
  extend MiddlewareRegistry
25
22
 
26
- register_middleware File.expand_path('response', __dir__),
27
- raise_error: [:RaiseError, 'raise_error'],
28
- logger: [:Logger, 'logger']
23
+ register_middleware File.expand_path('../response', __FILE__),
24
+ :raise_error => [:RaiseError, 'raise_error'],
25
+ :logger => [:Logger, 'logger']
29
26
 
30
27
  def initialize(env = nil)
31
28
  @env = Env.from(env) if env
@@ -34,6 +31,8 @@ module Faraday
34
31
 
35
32
  attr_reader :env
36
33
 
34
+ def_delegators :env, :to_hash
35
+
37
36
  def status
38
37
  finished? ? env.status : nil
39
38
  end
@@ -61,31 +60,26 @@ module Faraday
61
60
  else
62
61
  yield(env)
63
62
  end
64
- self
63
+ return self
65
64
  end
66
65
 
67
66
  def finish(env)
68
- raise 'response already finished' if finished?
69
-
67
+ raise "response already finished" if finished?
70
68
  @env = env.is_a?(Env) ? env : Env.from(env)
71
69
  @on_complete_callbacks.each { |callback| callback.call(@env) }
72
- self
70
+ return self
73
71
  end
74
72
 
75
73
  def success?
76
74
  finished? && env.success?
77
75
  end
78
76
 
79
- def to_hash
80
- {
81
- status: env.status, body: env.body,
82
- response_headers: env.response_headers
83
- }
84
- end
85
-
86
77
  # because @on_complete_callbacks cannot be marshalled
87
78
  def marshal_dump
88
- finished? ? to_hash : nil
79
+ !finished? ? nil : {
80
+ :status => @env.status, :body => @env.body,
81
+ :response_headers => @env.response_headers
82
+ }
89
83
  end
90
84
 
91
85
  def marshal_load(env)
@@ -96,9 +90,8 @@ module Faraday
96
90
  # Useful for applying request params after restoring a marshalled Response.
97
91
  def apply_request(request_env)
98
92
  raise "response didn't finish yet" unless finished?
99
-
100
93
  @env = Env.from(request_env).update(@env)
101
- self
94
+ return self
102
95
  end
103
96
  end
104
97
  end
@@ -1,33 +1,80 @@
1
- # frozen_string_literal: true
2
-
3
1
  require 'forwardable'
4
- require 'faraday/logging/formatter'
5
2
 
6
3
  module Faraday
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?
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]
21
62
  end
63
+ end
22
64
 
23
- def call(env)
24
- @formatter.request(env)
25
- super
65
+ def log_body?(type)
66
+ case @options[:bodies]
67
+ when Hash then @options[:bodies][type]
68
+ else @options[:bodies]
26
69
  end
70
+ end
27
71
 
28
- def on_complete(env)
29
- @formatter.response(env)
72
+ def apply_filters(output)
73
+ @filter.each do |pattern, replacement|
74
+ output = output.to_s.gsub(pattern, replacement)
30
75
  end
76
+ output
31
77
  end
78
+
32
79
  end
33
80
  end
@@ -1,43 +1,25 @@
1
- # frozen_string_literal: true
2
-
3
1
  module Faraday
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
2
+ class Response::RaiseError < Response::Middleware
3
+ ClientErrorStatuses = 400...600
12
4
 
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
- end
5
+ def on_complete(env)
6
+ case env[:status]
7
+ when 404
8
+ raise Faraday::ResourceNotFound, response_values(env)
9
+ when 407
10
+ # mimic the behavior that we get with proxy requests with HTTPS
11
+ raise Faraday::ConnectionFailed.new(
12
+ %{407 "Proxy Authentication Required "},
13
+ response_values(env))
14
+ when ClientErrorStatuses
15
+ raise Faraday::ClientError, response_values(env)
16
+ when nil
17
+ raise Faraday::NilStatusError, response_values(env)
36
18
  end
19
+ end
37
20
 
38
- def response_values(env)
39
- { status: env.status, headers: env.response_headers, body: env.body }
40
- end
21
+ def response_values(env)
22
+ {:status => env.status, :headers => env.response_headers, :body => env.body}
41
23
  end
42
24
  end
43
25
  end
@@ -0,0 +1,67 @@
1
+ begin
2
+ require 'composite_io'
3
+ require 'parts'
4
+ require 'stringio'
5
+ rescue LoadError
6
+ $stderr.puts "Install the multipart-post gem."
7
+ raise
8
+ end
9
+
10
+ module Faraday
11
+ # Similar but not compatible with ::CompositeReadIO provided by multipart-post.
12
+ class CompositeReadIO
13
+ def initialize(*parts)
14
+ @parts = parts.flatten
15
+ @ios = @parts.map { |part| part.to_io }
16
+ @index = 0
17
+ end
18
+
19
+ def length
20
+ @parts.inject(0) { |sum, part| sum + part.length }
21
+ end
22
+
23
+ def rewind
24
+ @ios.each { |io| io.rewind }
25
+ @index = 0
26
+ end
27
+
28
+ # Read from IOs in order until `length` bytes have been received.
29
+ def read(length = nil, outbuf = nil)
30
+ got_result = false
31
+ outbuf = outbuf ? outbuf.replace("") : ""
32
+
33
+ while io = current_io
34
+ if result = io.read(length)
35
+ got_result ||= !result.nil?
36
+ result.force_encoding("BINARY") if result.respond_to?(:force_encoding)
37
+ outbuf << result
38
+ length -= result.length if length
39
+ break if length == 0
40
+ end
41
+ advance_io
42
+ end
43
+ (!got_result && length) ? nil : outbuf
44
+ end
45
+
46
+ def close
47
+ @ios.each { |io| io.close }
48
+ end
49
+
50
+ def ensure_open_and_readable
51
+ # Rubinius compatibility
52
+ end
53
+
54
+ private
55
+
56
+ def current_io
57
+ @ios[@index]
58
+ end
59
+
60
+ def advance_io
61
+ @index += 1
62
+ end
63
+ end
64
+
65
+ UploadIO = ::UploadIO
66
+ Parts = ::Parts
67
+ end
data/lib/faraday/utils.rb CHANGED
@@ -1,12 +1,193 @@
1
- # frozen_string_literal: true
2
-
3
- require 'faraday/utils/headers'
4
- require 'faraday/utils/params_hash'
1
+ require 'thread'
5
2
 
6
3
  module Faraday
7
- # Utils contains various static helper methods.
8
4
  module Utils
9
- module_function
5
+ extend self
6
+
7
+ # Adapted from Rack::Utils::HeaderHash
8
+ class Headers < ::Hash
9
+ def self.from(value)
10
+ new(value)
11
+ end
12
+
13
+ def self.allocate
14
+ new_self = super
15
+ new_self.initialize_names
16
+ new_self
17
+ end
18
+
19
+ def initialize(hash = nil)
20
+ super()
21
+ @names = {}
22
+ self.update(hash || {})
23
+ end
24
+
25
+ def initialize_names
26
+ @names = {}
27
+ end
28
+
29
+ # on dup/clone, we need to duplicate @names hash
30
+ def initialize_copy(other)
31
+ super
32
+ @names = other.names.dup
33
+ end
34
+
35
+ # need to synchronize concurrent writes to the shared KeyMap
36
+ keymap_mutex = Mutex.new
37
+
38
+ # symbol -> string mapper + cache
39
+ KeyMap = Hash.new do |map, key|
40
+ value = if key.respond_to?(:to_str)
41
+ key
42
+ else
43
+ key.to_s.split('_'). # :user_agent => %w(user agent)
44
+ each { |w| w.capitalize! }. # => %w(User Agent)
45
+ join('-') # => "User-Agent"
46
+ end
47
+ keymap_mutex.synchronize { map[key] = value }
48
+ end
49
+ KeyMap[:etag] = "ETag"
50
+
51
+ def [](k)
52
+ k = KeyMap[k]
53
+ super(k) || super(@names[k.downcase])
54
+ end
55
+
56
+ def []=(k, v)
57
+ k = KeyMap[k]
58
+ k = (@names[k.downcase] ||= k)
59
+ # join multiple values with a comma
60
+ v = v.to_ary.join(', ') if v.respond_to? :to_ary
61
+ super(k, v)
62
+ end
63
+
64
+ def fetch(k, *args, &block)
65
+ k = KeyMap[k]
66
+ key = @names.fetch(k.downcase, k)
67
+ super(key, *args, &block)
68
+ end
69
+
70
+ def delete(k)
71
+ k = KeyMap[k]
72
+ if k = @names[k.downcase]
73
+ @names.delete k.downcase
74
+ super(k)
75
+ end
76
+ end
77
+
78
+ def include?(k)
79
+ @names.include? k.downcase
80
+ end
81
+
82
+ alias_method :has_key?, :include?
83
+ alias_method :member?, :include?
84
+ alias_method :key?, :include?
85
+
86
+ def merge!(other)
87
+ other.each { |k, v| self[k] = v }
88
+ self
89
+ end
90
+ alias_method :update, :merge!
91
+
92
+ def merge(other)
93
+ hash = dup
94
+ hash.merge! other
95
+ end
96
+
97
+ def replace(other)
98
+ clear
99
+ @names.clear
100
+ self.update other
101
+ self
102
+ end
103
+
104
+ def to_hash() ::Hash.new.update(self) end
105
+
106
+ def parse(header_string)
107
+ return unless header_string && !header_string.empty?
108
+
109
+ headers = header_string.split(/\r\n/)
110
+
111
+ # Find the last set of response headers.
112
+ start_index = headers.rindex { |x| x.match(/^HTTP\//) } || 0
113
+ last_response = headers.slice(start_index, headers.size)
114
+
115
+ last_response.
116
+ tap { |a| a.shift if a.first.index('HTTP/') == 0 }. # drop the HTTP status line
117
+ map { |h| h.split(/:\s*/, 2) }.reject { |p| p[0].nil? }. # split key and value, ignore blank lines
118
+ each { |key, value|
119
+ # join multiple values with a comma
120
+ if self[key]
121
+ self[key] << ', ' << value
122
+ else
123
+ self[key] = value
124
+ end
125
+ }
126
+ end
127
+
128
+ protected
129
+
130
+ def names
131
+ @names
132
+ end
133
+ end
134
+
135
+ # hash with stringified keys
136
+ class ParamsHash < Hash
137
+ def [](key)
138
+ super(convert_key(key))
139
+ end
140
+
141
+ def []=(key, value)
142
+ super(convert_key(key), value)
143
+ end
144
+
145
+ def delete(key)
146
+ super(convert_key(key))
147
+ end
148
+
149
+ def include?(key)
150
+ super(convert_key(key))
151
+ end
152
+
153
+ alias_method :has_key?, :include?
154
+ alias_method :member?, :include?
155
+ alias_method :key?, :include?
156
+
157
+ def update(params)
158
+ params.each do |key, value|
159
+ self[key] = value
160
+ end
161
+ self
162
+ end
163
+ alias_method :merge!, :update
164
+
165
+ def merge(params)
166
+ dup.update(params)
167
+ end
168
+
169
+ def replace(other)
170
+ clear
171
+ update(other)
172
+ end
173
+
174
+ def merge_query(query, encoder = nil)
175
+ if query && !query.empty?
176
+ update((encoder || Utils.default_params_encoder).decode(query))
177
+ end
178
+ self
179
+ end
180
+
181
+ def to_query(encoder = nil)
182
+ (encoder || Utils.default_params_encoder).encode(self)
183
+ end
184
+
185
+ private
186
+
187
+ def convert_key(key)
188
+ key.to_s
189
+ end
190
+ end
10
191
 
11
192
  def build_query(params)
12
193
  FlatParamsEncoder.encode(params)
@@ -16,19 +197,17 @@ module Faraday
16
197
  NestedParamsEncoder.encode(params)
17
198
  end
18
199
 
19
- ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/.freeze
200
+ ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
20
201
 
21
- def escape(str)
22
- str.to_s.gsub(ESCAPE_RE) do |match|
202
+ def escape(s)
203
+ s.to_s.gsub(ESCAPE_RE) {|match|
23
204
  '%' + match.unpack('H2' * match.bytesize).join('%').upcase
24
- end.tr(' ', '+')
205
+ }.tr(' ', '+')
25
206
  end
26
207
 
27
- def unescape(str)
28
- CGI.unescape str.to_s
29
- end
208
+ def unescape(s) CGI.unescape s.to_s end
30
209
 
31
- DEFAULT_SEP = /[&;] */n.freeze
210
+ DEFAULT_SEP = /[&;] */n
32
211
 
33
212
  # Adapted from Rack
34
213
  def parse_query(query)
@@ -47,18 +226,55 @@ module Faraday
47
226
  attr_writer :default_params_encoder
48
227
  end
49
228
 
229
+ # Stolen from Rack
230
+ def normalize_params(params, name, v = nil)
231
+ name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
232
+ k = $1 || ''
233
+ after = $' || ''
234
+
235
+ return if k.empty?
236
+
237
+ if after == ""
238
+ if params[k]
239
+ params[k] = Array[params[k]] unless params[k].kind_of?(Array)
240
+ params[k] << v
241
+ else
242
+ params[k] = v
243
+ end
244
+ elsif after == "[]"
245
+ params[k] ||= []
246
+ raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
247
+ params[k] << v
248
+ elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
249
+ child_key = $1
250
+ params[k] ||= []
251
+ raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
252
+ if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key)
253
+ normalize_params(params[k].last, child_key, v)
254
+ else
255
+ params[k] << normalize_params({}, child_key, v)
256
+ end
257
+ else
258
+ params[k] ||= {}
259
+ raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash)
260
+ params[k] = normalize_params(params[k], after, v)
261
+ end
262
+
263
+ return params
264
+ end
265
+
50
266
  # Normalize URI() behavior across Ruby versions
51
267
  #
52
268
  # url - A String or URI.
53
269
  #
54
270
  # Returns a parsed URI.
55
- def URI(url) # rubocop:disable Naming/MethodName
271
+ def URI(url)
56
272
  if url.respond_to?(:host)
57
273
  url
58
274
  elsif url.respond_to?(:to_str)
59
275
  default_uri_parser.call(url)
60
276
  else
61
- raise ArgumentError, 'bad argument (expected URI object or URI string)'
277
+ raise ArgumentError, "bad argument (expected URI object or URI string)"
62
278
  end
63
279
  end
64
280
 
@@ -71,28 +287,27 @@ module Faraday
71
287
 
72
288
  def default_uri_parser=(parser)
73
289
  @default_uri_parser = if parser.respond_to?(:call) || parser.nil?
74
- parser
75
- else
76
- parser.method(:parse)
77
- end
290
+ parser
291
+ else
292
+ parser.method(:parse)
293
+ end
78
294
  end
79
295
 
80
- # Receives a String or URI and returns just
81
- # the path with the query string sorted.
296
+ # Receives a String or URI and returns just the path with the query string sorted.
82
297
  def normalize_path(url)
83
298
  url = URI(url)
84
299
  (url.path.start_with?('/') ? url.path : '/' + url.path) +
85
- (url.query ? "?#{sort_query_params(url.query)}" : '')
300
+ (url.query ? "?#{sort_query_params(url.query)}" : "")
86
301
  end
87
302
 
88
303
  # Recursive hash update
89
304
  def deep_merge!(target, hash)
90
305
  hash.each do |key, value|
91
- target[key] = if value.is_a?(Hash) && target[key].is_a?(Hash)
92
- deep_merge(target[key], value)
93
- else
94
- value
95
- end
306
+ if Hash === value and Hash === target[key]
307
+ target[key] = deep_merge(target[key], value)
308
+ else
309
+ target[key] = value
310
+ end
96
311
  end
97
312
  target
98
313
  end
@@ -102,6 +317,8 @@ module Faraday
102
317
  deep_merge!(source.dup, hash)
103
318
  end
104
319
 
320
+ protected
321
+
105
322
  def sort_query_params(query)
106
323
  query.split('&').sort.join('&')
107
324
  end