faraday 1.0.0 → 1.1.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.md +1 -1
  3. data/README.md +3 -4
  4. data/examples/client_spec.rb +1 -1
  5. data/lib/faraday/adapter/em_http.rb +3 -2
  6. data/lib/faraday/adapter/excon.rb +2 -2
  7. data/lib/faraday/adapter/httpclient.rb +2 -1
  8. data/lib/faraday/adapter/net_http.rb +18 -8
  9. data/lib/faraday/adapter/typhoeus.rb +1 -1
  10. data/lib/faraday/adapter.rb +1 -0
  11. data/lib/faraday/adapter_registry.rb +3 -1
  12. data/lib/faraday/autoload.rb +1 -1
  13. data/lib/faraday/connection.rb +3 -3
  14. data/lib/faraday/dependency_loader.rb +3 -1
  15. data/lib/faraday/encoders/flat_params_encoder.rb +9 -2
  16. data/lib/faraday/encoders/nested_params_encoder.rb +7 -2
  17. data/lib/faraday/error.rb +8 -0
  18. data/lib/faraday/options.rb +4 -8
  19. data/lib/faraday/rack_builder.rb +13 -12
  20. data/lib/faraday/request/authorization.rb +3 -1
  21. data/lib/faraday/request/multipart.rb +10 -3
  22. data/lib/faraday/request/url_encoded.rb +3 -1
  23. data/lib/faraday/request.rb +20 -10
  24. data/lib/faraday/response/raise_error.rb +12 -1
  25. data/lib/faraday/response.rb +4 -1
  26. data/lib/faraday/utils/headers.rb +2 -2
  27. data/lib/faraday/utils.rb +11 -3
  28. data/lib/faraday.rb +2 -2
  29. data/spec/faraday/adapter/em_http_spec.rb +1 -1
  30. data/spec/faraday/adapter/em_synchrony_spec.rb +1 -1
  31. data/spec/faraday/adapter/patron_spec.rb +1 -1
  32. data/spec/faraday/adapter/test_spec.rb +260 -0
  33. data/spec/faraday/params_encoders/flat_spec.rb +8 -0
  34. data/spec/faraday/params_encoders/nested_spec.rb +8 -0
  35. data/spec/faraday/rack_builder_spec.rb +150 -1
  36. data/spec/faraday/request/authorization_spec.rb +2 -2
  37. data/spec/faraday/request/multipart_spec.rb +41 -13
  38. data/spec/faraday/request/url_encoded_spec.rb +13 -0
  39. data/spec/faraday/request_spec.rb +16 -5
  40. data/spec/faraday/response/middleware_spec.rb +16 -0
  41. data/spec/faraday/response/raise_error_spec.rb +33 -0
  42. data/spec/support/shared_examples/request_method.rb +3 -3
  43. metadata +21 -6
  44. data/UPGRADING.md +0 -55
@@ -4,7 +4,9 @@ module Faraday
4
4
  class Request
5
5
  # Middleware for supporting urlencoded requests.
6
6
  class UrlEncoded < Faraday::Middleware
7
- CONTENT_TYPE = 'Content-Type' unless defined? CONTENT_TYPE
7
+ unless defined?(::Faraday::Request::UrlEncoded::CONTENT_TYPE)
8
+ CONTENT_TYPE = 'Content-Type'
9
+ end
8
10
 
9
11
  class << self
10
12
  attr_accessor :mime_type
@@ -12,7 +12,7 @@ module Faraday
12
12
  # req.body = 'abc'
13
13
  # end
14
14
  #
15
- # @!attribute method
15
+ # @!attribute http_method
16
16
  # @return [Symbol] the HTTP method of the Request
17
17
  # @!attribute path
18
18
  # @return [URI, String] the path
@@ -26,7 +26,9 @@ module Faraday
26
26
  # @return [RequestOptions] options
27
27
  #
28
28
  # rubocop:disable Style/StructInheritance
29
- class Request < Struct.new(:method, :path, :params, :headers, :body, :options)
29
+ class Request < Struct.new(
30
+ :http_method, :path, :params, :headers, :body, :options
31
+ )
30
32
  # rubocop:enable Style/StructInheritance
31
33
 
32
34
  extend MiddlewareRegistry
@@ -56,6 +58,14 @@ module Faraday
56
58
  end
57
59
  end
58
60
 
61
+ def method
62
+ warn <<~TEXT
63
+ WARNING: `Faraday::Request##{__method__}` is deprecated; use `#http_method` instead. It will be removed in or after version 2.0.
64
+ `Faraday::Request##{__method__}` called from #{caller_locations(1..1).first}
65
+ TEXT
66
+ http_method
67
+ end
68
+
59
69
  # Replace params, preserving the existing hash type.
60
70
  #
61
71
  # @param hash [Hash] new params
@@ -116,7 +126,7 @@ module Faraday
116
126
  # @return [Hash] the hash ready to be serialized in Marshal.
117
127
  def marshal_dump
118
128
  {
119
- method: method,
129
+ http_method: http_method,
120
130
  body: body,
121
131
  headers: headers,
122
132
  path: path,
@@ -129,17 +139,17 @@ module Faraday
129
139
  # Restores the instance variables according to the +serialised+.
130
140
  # @param serialised [Hash] the serialised object.
131
141
  def marshal_load(serialised)
132
- self.method = serialised[:method]
133
- self.body = serialised[:body]
134
- self.headers = serialised[:headers]
135
- self.path = serialised[:path]
136
- self.params = serialised[:params]
137
- self.options = serialised[:options]
142
+ self.http_method = serialised[:http_method]
143
+ self.body = serialised[:body]
144
+ self.headers = serialised[:headers]
145
+ self.path = serialised[:path]
146
+ self.params = serialised[:params]
147
+ self.options = serialised[:options]
138
148
  end
139
149
 
140
150
  # @return [Env] the Env for this Request
141
151
  def to_env(connection)
142
- Env.new(method, body, connection.build_exclusive_url(path, params),
152
+ Env.new(http_method, body, connection.build_exclusive_url(path, params),
143
153
  options, headers, connection.ssl, connection.parallel_manager)
144
154
  end
145
155
  end
@@ -38,7 +38,18 @@ module Faraday
38
38
  end
39
39
 
40
40
  def response_values(env)
41
- { status: env.status, headers: env.response_headers, body: env.body }
41
+ {
42
+ status: env.status,
43
+ headers: env.response_headers,
44
+ body: env.body,
45
+ request: {
46
+ method: env.method,
47
+ url_path: env.url.path,
48
+ params: env.params,
49
+ headers: env.request_headers,
50
+ body: env.request_body
51
+ }
52
+ }
42
53
  end
43
54
  end
44
55
  end
@@ -15,8 +15,11 @@ module Faraday
15
15
 
16
16
  # Override this to modify the environment after the response has finished.
17
17
  # Calls the `parse` method if defined
18
+ # `parse` method can be defined as private, public and protected
18
19
  def on_complete(env)
19
- 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)
20
23
  end
21
24
  end
22
25
 
@@ -105,7 +105,7 @@ module Faraday
105
105
  end
106
106
 
107
107
  def to_hash
108
- ::Hash.new.update(self)
108
+ {}.update(self)
109
109
  end
110
110
 
111
111
  def parse(header_string)
@@ -114,7 +114,7 @@ module Faraday
114
114
  headers = header_string.split(/\r\n/)
115
115
 
116
116
  # Find the last set of response headers.
117
- start_index = headers.rindex { |x| x.match(%r{^HTTP/}) } || 0
117
+ start_index = headers.rindex { |x| x.start_with?('HTTP/') } || 0
118
118
  last_response = headers.slice(start_index, headers.size)
119
119
 
120
120
  last_response
data/lib/faraday/utils.rb CHANGED
@@ -16,12 +16,20 @@ module Faraday
16
16
  NestedParamsEncoder.encode(params)
17
17
  end
18
18
 
19
+ def default_space_encoding
20
+ @default_space_encoding ||= '+'
21
+ end
22
+
23
+ class << self
24
+ attr_writer :default_space_encoding
25
+ end
26
+
19
27
  ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/.freeze
20
28
 
21
29
  def escape(str)
22
30
  str.to_s.gsub(ESCAPE_RE) do |match|
23
- '%' + match.unpack('H2' * match.bytesize).join('%').upcase
24
- end.tr(' ', '+')
31
+ "%#{match.unpack('H2' * match.bytesize).join('%').upcase}"
32
+ end.gsub(' ', default_space_encoding)
25
33
  end
26
34
 
27
35
  def unescape(str)
@@ -81,7 +89,7 @@ module Faraday
81
89
  # the path with the query string sorted.
82
90
  def normalize_path(url)
83
91
  url = URI(url)
84
- (url.path.start_with?('/') ? url.path : '/' + url.path) +
92
+ (url.path.start_with?('/') ? url.path : "/#{url.path}") +
85
93
  (url.query ? "?#{sort_query_params(url.query)}" : '')
86
94
  end
87
95
 
data/lib/faraday.rb CHANGED
@@ -19,7 +19,7 @@ require 'faraday/dependency_loader'
19
19
  # conn.get '/'
20
20
  #
21
21
  module Faraday
22
- VERSION = '1.0.0'
22
+ VERSION = '1.1.0'
23
23
  METHODS_WITH_QUERY = %w[get head delete trace].freeze
24
24
  METHODS_WITH_BODY = %w[post put patch].freeze
25
25
 
@@ -153,7 +153,7 @@ module Faraday
153
153
  @default_connection_options = ConnectionOptions.from(options)
154
154
  end
155
155
 
156
- unless const_defined? :Timer
156
+ unless defined?(::Faraday::Timer)
157
157
  require 'timeout'
158
158
  Timer = Timeout
159
159
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- RSpec.describe Faraday::Adapter::EMHttp do
3
+ RSpec.describe Faraday::Adapter::EMHttp, unless: defined?(JRUBY_VERSION) do
4
4
  features :request_body_on_query_methods, :reason_phrase_parse, :trace_method,
5
5
  :skip_response_body_on_head, :parallel, :local_socket_binding
6
6
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- RSpec.describe Faraday::Adapter::EMSynchrony do
3
+ RSpec.describe Faraday::Adapter::EMSynchrony, unless: defined?(JRUBY_VERSION) do
4
4
  features :request_body_on_query_methods, :reason_phrase_parse,
5
5
  :skip_response_body_on_head, :parallel, :local_socket_binding
6
6
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- RSpec.describe Faraday::Adapter::Patron do
3
+ RSpec.describe Faraday::Adapter::Patron, unless: defined?(JRUBY_VERSION) do
4
4
  features :reason_phrase_parse
5
5
 
6
6
  it_behaves_like 'an adapter'
@@ -0,0 +1,260 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::Adapter::Test do
4
+ let(:stubs) do
5
+ described_class::Stubs.new do |stub|
6
+ stub.get('http://domain.test/hello') do
7
+ [200, { 'Content-Type' => 'text/html' }, 'domain: hello']
8
+ end
9
+
10
+ stub.get('http://wrong.test/hello') do
11
+ [200, { 'Content-Type' => 'text/html' }, 'wrong: hello']
12
+ end
13
+
14
+ stub.get('http://wrong.test/bait') do
15
+ [404, { 'Content-Type' => 'text/html' }]
16
+ end
17
+
18
+ stub.get('/hello') do
19
+ [200, { 'Content-Type' => 'text/html' }, 'hello']
20
+ end
21
+
22
+ stub.get('/method-echo') do |env|
23
+ [200, { 'Content-Type' => 'text/html' }, env[:method].to_s]
24
+ end
25
+
26
+ stub.get(%r{\A/resources/\d+(?:\?|\z)}) do
27
+ [200, { 'Content-Type' => 'text/html' }, 'show']
28
+ end
29
+
30
+ stub.get(%r{\A/resources/(specified)\z}) do |_env, meta|
31
+ [200, { 'Content-Type' => 'text/html' }, "show #{meta[:match_data][1]}"]
32
+ end
33
+ end
34
+ end
35
+
36
+ let(:connection) do
37
+ Faraday.new do |builder|
38
+ builder.adapter :test, stubs
39
+ end
40
+ end
41
+
42
+ let(:response) { connection.get('/hello') }
43
+
44
+ context 'with simple path sets status' do
45
+ subject { response.status }
46
+
47
+ it { is_expected.to eq 200 }
48
+ end
49
+
50
+ context 'with simple path sets headers' do
51
+ subject { response.headers['Content-Type'] }
52
+
53
+ it { is_expected.to eq 'text/html' }
54
+ end
55
+
56
+ context 'with simple path sets body' do
57
+ subject { response.body }
58
+
59
+ it { is_expected.to eq 'hello' }
60
+ end
61
+
62
+ context 'with host points to the right stub' do
63
+ subject { connection.get('http://domain.test/hello').body }
64
+
65
+ it { is_expected.to eq 'domain: hello' }
66
+ end
67
+
68
+ describe 'can be called several times' do
69
+ subject { connection.get('/hello').body }
70
+
71
+ it { is_expected.to eq 'hello' }
72
+ end
73
+
74
+ describe 'can handle regular expression path' do
75
+ subject { connection.get('/resources/1').body }
76
+
77
+ it { is_expected.to eq 'show' }
78
+ end
79
+
80
+ describe 'can handle single parameter block' do
81
+ subject { connection.get('/method-echo').body }
82
+
83
+ it { is_expected.to eq 'get' }
84
+ end
85
+
86
+ describe 'can handle regular expression path with captured result' do
87
+ subject { connection.get('/resources/specified').body }
88
+
89
+ it { is_expected.to eq 'show specified' }
90
+ end
91
+
92
+ context 'with get params' do
93
+ subject { connection.get('/param?a=1').body }
94
+
95
+ before do
96
+ stubs.get('/param?a=1') { [200, {}, 'a'] }
97
+ end
98
+
99
+ it { is_expected.to eq 'a' }
100
+ end
101
+
102
+ describe 'ignoring unspecified get params' do
103
+ before do
104
+ stubs.get('/optional?a=1') { [200, {}, 'a'] }
105
+ end
106
+
107
+ context 'with multiple params' do
108
+ subject { connection.get('/optional?a=1&b=1').body }
109
+
110
+ it { is_expected.to eq 'a' }
111
+ end
112
+
113
+ context 'with single param' do
114
+ subject { connection.get('/optional?a=1').body }
115
+
116
+ it { is_expected.to eq 'a' }
117
+ end
118
+
119
+ context 'without params' do
120
+ subject(:request) { connection.get('/optional') }
121
+
122
+ it do
123
+ expect { request }.to raise_error(
124
+ Faraday::Adapter::Test::Stubs::NotFound
125
+ )
126
+ end
127
+ end
128
+ end
129
+
130
+ context 'with http headers' do
131
+ before do
132
+ stubs.get('/yo', 'X-HELLO' => 'hello') { [200, {}, 'a'] }
133
+ stubs.get('/yo') { [200, {}, 'b'] }
134
+ end
135
+
136
+ context 'with header' do
137
+ subject do
138
+ connection.get('/yo') { |env| env.headers['X-HELLO'] = 'hello' }.body
139
+ end
140
+
141
+ it { is_expected.to eq 'a' }
142
+ end
143
+
144
+ context 'without header' do
145
+ subject do
146
+ connection.get('/yo').body
147
+ end
148
+
149
+ it { is_expected.to eq 'b' }
150
+ end
151
+ end
152
+
153
+ describe 'different outcomes for the same request' do
154
+ def make_request
155
+ connection.get('/foo')
156
+ end
157
+
158
+ subject(:request) { make_request.body }
159
+
160
+ before do
161
+ stubs.get('/foo') { [200, { 'Content-Type' => 'text/html' }, 'hello'] }
162
+ stubs.get('/foo') { [200, { 'Content-Type' => 'text/html' }, 'world'] }
163
+ end
164
+
165
+ context 'the first request' do
166
+ it { is_expected.to eq 'hello' }
167
+ end
168
+
169
+ context 'the second request' do
170
+ before do
171
+ make_request
172
+ end
173
+
174
+ it { is_expected.to eq 'world' }
175
+ end
176
+ end
177
+
178
+ describe 'yielding env to stubs' do
179
+ subject { connection.get('http://foo.com/foo?a=1').body }
180
+
181
+ before do
182
+ stubs.get '/foo' do |env|
183
+ expect(env[:url].path).to eq '/foo'
184
+ expect(env[:url].host).to eq 'foo.com'
185
+ expect(env[:params]['a']).to eq '1'
186
+ expect(env[:request_headers]['Accept']).to eq 'text/plain'
187
+ [200, {}, 'a']
188
+ end
189
+
190
+ connection.headers['Accept'] = 'text/plain'
191
+ end
192
+
193
+ it { is_expected.to eq 'a' }
194
+ end
195
+
196
+ describe 'params parsing' do
197
+ subject { connection.get('http://foo.com/foo?a[b]=1').body }
198
+
199
+ context 'with default encoder' do
200
+ before do
201
+ stubs.get '/foo' do |env|
202
+ expect(env[:params]['a']['b']).to eq '1'
203
+ [200, {}, 'a']
204
+ end
205
+ end
206
+
207
+ it { is_expected.to eq 'a' }
208
+ end
209
+
210
+ context 'with nested encoder' do
211
+ before do
212
+ stubs.get '/foo' do |env|
213
+ expect(env[:params]['a']['b']).to eq '1'
214
+ [200, {}, 'a']
215
+ end
216
+
217
+ connection.options.params_encoder = Faraday::NestedParamsEncoder
218
+ end
219
+
220
+ it { is_expected.to eq 'a' }
221
+ end
222
+
223
+ context 'with flat encoder' do
224
+ before do
225
+ stubs.get '/foo' do |env|
226
+ expect(env[:params]['a[b]']).to eq '1'
227
+ [200, {}, 'a']
228
+ end
229
+
230
+ connection.options.params_encoder = Faraday::FlatParamsEncoder
231
+ end
232
+
233
+ it { is_expected.to eq 'a' }
234
+ end
235
+ end
236
+
237
+ describe 'raising an error if no stub was found' do
238
+ describe 'for request' do
239
+ subject(:request) { connection.get('/invalid') { [200, {}, []] } }
240
+
241
+ it { expect { request }.to raise_error described_class::Stubs::NotFound }
242
+ end
243
+
244
+ describe 'for specified host' do
245
+ subject(:request) { connection.get('http://domain.test/bait') }
246
+
247
+ it { expect { request }.to raise_error described_class::Stubs::NotFound }
248
+ end
249
+
250
+ describe 'for request without specified header' do
251
+ subject(:request) { connection.get('/yo') }
252
+
253
+ before do
254
+ stubs.get('/yo', 'X-HELLO' => 'hello') { [200, {}, 'a'] }
255
+ end
256
+
257
+ it { expect { request }.to raise_error described_class::Stubs::NotFound }
258
+ end
259
+ end
260
+ end