faraday 0.8.11 → 0.9.0.rc1

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 (70) hide show
  1. data/.document +6 -0
  2. data/CONTRIBUTING.md +36 -0
  3. data/Gemfile +7 -6
  4. data/LICENSE.md +1 -1
  5. data/README.md +38 -51
  6. data/Rakefile +2 -18
  7. data/faraday.gemspec +34 -0
  8. data/lib/faraday.rb +181 -67
  9. data/lib/faraday/adapter.rb +19 -34
  10. data/lib/faraday/adapter/em_http.rb +24 -10
  11. data/lib/faraday/adapter/em_synchrony.rb +1 -15
  12. data/lib/faraday/adapter/excon.rb +6 -12
  13. data/lib/faraday/adapter/httpclient.rb +92 -0
  14. data/lib/faraday/adapter/net_http.rb +2 -3
  15. data/lib/faraday/adapter/net_http_persistent.rb +3 -15
  16. data/lib/faraday/adapter/patron.rb +13 -21
  17. data/lib/faraday/adapter/rack.rb +0 -2
  18. data/lib/faraday/adapter/test.rb +35 -36
  19. data/lib/faraday/adapter/typhoeus.rb +10 -12
  20. data/lib/faraday/autoload.rb +87 -0
  21. data/lib/faraday/connection.rb +196 -99
  22. data/lib/faraday/error.rb +33 -33
  23. data/lib/faraday/options.rb +215 -0
  24. data/lib/faraday/parameters.rb +193 -0
  25. data/lib/faraday/{builder.rb → rack_builder.rb} +78 -21
  26. data/lib/faraday/request.rb +12 -25
  27. data/lib/faraday/request/authorization.rb +3 -3
  28. data/lib/faraday/request/basic_authentication.rb +1 -1
  29. data/lib/faraday/request/instrumentation.rb +38 -0
  30. data/lib/faraday/request/multipart.rb +10 -9
  31. data/lib/faraday/request/retry.rb +70 -6
  32. data/lib/faraday/request/token_authentication.rb +2 -2
  33. data/lib/faraday/request/url_encoded.rb +7 -6
  34. data/lib/faraday/response.rb +17 -22
  35. data/lib/faraday/response/logger.rb +4 -4
  36. data/lib/faraday/response/raise_error.rb +4 -5
  37. data/lib/faraday/utils.rb +54 -67
  38. data/script/console +7 -0
  39. data/script/release +6 -3
  40. data/script/server +3 -1
  41. data/script/test +7 -33
  42. data/test/adapters/em_http_test.rb +6 -1
  43. data/test/adapters/em_synchrony_test.rb +7 -1
  44. data/test/adapters/excon_test.rb +0 -7
  45. data/test/adapters/httpclient_test.rb +16 -0
  46. data/test/adapters/integration.rb +8 -39
  47. data/test/adapters/logger_test.rb +1 -1
  48. data/test/adapters/net_http_test.rb +0 -31
  49. data/test/adapters/patron_test.rb +1 -1
  50. data/test/adapters/rack_test.rb +0 -5
  51. data/test/adapters/test_middleware_test.rb +19 -4
  52. data/test/adapters/typhoeus_test.rb +20 -3
  53. data/test/authentication_middleware_test.rb +7 -7
  54. data/test/connection_test.rb +52 -75
  55. data/test/env_test.rb +33 -24
  56. data/test/helper.rb +15 -13
  57. data/test/live_server.rb +10 -4
  58. data/test/middleware/instrumentation_test.rb +75 -0
  59. data/test/middleware/retry_test.rb +44 -38
  60. data/test/middleware_stack_test.rb +12 -11
  61. data/test/options_test.rb +126 -0
  62. data/test/request_middleware_test.rb +17 -7
  63. data/test/response_middleware_test.rb +2 -4
  64. data/test/strawberry.rb +2 -0
  65. metadata +82 -28
  66. checksums.yaml +0 -7
  67. data/script/proxy-server +0 -41
  68. data/test/multibyte.txt +0 -1
  69. data/test/parameters_test.rb +0 -24
  70. data/test/utils_test.rb +0 -30
@@ -1,45 +1,45 @@
1
1
  module Faraday
2
- module Error
3
- class ClientError < StandardError
4
- attr_reader :response
2
+ class Error < StandardError; end
3
+ class MissingDependency < Error; end
5
4
 
6
- def initialize(ex, response = nil)
7
- @wrapped_exception = nil
8
- @response = response
5
+ class ClientError < Error
6
+ attr_reader :response
9
7
 
10
- if ex.respond_to?(:backtrace)
11
- super(ex.message)
12
- @wrapped_exception = ex
13
- elsif ex.respond_to?(:each_key)
14
- super("the server responded with status #{ex[:status]}")
15
- @response = ex
16
- else
17
- super(ex.to_s)
18
- end
19
- end
8
+ def initialize(ex, response = nil)
9
+ @wrapped_exception = nil
10
+ @response = response
20
11
 
21
- def backtrace
22
- if @wrapped_exception
23
- @wrapped_exception.backtrace
24
- else
25
- super
26
- end
12
+ if ex.respond_to?(:backtrace)
13
+ super(ex.message)
14
+ @wrapped_exception = ex
15
+ elsif ex.respond_to?(:each_key)
16
+ super("the server responded with status #{ex[:status]}")
17
+ @response = ex
18
+ else
19
+ super(ex.to_s)
27
20
  end
21
+ end
28
22
 
29
- def inspect
30
- %(#<#{self.class}>)
23
+ def backtrace
24
+ if @wrapped_exception
25
+ @wrapped_exception.backtrace
26
+ else
27
+ super
31
28
  end
32
29
  end
33
30
 
34
- class ConnectionFailed < ClientError; end
35
- class ResourceNotFound < ClientError; end
36
- class ParsingError < ClientError; end
37
- class MissingDependency < StandardError; end
38
-
39
- class TimeoutError < ClientError
40
- def initialize(ex = nil)
41
- super(ex || "timeout")
42
- end
31
+ def inspect
32
+ %(#<#{self.class}>)
43
33
  end
44
34
  end
35
+
36
+ class ConnectionFailed < ClientError; end
37
+ class ResourceNotFound < ClientError; end
38
+ class ParsingError < ClientError; end
39
+ class TimeoutError < ClientError; end
40
+
41
+ [:MissingDependency, :ClientError, :ConnectionFailed, :ResourceNotFound,
42
+ :ParsingError, :TimeoutError].each do |const|
43
+ Error.const_set(const, Faraday.const_get(const))
44
+ end
45
45
  end
@@ -0,0 +1,215 @@
1
+ module Faraday
2
+ # Subclasses Struct with some special helpers for converting from a Hash to
3
+ # a Struct.
4
+ class Options < Struct
5
+ # Public
6
+ def self.from(value)
7
+ value ? new.update(value) : new
8
+ end
9
+
10
+ # Public
11
+ def each(&block)
12
+ members.each do |key|
13
+ block.call key.to_sym, send(key)
14
+ end
15
+ end
16
+
17
+ # Public
18
+ def update(obj)
19
+ obj.each do |key, value|
20
+ next unless value
21
+ sub_options = self.class.options_for(key)
22
+ if sub_options && value
23
+ value = sub_options.from(value)
24
+ elsif Hash === value
25
+ hash = {}
26
+ value.each do |hash_key, hash_value|
27
+ hash[hash_key] = hash_value
28
+ end
29
+ value = hash
30
+ end
31
+
32
+ self.send("#{key}=", value)
33
+ end
34
+ self
35
+ end
36
+
37
+ # Public
38
+ def delete(key)
39
+ value = send(key)
40
+ send("#{key}=", nil)
41
+ value
42
+ end
43
+
44
+ # Public
45
+ def merge(value)
46
+ dup.update(value)
47
+ end
48
+
49
+ # Public
50
+ def fetch(key, default = nil)
51
+ send(key) || send("#{key}=", default ||
52
+ (block_given? ? Proc.new.call : nil))
53
+ end
54
+
55
+ # Public
56
+ def values_at(*keys)
57
+ keys.map { |key| send(key) }
58
+ end
59
+
60
+ # Public
61
+ def keys
62
+ members.reject { |m| send(m).nil? }
63
+ end
64
+
65
+ # Public
66
+ def to_hash
67
+ hash = {}
68
+ members.each do |key|
69
+ value = send(key)
70
+ hash[key.to_sym] = value if value
71
+ end
72
+ hash
73
+ end
74
+
75
+ # Internal
76
+ def inspect
77
+ values = []
78
+ members.each do |m|
79
+ value = send(m)
80
+ values << "#{m}=#{value.inspect}" if value
81
+ end
82
+ values = values.empty? ? ' (empty)' : (' ' << values.join(", "))
83
+
84
+ %(#<#{self.class}#{values}>)
85
+ end
86
+
87
+ # Internal
88
+ def self.options(mapping)
89
+ attribute_options.update(mapping)
90
+ end
91
+
92
+ # Internal
93
+ def self.options_for(key)
94
+ attribute_options[key]
95
+ end
96
+
97
+ # Internal
98
+ def self.attribute_options
99
+ @attribute_options ||= {}
100
+ end
101
+ end
102
+
103
+ class RequestOptions < Options.new(:params_encoder, :proxy, :bind,
104
+ :timeout, :open_timeout, :boundary,
105
+ :oauth)
106
+
107
+ def []=(key, value)
108
+ if key && key.to_sym == :proxy
109
+ super(key, value ? ProxyOptions.from(value) : nil)
110
+ else
111
+ super(key, value)
112
+ end
113
+ end
114
+ end
115
+
116
+ class SSLOptions < Options.new(:verify, :ca_file, :ca_path, :verify_mode,
117
+ :cert_store, :client_cert, :client_key, :verify_depth, :version)
118
+
119
+ def verify?
120
+ verify != false
121
+ end
122
+
123
+ def disable?
124
+ !verify?
125
+ end
126
+ end
127
+
128
+ class ProxyOptions < Options.new(:uri, :user, :password)
129
+ extend Forwardable
130
+ def_delegators :uri, :scheme, :scheme=, :host, :host=, :port, :port=, :path, :path=
131
+
132
+ def self.from(value)
133
+ case value
134
+ when String then value = {:uri => Utils.URI(value)}
135
+ when URI then value = {:uri => value}
136
+ when Hash, Options
137
+ if uri = value.delete(:uri)
138
+ value[:uri] = Utils.URI(uri)
139
+ end
140
+ end
141
+ super(value)
142
+ end
143
+
144
+ def user
145
+ self[:user] ||= Utils.unescape(uri.user)
146
+ end
147
+
148
+ def password
149
+ self[:password] ||= Utils.unescape(uri.password)
150
+ end
151
+ end
152
+
153
+ class ConnectionOptions < Options.new(:request, :proxy, :ssl, :builder, :url,
154
+ :parallel_manager, :params, :headers, :builder_class)
155
+
156
+ options :request => RequestOptions, :ssl => SSLOptions
157
+
158
+ def request
159
+ self[:request] ||= self.class.options_for(:request).new
160
+ end
161
+
162
+ def ssl
163
+ self[:ssl] ||= self.class.options_for(:ssl).new
164
+ end
165
+
166
+ def builder_class
167
+ self[:builder_class] ||= RackBuilder
168
+ end
169
+
170
+ def new_builder(block)
171
+ builder_class.new(&block)
172
+ end
173
+ end
174
+
175
+ class Env < Options.new(:method, :body, :url, :request, :request_headers,
176
+ :ssl, :parallel_manager, :params, :response, :response_headers, :status)
177
+
178
+ ContentLength = 'Content-Length'.freeze
179
+ StatusesWithoutBody = Set.new [204, 304]
180
+ SuccessfulStatuses = 200..299
181
+
182
+ # A Set of HTTP verbs that typically send a body. If no body is set for
183
+ # these requests, the Content-Length header is set to 0.
184
+ MethodsWithBodies = Set.new [:post, :put, :patch, :options]
185
+
186
+ options :request => RequestOptions,
187
+ :request_headers => Utils::Headers, :response_headers => Utils::Headers
188
+
189
+ extend Forwardable
190
+
191
+ def_delegators :request, :params_encoder
192
+
193
+ def success?
194
+ SuccessfulStatuses.include?(status)
195
+ end
196
+
197
+ def needs_body?
198
+ !body && MethodsWithBodies.include?(method)
199
+ end
200
+
201
+ def clear_body
202
+ request_headers[ContentLength] = '0'
203
+ self.body = ''
204
+ end
205
+
206
+ def parse_body?
207
+ !StatusesWithoutBody.include?(status)
208
+ end
209
+
210
+ def parallel?
211
+ !!parallel_manager
212
+ end
213
+ end
214
+ end
215
+
@@ -0,0 +1,193 @@
1
+ module Faraday
2
+ module NestedParamsEncoder
3
+ ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
4
+
5
+ def self.escape(s)
6
+ return s.to_s.gsub(ESCAPE_RE) {
7
+ '%' + $&.unpack('H2' * $&.bytesize).join('%').upcase
8
+ }.tr(' ', '+')
9
+ end
10
+
11
+ def self.unescape(s)
12
+ CGI.unescape(s.to_s)
13
+ end
14
+
15
+ def self.encode(params)
16
+ return nil if params == nil
17
+
18
+ if !params.is_a?(Array)
19
+ if !params.respond_to?(:to_hash)
20
+ raise TypeError,
21
+ "Can't convert #{params.class} into Hash."
22
+ end
23
+ params = params.to_hash
24
+ params = params.map do |key, value|
25
+ key = key.to_s if key.kind_of?(Symbol)
26
+ [key, value]
27
+ end
28
+ # Useful default for OAuth and caching.
29
+ # Only to be used for non-Array inputs. Arrays should preserve order.
30
+ params.sort!
31
+ end
32
+
33
+ # Helper lambda
34
+ to_query = lambda do |parent, value|
35
+ if value.is_a?(Hash)
36
+ value = value.map do |key, val|
37
+ key = escape(key)
38
+ [key, val]
39
+ end
40
+ value.sort!
41
+ buffer = ""
42
+ value.each do |key, val|
43
+ new_parent = "#{parent}%5B#{key}%5D"
44
+ buffer << "#{to_query.call(new_parent, val)}&"
45
+ end
46
+ return buffer.chop
47
+ elsif value.is_a?(Array)
48
+ buffer = ""
49
+ value.each_with_index do |val, i|
50
+ new_parent = "#{parent}%5B%5D"
51
+ buffer << "#{to_query.call(new_parent, val)}&"
52
+ end
53
+ return buffer.chop
54
+ else
55
+ encoded_value = escape(value)
56
+ return "#{parent}=#{encoded_value}"
57
+ end
58
+ end
59
+
60
+ # The params have form [['key1', 'value1'], ['key2', 'value2']].
61
+ buffer = ''
62
+ params.each do |parent, value|
63
+ encoded_parent = escape(parent)
64
+ buffer << "#{to_query.call(encoded_parent, value)}&"
65
+ end
66
+ return buffer.chop
67
+ end
68
+
69
+ def self.decode(query)
70
+ return nil if query == nil
71
+ # Recursive helper lambda
72
+ dehash = lambda do |hash|
73
+ hash.each do |(key, value)|
74
+ if value.kind_of?(Hash)
75
+ hash[key] = dehash.call(value)
76
+ end
77
+ end
78
+ # Numeric keys implies an array
79
+ if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
80
+ hash.sort.inject([]) do |accu, (_, value)|
81
+ accu << value; accu
82
+ end
83
+ else
84
+ hash
85
+ end
86
+ end
87
+
88
+ empty_accumulator = {}
89
+ return ((query.split('&').map do |pair|
90
+ pair.split('=', 2) if pair && !pair.empty?
91
+ end).compact.inject(empty_accumulator.dup) do |accu, (key, value)|
92
+ key = unescape(key)
93
+ if value.kind_of?(String)
94
+ value = unescape(value.gsub(/\+/, ' '))
95
+ end
96
+
97
+ array_notation = !!(key =~ /\[\]$/)
98
+ subkeys = key.split(/[\[\]]+/)
99
+ current_hash = accu
100
+ for i in 0...(subkeys.size - 1)
101
+ subkey = subkeys[i]
102
+ current_hash[subkey] = {} unless current_hash[subkey]
103
+ current_hash = current_hash[subkey]
104
+ end
105
+ if array_notation
106
+ current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
107
+ current_hash[subkeys.last] << value
108
+ else
109
+ current_hash[subkeys.last] = value
110
+ end
111
+ accu
112
+ end).inject(empty_accumulator.dup) do |accu, (key, value)|
113
+ accu[key] = value.kind_of?(Hash) ? dehash.call(value) : value
114
+ accu
115
+ end
116
+ end
117
+ end
118
+
119
+ module FlatParamsEncoder
120
+ ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
121
+
122
+ def self.escape(s)
123
+ return s.to_s.gsub(ESCAPE_RE) {
124
+ '%' + $&.unpack('H2' * $&.bytesize).join('%').upcase
125
+ }.tr(' ', '+')
126
+ end
127
+
128
+ def self.unescape(s)
129
+ CGI.unescape(s.to_s)
130
+ end
131
+
132
+ def self.encode(params)
133
+ return nil if params == nil
134
+
135
+ if !params.is_a?(Array)
136
+ if !params.respond_to?(:to_hash)
137
+ raise TypeError,
138
+ "Can't convert #{params.class} into Hash."
139
+ end
140
+ params = params.to_hash
141
+ params = params.map do |key, value|
142
+ key = key.to_s if key.kind_of?(Symbol)
143
+ [key, value]
144
+ end
145
+ # Useful default for OAuth and caching.
146
+ # Only to be used for non-Array inputs. Arrays should preserve order.
147
+ params.sort!
148
+ end
149
+
150
+ # The params have form [['key1', 'value1'], ['key2', 'value2']].
151
+ buffer = ''
152
+ params.each do |key, value|
153
+ encoded_key = escape(key)
154
+ value = value.to_s if value == true || value == false
155
+ if value == nil
156
+ buffer << "#{encoded_key}&"
157
+ elsif value.kind_of?(Array)
158
+ value.each do |sub_value|
159
+ encoded_value = escape(sub_value)
160
+ buffer << "#{encoded_key}=#{encoded_value}&"
161
+ end
162
+ else
163
+ encoded_value = escape(value)
164
+ buffer << "#{encoded_key}=#{encoded_value}&"
165
+ end
166
+ end
167
+ return buffer.chop
168
+ end
169
+
170
+ def self.decode(query)
171
+ empty_accumulator = {}
172
+ return nil if query == nil
173
+ split_query = (query.split('&').map do |pair|
174
+ pair.split('=', 2) if pair && !pair.empty?
175
+ end).compact
176
+ return split_query.inject(empty_accumulator.dup) do |accu, pair|
177
+ pair[0] = unescape(pair[0])
178
+ pair[1] = true if pair[1].nil?
179
+ if pair[1].respond_to?(:to_str)
180
+ pair[1] = unescape(pair[1].to_str.gsub(/\+/, " "))
181
+ end
182
+ if accu[pair[0]].kind_of?(Array)
183
+ accu[pair[0]] << pair[1]
184
+ elsif accu[pair[0]]
185
+ accu[pair[0]] = [accu[pair[0]], pair[1]]
186
+ else
187
+ accu[pair[0]] = pair[1]
188
+ end
189
+ accu
190
+ end
191
+ end
192
+ end
193
+ end