faraday 0.13.0 → 2.0.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 (92) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +496 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +28 -328
  5. data/Rakefile +7 -0
  6. data/examples/client_spec.rb +97 -0
  7. data/examples/client_test.rb +118 -0
  8. data/lib/faraday/adapter/test.rb +127 -68
  9. data/lib/faraday/adapter.rb +71 -22
  10. data/lib/faraday/adapter_registry.rb +30 -0
  11. data/lib/faraday/connection.rb +314 -226
  12. data/lib/faraday/encoders/flat_params_encoder.rb +105 -0
  13. data/lib/faraday/encoders/nested_params_encoder.rb +176 -0
  14. data/lib/faraday/error.rb +121 -37
  15. data/lib/faraday/logging/formatter.rb +106 -0
  16. data/lib/faraday/methods.rb +6 -0
  17. data/lib/faraday/middleware.rb +18 -25
  18. data/lib/faraday/middleware_registry.rb +65 -0
  19. data/lib/faraday/options/connection_options.rb +22 -0
  20. data/lib/faraday/options/env.rb +181 -0
  21. data/lib/faraday/options/proxy_options.rb +32 -0
  22. data/lib/faraday/options/request_options.rb +22 -0
  23. data/lib/faraday/options/ssl_options.rb +59 -0
  24. data/lib/faraday/options.rb +41 -195
  25. data/lib/faraday/parameters.rb +4 -196
  26. data/lib/faraday/rack_builder.rb +91 -74
  27. data/lib/faraday/request/authorization.rb +37 -29
  28. data/lib/faraday/request/instrumentation.rb +47 -27
  29. data/lib/faraday/request/json.rb +55 -0
  30. data/lib/faraday/request/url_encoded.rb +45 -23
  31. data/lib/faraday/request.rb +74 -32
  32. data/lib/faraday/response/json.rb +54 -0
  33. data/lib/faraday/response/logger.rb +22 -69
  34. data/lib/faraday/response/raise_error.rb +57 -14
  35. data/lib/faraday/response.rb +26 -33
  36. data/lib/faraday/utils/headers.rb +139 -0
  37. data/lib/faraday/utils/params_hash.rb +61 -0
  38. data/lib/faraday/utils.rb +47 -251
  39. data/lib/faraday/version.rb +5 -0
  40. data/lib/faraday.rb +104 -197
  41. data/spec/external_adapters/faraday_specs_setup.rb +14 -0
  42. data/spec/faraday/adapter/test_spec.rb +377 -0
  43. data/spec/faraday/adapter_registry_spec.rb +28 -0
  44. data/spec/faraday/adapter_spec.rb +55 -0
  45. data/spec/faraday/connection_spec.rb +787 -0
  46. data/spec/faraday/error_spec.rb +60 -0
  47. data/spec/faraday/middleware_spec.rb +52 -0
  48. data/spec/faraday/options/env_spec.rb +70 -0
  49. data/spec/faraday/options/options_spec.rb +297 -0
  50. data/spec/faraday/options/proxy_options_spec.rb +44 -0
  51. data/spec/faraday/options/request_options_spec.rb +19 -0
  52. data/spec/faraday/params_encoders/flat_spec.rb +42 -0
  53. data/spec/faraday/params_encoders/nested_spec.rb +142 -0
  54. data/spec/faraday/rack_builder_spec.rb +302 -0
  55. data/spec/faraday/request/authorization_spec.rb +83 -0
  56. data/spec/faraday/request/instrumentation_spec.rb +74 -0
  57. data/spec/faraday/request/json_spec.rb +111 -0
  58. data/spec/faraday/request/url_encoded_spec.rb +82 -0
  59. data/spec/faraday/request_spec.rb +109 -0
  60. data/spec/faraday/response/json_spec.rb +117 -0
  61. data/spec/faraday/response/logger_spec.rb +220 -0
  62. data/spec/faraday/response/raise_error_spec.rb +172 -0
  63. data/spec/faraday/response_spec.rb +75 -0
  64. data/spec/faraday/utils/headers_spec.rb +82 -0
  65. data/spec/faraday/utils_spec.rb +117 -0
  66. data/spec/faraday_spec.rb +37 -0
  67. data/spec/spec_helper.rb +132 -0
  68. data/spec/support/disabling_stub.rb +14 -0
  69. data/spec/support/fake_safe_buffer.rb +15 -0
  70. data/spec/support/helper_methods.rb +96 -0
  71. data/spec/support/shared_examples/adapter.rb +104 -0
  72. data/spec/support/shared_examples/params_encoder.rb +18 -0
  73. data/spec/support/shared_examples/request_method.rb +249 -0
  74. data/spec/support/streaming_response_checker.rb +35 -0
  75. metadata +71 -34
  76. data/lib/faraday/adapter/em_http.rb +0 -243
  77. data/lib/faraday/adapter/em_http_ssl_patch.rb +0 -56
  78. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +0 -66
  79. data/lib/faraday/adapter/em_synchrony.rb +0 -106
  80. data/lib/faraday/adapter/excon.rb +0 -80
  81. data/lib/faraday/adapter/httpclient.rb +0 -128
  82. data/lib/faraday/adapter/net_http.rb +0 -135
  83. data/lib/faraday/adapter/net_http_persistent.rb +0 -54
  84. data/lib/faraday/adapter/patron.rb +0 -83
  85. data/lib/faraday/adapter/rack.rb +0 -58
  86. data/lib/faraday/adapter/typhoeus.rb +0 -123
  87. data/lib/faraday/autoload.rb +0 -84
  88. data/lib/faraday/request/basic_authentication.rb +0 -13
  89. data/lib/faraday/request/multipart.rb +0 -68
  90. data/lib/faraday/request/retry.rb +0 -164
  91. data/lib/faraday/request/token_authentication.rb +0 -15
  92. data/lib/faraday/upload_io.rb +0 -67
@@ -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
+ {}.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.start_with?('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
data/lib/faraday/utils.rb CHANGED
@@ -1,193 +1,14 @@
1
- require 'thread'
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+ require 'uri'
5
+ require 'faraday/utils/headers'
6
+ require 'faraday/utils/params_hash'
2
7
 
3
8
  module Faraday
9
+ # Utils contains various static helper methods.
4
10
  module Utils
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
11
+ module_function
191
12
 
192
13
  def build_query(params)
193
14
  FlatParamsEncoder.encode(params)
@@ -197,17 +18,27 @@ module Faraday
197
18
  NestedParamsEncoder.encode(params)
198
19
  end
199
20
 
200
- ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
21
+ def default_space_encoding
22
+ @default_space_encoding ||= '+'
23
+ end
24
+
25
+ class << self
26
+ attr_writer :default_space_encoding
27
+ end
28
+
29
+ ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/.freeze
201
30
 
202
- def escape(s)
203
- s.to_s.gsub(ESCAPE_RE) {|match|
204
- '%' + match.unpack('H2' * match.bytesize).join('%').upcase
205
- }.tr(' ', '+')
31
+ def escape(str)
32
+ str.to_s.gsub(ESCAPE_RE) do |match|
33
+ "%#{match.unpack('H2' * match.bytesize).join('%').upcase}"
34
+ end.gsub(' ', default_space_encoding)
206
35
  end
207
36
 
208
- def unescape(s) CGI.unescape s.to_s end
37
+ def unescape(str)
38
+ CGI.unescape str.to_s
39
+ end
209
40
 
210
- DEFAULT_SEP = /[&;] */n
41
+ DEFAULT_SEP = /[&;] */n.freeze
211
42
 
212
43
  # Adapted from Rack
213
44
  def parse_query(query)
@@ -222,45 +53,14 @@ module Faraday
222
53
  @default_params_encoder ||= NestedParamsEncoder
223
54
  end
224
55
 
225
- class << self
226
- attr_writer :default_params_encoder
56
+ def basic_header_from(login, pass)
57
+ value = Base64.encode64("#{login}:#{pass}")
58
+ value.delete!("\n")
59
+ "Basic #{value}"
227
60
  end
228
61
 
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
62
+ class << self
63
+ attr_writer :default_params_encoder
264
64
  end
265
65
 
266
66
  # Normalize URI() behavior across Ruby versions
@@ -268,46 +68,44 @@ module Faraday
268
68
  # url - A String or URI.
269
69
  #
270
70
  # Returns a parsed URI.
271
- def URI(url)
71
+ def URI(url) # rubocop:disable Naming/MethodName
272
72
  if url.respond_to?(:host)
273
73
  url
274
74
  elsif url.respond_to?(:to_str)
275
75
  default_uri_parser.call(url)
276
76
  else
277
- raise ArgumentError, "bad argument (expected URI object or URI string)"
77
+ raise ArgumentError, 'bad argument (expected URI object or URI string)'
278
78
  end
279
79
  end
280
80
 
281
81
  def default_uri_parser
282
- @default_uri_parser ||= begin
283
- require 'uri'
284
- Kernel.method(:URI)
285
- end
82
+ @default_uri_parser ||= Kernel.method(:URI)
286
83
  end
287
84
 
288
85
  def default_uri_parser=(parser)
289
86
  @default_uri_parser = if parser.respond_to?(:call) || parser.nil?
290
- parser
291
- else
292
- parser.method(:parse)
293
- end
87
+ parser
88
+ else
89
+ parser.method(:parse)
90
+ end
294
91
  end
295
92
 
296
- # Receives a String or URI and returns just the path with the query string sorted.
93
+ # Receives a String or URI and returns just
94
+ # the path with the query string sorted.
297
95
  def normalize_path(url)
298
96
  url = URI(url)
299
- (url.path.start_with?('/') ? url.path : '/' + url.path) +
300
- (url.query ? "?#{sort_query_params(url.query)}" : "")
97
+ (url.path.start_with?('/') ? url.path : "/#{url.path}") +
98
+ (url.query ? "?#{sort_query_params(url.query)}" : '')
301
99
  end
302
100
 
303
101
  # Recursive hash update
304
102
  def deep_merge!(target, hash)
305
103
  hash.each do |key, value|
306
- if Hash === value and Hash === target[key]
307
- target[key] = deep_merge(target[key], value)
308
- else
309
- target[key] = value
310
- end
104
+ target[key] = if value.is_a?(Hash) && (target[key].is_a?(Hash) || target[key].is_a?(Options))
105
+ deep_merge(target[key], value)
106
+ else
107
+ value
108
+ end
311
109
  end
312
110
  target
313
111
  end
@@ -317,8 +115,6 @@ module Faraday
317
115
  deep_merge!(source.dup, hash)
318
116
  end
319
117
 
320
- protected
321
-
322
118
  def sort_query_params(query)
323
119
  query.split('&').sort.join('&')
324
120
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ VERSION = '2.0.0'
5
+ end