faraday 0.7.6 → 0.8.0.rc2

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.
@@ -1,16 +1,14 @@
1
1
  module Faraday
2
2
  class Middleware
3
- class << self
4
- attr_accessor :load_error, :supports_parallel_requests
5
- alias supports_parallel_requests? supports_parallel_requests
3
+ extend MiddlewareRegistry
6
4
 
7
- # valid parallel managers should respond to #run with no parameters.
8
- # otherwise, return a short wrapper around it.
9
- def setup_parallel_manager(options = {})
10
- nil
11
- end
5
+ class << self
6
+ attr_accessor :load_error
7
+ private :load_error=
12
8
  end
13
9
 
10
+ self.load_error = nil
11
+
14
12
  # Executes a block which should try to require and reference dependent libraries
15
13
  def self.dependency(lib = nil)
16
14
  lib ? require(lib) : yield
@@ -18,8 +16,18 @@ module Faraday
18
16
  self.load_error = error
19
17
  end
20
18
 
19
+ def self.new(*)
20
+ raise "missing dependency for #{self}: #{load_error.message}" unless loaded?
21
+ super
22
+ end
23
+
21
24
  def self.loaded?
22
- !defined? @load_error or @load_error.nil?
25
+ load_error.nil?
26
+ end
27
+
28
+ def self.inherited(subclass)
29
+ super
30
+ subclass.send(:load_error=, self.load_error)
23
31
  end
24
32
 
25
33
  def initialize(app = nil)
@@ -9,20 +9,24 @@ module Faraday
9
9
  # req.body = 'abc'
10
10
  # end
11
11
  #
12
- class Request < Struct.new(:path, :params, :headers, :body, :options)
12
+ class Request < Struct.new(:method, :path, :params, :headers, :body, :options)
13
13
  extend AutoloadHelper
14
+ extend MiddlewareRegistry
14
15
 
15
16
  autoload_all 'faraday/request',
16
- :JSON => 'json',
17
17
  :UrlEncoded => 'url_encoded',
18
- :Multipart => 'multipart'
18
+ :Multipart => 'multipart',
19
+ :Retry => 'retry',
20
+ :Timeout => 'timeout',
21
+ :BasicAuthentication => 'basic_authentication',
22
+ :TokenAuthentication => 'token_authentication'
19
23
 
20
- register_lookup_modules \
21
- :json => :JSON,
24
+ register_middleware \
22
25
  :url_encoded => :UrlEncoded,
23
- :multipart => :Multipart
24
-
25
- attr_reader :method
26
+ :multipart => :Multipart,
27
+ :retry => :Retry,
28
+ :basic_auth => :BasicAuthentication,
29
+ :token_auth => :TokenAuthentication
26
30
 
27
31
  def self.create(request_method)
28
32
  new(request_method).tap do |request|
@@ -30,16 +34,32 @@ module Faraday
30
34
  end
31
35
  end
32
36
 
33
- def initialize(request_method)
34
- @method = request_method
35
- self.params = {}
36
- self.headers = {}
37
- self.options = {}
37
+ # Public: Replace params, preserving the existing hash type
38
+ def params=(hash)
39
+ if params then params.replace hash
40
+ else super
41
+ end
38
42
  end
39
43
 
40
- def url(path, params = {})
41
- self.path = path
42
- self.params = params
44
+ # Public: Replace request headers, preserving the existing hash type
45
+ def headers=(hash)
46
+ if headers then headers.replace hash
47
+ else super
48
+ end
49
+ end
50
+
51
+ def url(path, params = nil)
52
+ if path.respond_to? :query
53
+ if query = path.query
54
+ path = path.dup
55
+ path.query = nil
56
+ end
57
+ else
58
+ path, query = path.split('?', 2)
59
+ end
60
+ self.path = path
61
+ self.params.merge_query query
62
+ self.params.update(params) if params
43
63
  end
44
64
 
45
65
  def [](key)
@@ -53,7 +73,7 @@ module Faraday
53
73
  # ENV Keys
54
74
  # :method - a symbolized request method (:get, :post)
55
75
  # :body - the request body that will eventually be converted to a string.
56
- # :url - Addressable::URI instance of the URI for the current request.
76
+ # :url - URI instance for the current request.
57
77
  # :status - HTTP response status code
58
78
  # :request_headers - hash of HTTP Headers to be sent to the server
59
79
  # :response_headers - Hash of HTTP headers from the server
@@ -67,17 +87,12 @@ module Faraday
67
87
  # :password - Proxy server password
68
88
  # :ssl - Hash of options for configuring SSL requests.
69
89
  def to_env(connection)
70
- env_params = connection.params.merge(params)
71
- env_headers = connection.headers.merge(headers)
72
- request_options = Utils.deep_merge(connection.options, options)
73
- Utils.deep_merge!(request_options, :proxy => connection.proxy)
74
-
75
90
  { :method => method,
76
91
  :body => body,
77
- :url => connection.build_url(path, env_params),
78
- :request_headers => env_headers,
92
+ :url => connection.build_exclusive_url(path, params),
93
+ :request_headers => headers,
79
94
  :parallel_manager => connection.parallel_manager,
80
- :request => request_options,
95
+ :request => options,
81
96
  :ssl => connection.ssl}
82
97
  end
83
98
  end
@@ -0,0 +1,17 @@
1
+ require 'base64'
2
+
3
+ module Faraday
4
+ class Request::BasicAuthentication < Faraday::Middleware
5
+ def initialize(app, login, pass)
6
+ super(app)
7
+ @header_value = "Basic #{Base64.encode64([login, pass].join(':')).gsub("\n", '')}"
8
+ end
9
+
10
+ def call(env)
11
+ unless env[:request_headers]['Authorization']
12
+ env[:request_headers]['Authorization'] = @header_value
13
+ end
14
+ @app.call(env)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,21 @@
1
+ module Faraday
2
+ class Request::Retry < Faraday::Middleware
3
+ def initialize(app, retries = 2)
4
+ @retries = retries
5
+ super(app)
6
+ end
7
+
8
+ def call(env)
9
+ retries = @retries
10
+ begin
11
+ @app.call(env)
12
+ rescue StandardError, Timeout::Error
13
+ if retries > 0
14
+ retries -= 1
15
+ retry
16
+ end
17
+ raise
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module Faraday
2
+ class Request::TokenAuthentication < Faraday::Middleware
3
+ def initialize(app, token, options={})
4
+ super(app)
5
+
6
+ values = ["token=#{token.to_s.inspect}"]
7
+ options.each do |key, value|
8
+ values << "#{key}=#{value.to_s.inspect}"
9
+ end
10
+ comma = ",\n#{' ' * ('Authorization: Token '.size)}"
11
+ @header_value = "Token #{values * comma}"
12
+ end
13
+
14
+ def call(env)
15
+ unless env[:request_headers]['Authorization']
16
+ env[:request_headers]['Authorization'] = @header_value
17
+ end
18
+ @app.call(env)
19
+ end
20
+ end
21
+ end
@@ -21,12 +21,13 @@ module Faraday
21
21
 
22
22
  extend Forwardable
23
23
  extend AutoloadHelper
24
+ extend MiddlewareRegistry
24
25
 
25
26
  autoload_all 'faraday/response',
26
27
  :RaiseError => 'raise_error',
27
28
  :Logger => 'logger'
28
29
 
29
- register_lookup_modules \
30
+ register_middleware \
30
31
  :raise_error => :RaiseError,
31
32
  :logger => :Logger
32
33
 
@@ -1,13 +1,17 @@
1
- require 'rack/utils'
1
+ require 'cgi'
2
2
 
3
3
  module Faraday
4
4
  module Utils
5
- include Rack::Utils
6
-
7
- extend Rack::Utils
8
5
  extend self
9
6
 
10
- class Headers < HeaderHash
7
+ # Adapted from Rack::Utils::HeaderHash
8
+ class Headers < ::Hash
9
+ def initialize(hash={})
10
+ super()
11
+ @names = {}
12
+ self.update hash
13
+ end
14
+
11
15
  # symbol -> string mapper + cache
12
16
  KeyMap = Hash.new do |map, key|
13
17
  map[key] = if key.respond_to?(:to_str) then key
@@ -20,17 +24,53 @@ module Faraday
20
24
  KeyMap[:etag] = "ETag"
21
25
 
22
26
  def [](k)
23
- super(KeyMap[k])
27
+ k = KeyMap[k]
28
+ super(k) || super(@names[k.downcase])
24
29
  end
25
30
 
26
31
  def []=(k, v)
32
+ k = KeyMap[k]
33
+ k = (@names[k.downcase] ||= k)
27
34
  # join multiple values with a comma
28
35
  v = v.to_ary.join(', ') if v.respond_to? :to_ary
29
- super(KeyMap[k], v)
36
+ super k, v
37
+ end
38
+
39
+ def delete(k)
40
+ k = KeyMap[k]
41
+ if k = @names[k.downcase]
42
+ @names.delete k.downcase
43
+ super k
44
+ end
30
45
  end
31
46
 
47
+ def include?(k)
48
+ @names.include? k.downcase
49
+ end
50
+
51
+ alias_method :has_key?, :include?
52
+ alias_method :member?, :include?
53
+ alias_method :key?, :include?
54
+
55
+ def merge!(other)
56
+ other.each { |k, v| self[k] = v }
57
+ self
58
+ end
32
59
  alias_method :update, :merge!
33
60
 
61
+ def merge(other)
62
+ hash = dup
63
+ hash.merge! other
64
+ end
65
+
66
+ def replace(other)
67
+ clear
68
+ self.update other
69
+ self
70
+ end
71
+
72
+ def to_hash() ::Hash.new.update(self) end
73
+
34
74
  def parse(header_string)
35
75
  return unless header_string && !header_string.empty?
36
76
  header_string.split(/\r\n/).
@@ -92,7 +132,7 @@ module Faraday
92
132
  end
93
133
 
94
134
  def to_query
95
- Utils.build_query(self)
135
+ Utils.build_nested_query(self)
96
136
  end
97
137
 
98
138
  private
@@ -102,10 +142,18 @@ module Faraday
102
142
  end
103
143
  end
104
144
 
105
- # Make Rack::Utils methods public.
106
- public :build_query, :parse_query
145
+ # Copied from Rack
146
+ def build_query(params)
147
+ params.map { |k, v|
148
+ if v.class == Array
149
+ build_query(v.map { |x| [k, x] })
150
+ else
151
+ v.nil? ? escape(k) : "#{escape(k)}=#{escape(v)}"
152
+ end
153
+ }.join("&")
154
+ end
107
155
 
108
- # Override Rack's version since it doesn't handle non-String values
156
+ # Rack's version modified to handle non-String values
109
157
  def build_nested_query(value, prefix = nil)
110
158
  case value
111
159
  when Array
@@ -122,14 +170,70 @@ module Faraday
122
170
  end
123
171
  end
124
172
 
125
- # Be sure to URI escape '+' symbols to %2B. Otherwise, they get interpreted
126
- # as spaces.
127
- def escape(s)
128
- s = s.to_s
129
- s = s.dup.force_encoding('binary') if s.respond_to? :force_encoding
130
- s.gsub(/([^a-zA-Z0-9_.-]+)/n) do |match|
131
- '%' << match.unpack('H2'*bytesize(match)).join('%').tap { |c| c.upcase! }
173
+ def escape(s) CGI.escape s.to_s end
174
+
175
+ def unescape(s) CGI.unescape s.to_s end
176
+
177
+ DEFAULT_SEP = /[&;] */n
178
+
179
+ # Adapted from Rack
180
+ def parse_query(qs)
181
+ params = {}
182
+
183
+ (qs || '').split(DEFAULT_SEP).each do |p|
184
+ k, v = p.split('=', 2).map { |x| unescape(x) }
185
+
186
+ if cur = params[k]
187
+ if cur.class == Array then params[k] << v
188
+ else params[k] = [cur, v]
189
+ end
190
+ else
191
+ params[k] = v
192
+ end
193
+ end
194
+ params
195
+ end
196
+
197
+ def parse_nested_query(qs)
198
+ params = {}
199
+
200
+ (qs || '').split(DEFAULT_SEP).each do |p|
201
+ k, v = p.split('=', 2).map { |s| unescape(s) }
202
+ normalize_params(params, k, v)
132
203
  end
204
+ params
205
+ end
206
+
207
+ # Stolen from Rack
208
+ def normalize_params(params, name, v = nil)
209
+ name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
210
+ k = $1 || ''
211
+ after = $' || ''
212
+
213
+ return if k.empty?
214
+
215
+ if after == ""
216
+ params[k] = v
217
+ elsif after == "[]"
218
+ params[k] ||= []
219
+ raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
220
+ params[k] << v
221
+ elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
222
+ child_key = $1
223
+ params[k] ||= []
224
+ raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
225
+ if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key)
226
+ normalize_params(params[k].last, child_key, v)
227
+ else
228
+ params[k] << normalize_params({}, child_key, v)
229
+ end
230
+ else
231
+ params[k] ||= {}
232
+ raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash)
233
+ params[k] = normalize_params(params[k], after, v)
234
+ end
235
+
236
+ return params
133
237
  end
134
238
 
135
239
  # Receives a URL and returns just the path with the query string sorted.
@@ -6,10 +6,10 @@ else
6
6
  module Adapters
7
7
  class LiveTest < Faraday::TestCase
8
8
  adapters = if ENV['ADAPTER']
9
- ENV['ADAPTER'].split(':').map { |name| Faraday::Adapter.lookup_module name.to_sym }
9
+ ENV['ADAPTER'].split(':').map { |name| Faraday::Adapter.lookup_middleware name.to_sym }
10
10
  else
11
11
  loaded_adapters = Faraday::Adapter.all_loaded_constants
12
- loaded_adapters -= [Faraday::Adapter::ActionDispatch]
12
+ loaded_adapters -= [Faraday::Adapter::Test, Faraday::Adapter::ActionDispatch]
13
13
  # https://github.com/geemus/excon/issues/98
14
14
  loaded_adapters -= [Faraday::Adapter::Excon] if defined? RUBY_ENGINE and "rbx" == RUBY_ENGINE
15
15
  loaded_adapters << :default
@@ -108,18 +108,16 @@ else
108
108
  end
109
109
 
110
110
  # https://github.com/toland/patron/issues/34
111
- unless %w[Faraday::Adapter::Patron Faraday::Adapter::EMSynchrony].include? adapter.to_s
111
+ unless %w[Faraday::Adapter::Patron].include? adapter.to_s
112
112
  define_method "test_#{adapter}_PATCH_send_url_encoded_params" do
113
113
  resp = create_connection(adapter).patch('echo_name', 'name' => 'zack')
114
114
  assert_equal %("zack"), resp.body
115
115
  end
116
116
  end
117
117
 
118
- unless %[Faraday::Adapter::EMSynchrony] == adapter.to_s
119
- define_method "test_#{adapter}_OPTIONS" do
120
- resp = create_connection(adapter).run_request(:options, '/options', nil, {})
121
- assert_equal "hi", resp.body
122
- end
118
+ define_method "test_#{adapter}_OPTIONS" do
119
+ resp = create_connection(adapter).run_request(:options, '/options', nil, {})
120
+ assert_equal "hi", resp.body
123
121
  end
124
122
 
125
123
  define_method "test_#{adapter}_HEAD_send_url_encoded_params" do
@@ -145,32 +143,38 @@ else
145
143
  assert_match(/deleted/, create_connection(adapter).delete('delete_with_json').body)
146
144
  end
147
145
 
148
- define_method "test_#{adapter}_async_requests_clear_parallel_manager_after_running_a_single_request" do
149
- connection = create_connection(adapter)
150
- assert !connection.in_parallel?
151
- connection.get('hello_world')
152
- assert !connection.in_parallel?
153
- assert_equal 'hello world', connection.get('hello_world').body
154
- end
155
-
156
- define_method "test_#{adapter}_async_requests_uses_parallel_manager_to_run_multiple_json_requests" do
157
- resp1, resp2 = nil, nil
146
+ if :default != adapter and adapter.supports_parallel?
147
+ define_method "test_#{adapter}_in_parallel" do
148
+ resp1, resp2 = nil, nil
158
149
 
159
- connection = create_connection(adapter)
160
- adapter = real_adapter_for(adapter)
150
+ connection = create_connection(adapter)
151
+ klass = real_adapter_for(adapter)
161
152
 
162
- connection.in_parallel(adapter.setup_parallel_manager) do
163
- resp1 = connection.get('json')
164
- resp2 = connection.get('json')
165
- if adapter.supports_parallel_requests?
153
+ connection.in_parallel do
154
+ resp1 = connection.get('echo?a=1')
155
+ resp2 = connection.get('echo?b=2')
166
156
  assert connection.in_parallel?
167
157
  assert_nil resp1.body
168
158
  assert_nil resp2.body
169
159
  end
160
+ assert !connection.in_parallel?
161
+ assert_equal 'get ?{"a"=>"1"}', resp1.body
162
+ assert_equal 'get ?{"b"=>"2"}', resp2.body
163
+ end
164
+ else
165
+ define_method "test_#{adapter}_no_parallel_support" do
166
+ connection = create_connection(adapter)
167
+ response = nil
168
+
169
+ err = capture_warnings do
170
+ connection.in_parallel do
171
+ response = connection.get('echo').body
172
+ end
173
+ end
174
+ assert response
175
+ assert_match "no parallel-capable adapter on Faraday stack", err
176
+ assert_match __FILE__, err
170
177
  end
171
- assert !connection.in_parallel?
172
- assert_equal '[1,2,3]', resp1.body
173
- assert_equal '[1,2,3]', resp2.body
174
178
  end
175
179
 
176
180
  if adapter.to_s == "Faraday::Adapter::EMSynchrony"
@@ -187,9 +191,10 @@ else
187
191
  end
188
192
  end
189
193
 
190
- if %w[Faraday::Adapter::Patron Faraday::Adapter::NetHttp].include?(adapter.to_s)
194
+ # https://github.com/eventmachine/eventmachine/pull/289
195
+ unless %w[Faraday::Adapter::EMHttp Faraday::Adapter::EMSynchrony Faraday::Adapter::Excon].include?(adapter.to_s)
191
196
  define_method "test_#{adapter}_timeout" do
192
- conn = create_connection(adapter, :request => {:timeout => 1, :read_timeout => 1})
197
+ conn = create_connection(adapter, :request => {:timeout => 1, :open_timeout => 1})
193
198
  assert_raise Faraday::Error::TimeoutError do
194
199
  conn.get '/slow'
195
200
  end
@@ -217,7 +222,7 @@ else
217
222
 
218
223
  def real_adapter_for(adapter)
219
224
  if adapter == :default
220
- Faraday::Adapter.lookup_module(Faraday.default_adapter)
225
+ Faraday::Adapter.lookup_middleware(Faraday.default_adapter)
221
226
  else
222
227
  adapter
223
228
  end