faraday 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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