faraday 0.8.11 → 0.9.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -13,43 +13,37 @@ module Faraday
13
13
  # Override this to modify the environment after the response has finished.
14
14
  # Calls the `parse` method if defined
15
15
  def on_complete(env)
16
- if respond_to? :parse
17
- env[:body] = parse(env[:body]) unless [204,304].index env[:status]
18
- end
16
+ env.body = parse(env.body) if respond_to?(:parse) && env.parse_body?
19
17
  end
20
18
  end
21
19
 
22
20
  extend Forwardable
23
- extend AutoloadHelper
24
21
  extend MiddlewareRegistry
25
22
 
26
- autoload_all 'faraday/response',
27
- :RaiseError => 'raise_error',
28
- :Logger => 'logger'
29
-
30
- register_middleware \
31
- :raise_error => :RaiseError,
32
- :logger => :Logger
23
+ register_middleware File.expand_path('../response', __FILE__),
24
+ :raise_error => [:RaiseError, 'raise_error'],
25
+ :logger => [:Logger, 'logger']
33
26
 
34
27
  def initialize(env = nil)
35
- @env = env
28
+ @env = Env.from(env) if env
36
29
  @on_complete_callbacks = []
37
30
  end
38
31
 
39
32
  attr_reader :env
40
- alias_method :to_hash, :env
33
+
34
+ def_delegators :env, :to_hash
41
35
 
42
36
  def status
43
- finished? ? env[:status] : nil
37
+ finished? ? env.status : nil
44
38
  end
45
39
 
46
40
  def headers
47
- finished? ? env[:response_headers] : {}
41
+ finished? ? env.response_headers : {}
48
42
  end
49
43
  def_delegator :headers, :[]
50
44
 
51
45
  def body
52
- finished? ? env[:body] : nil
46
+ finished? ? env.body : nil
53
47
  end
54
48
 
55
49
  def finished?
@@ -67,33 +61,34 @@ module Faraday
67
61
 
68
62
  def finish(env)
69
63
  raise "response already finished" if finished?
70
- @env = env
64
+ @env = Env.from(env)
71
65
  @on_complete_callbacks.each { |callback| callback.call(env) }
72
66
  return self
73
67
  end
74
68
 
75
69
  def success?
76
- (200..299).include?(status)
70
+ finished? && env.success?
77
71
  end
78
72
 
79
73
  # because @on_complete_callbacks cannot be marshalled
80
74
  def marshal_dump
81
75
  !finished? ? nil : {
82
- :status => @env[:status], :body => @env[:body],
83
- :response_headers => @env[:response_headers]
76
+ :status => @env.status, :body => @env.body,
77
+ :response_headers => @env.response_headers
84
78
  }
85
79
  end
86
80
 
87
81
  def marshal_load(env)
88
- @env = env
82
+ @env = Env.from(env)
89
83
  end
90
84
 
91
85
  # Expand the env with more properties, without overriding existing ones.
92
86
  # Useful for applying request params after restoring a marshalled Response.
93
87
  def apply_request(request_env)
94
88
  raise "response didn't finish yet" unless finished?
95
- @env = request_env.merge @env
89
+ @env = Env.from(request_env).merge(@env)
96
90
  return self
97
91
  end
98
92
  end
99
93
  end
94
+
@@ -15,14 +15,14 @@ module Faraday
15
15
  def_delegators :@logger, :debug, :info, :warn, :error, :fatal
16
16
 
17
17
  def call(env)
18
- info "#{env[:method]} #{env[:url].to_s}"
19
- debug('request') { dump_headers env[:request_headers] }
18
+ info "#{env.method} #{env.url.to_s}"
19
+ debug('request') { dump_headers env.request_headers }
20
20
  super
21
21
  end
22
22
 
23
23
  def on_complete(env)
24
- info('Status') { env[:status].to_s }
25
- debug('response') { dump_headers env[:response_headers] }
24
+ info('Status') { env.status.to_s }
25
+ debug('response') { dump_headers env.response_headers }
26
26
  end
27
27
 
28
28
  private
@@ -1,19 +1,18 @@
1
1
  module Faraday
2
2
  class Response::RaiseError < Response::Middleware
3
+ ClientErrorStatuses = 400...600
4
+
3
5
  def on_complete(env)
4
6
  case env[:status]
5
7
  when 404
6
8
  raise Faraday::Error::ResourceNotFound, response_values(env)
7
- when 407
8
- # mimic the behavior that we get with proxy requests with HTTPS
9
- raise Faraday::Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
10
- when 400...600
9
+ when ClientErrorStatuses
11
10
  raise Faraday::Error::ClientError, response_values(env)
12
11
  end
13
12
  end
14
13
 
15
14
  def response_values(env)
16
- {:status => env[:status], :headers => env[:response_headers], :body => env[:body]}
15
+ {:status => env.status, :headers => env.response_headers, :body => env.body}
17
16
  end
18
17
  end
19
18
  end
@@ -1,4 +1,6 @@
1
- require 'cgi'
1
+ require 'uri'
2
+ require 'thread'
3
+ Faraday.require_libs 'parameters'
2
4
 
3
5
  module Faraday
4
6
  module Utils
@@ -6,26 +8,28 @@ module Faraday
6
8
 
7
9
  # Adapted from Rack::Utils::HeaderHash
8
10
  class Headers < ::Hash
11
+ def self.from(value)
12
+ new(value)
13
+ end
14
+
9
15
  def initialize(hash={})
10
16
  super()
11
17
  @names = {}
12
18
  self.update hash
13
19
  end
14
20
 
15
- # on dup/clone, we need to duplicate @names hash
16
- def initialize_copy(other)
17
- super
18
- @names = other.names.dup
19
- end
21
+ # need to synchronize concurrent writes to the shared KeyMap
22
+ keymap_mutex = Mutex.new
20
23
 
21
24
  # symbol -> string mapper + cache
22
25
  KeyMap = Hash.new do |map, key|
23
- map[key] = if key.respond_to?(:to_str) then key
26
+ value = if key.respond_to?(:to_str) then key
24
27
  else
25
28
  key.to_s.split('_'). # :user_agent => %w(user agent)
26
29
  each { |w| w.capitalize! }. # => %w(User Agent)
27
30
  join('-') # => "User-Agent"
28
31
  end
32
+ keymap_mutex.synchronize { map[key] = value }
29
33
  end
30
34
  KeyMap[:etag] = "ETag"
31
35
 
@@ -71,7 +75,6 @@ module Faraday
71
75
 
72
76
  def replace(other)
73
77
  clear
74
- @names.clear
75
78
  self.update other
76
79
  self
77
80
  end
@@ -90,12 +93,6 @@ module Faraday
90
93
  end
91
94
  }
92
95
  end
93
-
94
- protected
95
-
96
- def names
97
- @names
98
- end
99
96
  end
100
97
 
101
98
  # hash with stringified keys
@@ -137,15 +134,15 @@ module Faraday
137
134
  update(other)
138
135
  end
139
136
 
140
- def merge_query(query)
137
+ def merge_query(query, encoder = nil)
141
138
  if query && !query.empty?
142
- update Utils.parse_nested_query(query)
139
+ update((encoder || Utils.default_params_encoder).decode(query))
143
140
  end
144
141
  self
145
142
  end
146
143
 
147
- def to_query
148
- Utils.build_nested_query(self)
144
+ def to_query(encoder = nil)
145
+ (encoder || Utils.default_params_encoder).encode(self)
149
146
  end
150
147
 
151
148
  private
@@ -155,39 +152,19 @@ module Faraday
155
152
  end
156
153
  end
157
154
 
158
- # Copied from Rack
159
155
  def build_query(params)
160
- params.map { |k, v|
161
- if v.class == Array
162
- build_query(v.map { |x| [k, x] })
163
- else
164
- v.nil? ? escape(k) : "#{escape(k)}=#{escape(v)}"
165
- end
166
- }.join("&")
156
+ FlatParamsEncoder.encode(params)
167
157
  end
168
158
 
169
- # Rack's version modified to handle non-String values
170
- def build_nested_query(value, prefix = nil)
171
- case value
172
- when Array
173
- value.map { |v| build_nested_query(v, "#{prefix}%5B%5D") }.join("&")
174
- when Hash
175
- value.map { |k, v|
176
- build_nested_query(v, prefix ? "#{prefix}%5B#{escape(k)}%5D" : escape(k))
177
- }.join("&")
178
- when NilClass
179
- prefix
180
- else
181
- raise ArgumentError, "value must be a Hash" if prefix.nil?
182
- "#{prefix}=#{escape(value)}"
183
- end
159
+ def build_nested_query(params)
160
+ NestedParamsEncoder.encode(params)
184
161
  end
185
162
 
186
163
  ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
187
164
 
188
165
  def escape(s)
189
- s.to_s.gsub(ESCAPE_RE) {|match|
190
- '%' + match.unpack('H2' * match.bytesize).join('%').upcase
166
+ s.to_s.gsub(ESCAPE_RE) {
167
+ '%' + $&.unpack('H2' * $&.bytesize).join('%').upcase
191
168
  }.tr(' ', '+')
192
169
  end
193
170
 
@@ -196,31 +173,20 @@ module Faraday
196
173
  DEFAULT_SEP = /[&;] */n
197
174
 
198
175
  # Adapted from Rack
199
- def parse_query(qs)
200
- params = {}
201
-
202
- (qs || '').split(DEFAULT_SEP).each do |p|
203
- k, v = p.split('=', 2).map { |x| unescape(x) }
176
+ def parse_query(query)
177
+ FlatParamsEncoder.decode(query)
178
+ end
204
179
 
205
- if cur = params[k]
206
- if cur.class == Array then params[k] << v
207
- else params[k] = [cur, v]
208
- end
209
- else
210
- params[k] = v
211
- end
212
- end
213
- params
180
+ def parse_nested_query(query)
181
+ NestedParamsEncoder.decode(query)
214
182
  end
215
183
 
216
- def parse_nested_query(qs)
217
- params = {}
184
+ def default_params_encoder
185
+ @default_params_encoder ||= NestedParamsEncoder
186
+ end
218
187
 
219
- (qs || '').split(DEFAULT_SEP).each do |p|
220
- k, v = p.split('=', 2).map { |s| unescape(s) }
221
- normalize_params(params, k, v)
222
- end
223
- params
188
+ class << self
189
+ attr_writer :default_params_encoder
224
190
  end
225
191
 
226
192
  # Stolen from Rack
@@ -232,7 +198,12 @@ module Faraday
232
198
  return if k.empty?
233
199
 
234
200
  if after == ""
235
- params[k] = v
201
+ if params[k]
202
+ params[k] = Array[params[k]] unless params[k].kind_of?(Array)
203
+ params[k] << v
204
+ else
205
+ params[k] = v
206
+ end
236
207
  elsif after == "[]"
237
208
  params[k] ||= []
238
209
  raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
@@ -255,9 +226,25 @@ module Faraday
255
226
  return params
256
227
  end
257
228
 
258
- # Receives a URL and returns just the path with the query string sorted.
229
+ # Normalize URI() behavior across Ruby versions
230
+ #
231
+ # url - A String or URI.
232
+ #
233
+ # Returns a parsed URI.
234
+ def URI(url)
235
+ if url.respond_to?(:host)
236
+ url
237
+ elsif url.respond_to?(:to_str)
238
+ Kernel.URI(url)
239
+ else
240
+ raise ArgumentError, "bad argument (expected URI object or URI string)"
241
+ end
242
+ end
243
+
244
+ # Receives a String or URI and returns just the path with the query string sorted.
259
245
  def normalize_path(url)
260
- (url.path != "" ? url.path : "/") +
246
+ url = URI(url)
247
+ (url.path.start_with?('/') ? url.path : '/' + url.path) +
261
248
  (url.query ? "?#{sort_query_params(url.query)}" : "")
262
249
  end
263
250
 
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env bash
2
+ # Usage: script/console
3
+ # Starts an IRB console with this library loaded.
4
+
5
+ gemspec="$(ls *.gemspec | head -1)"
6
+
7
+ exec bundle exec irb -r "${gemspec%.*}"
@@ -8,7 +8,10 @@ set -e
8
8
  version="$(script/package | grep Version: | awk '{print $2}')"
9
9
  [ -n "$version" ] || exit 1
10
10
 
11
- git commit -a -m "faraday $version"
12
- git tag "v${version}"
13
- git push origin "v${version}" HEAD
11
+ git commit --allow-empty -a -m "Release $version"
12
+ git tag "v$version"
13
+ git push origin
14
+ git push origin "v$version"
15
+ git push legacy
16
+ git push legacy "v$version"
14
17
  gem push pkg/*-${version}.gem
@@ -6,13 +6,15 @@ ensure
6
6
  $VERBOSE = old_verbose
7
7
  end
8
8
  require 'webrick'
9
+ require 'fileutils'
9
10
 
10
11
  port = 4000
11
12
  if found = ARGV.index('-p')
12
13
  port = ARGV[found + 1].to_i
13
14
  end
14
15
 
15
- log_io = $stdout
16
+ FileUtils.mkdir_p('log')
17
+ log_io = File.open('log/test.log', 'w')
16
18
  log_io.sync = true
17
19
 
18
20
  webrick_opts = {
@@ -24,7 +24,6 @@ if [[ "$RUBYOPT" != *"bundler/setup"* ]]; then
24
24
  fi
25
25
 
26
26
  port=3999
27
- proxy_port=3998
28
27
  scheme=http
29
28
 
30
29
  if [ "$SSL" = "yes" ]; then
@@ -48,18 +47,12 @@ filter_matching() {
48
47
 
49
48
  start_server() {
50
49
  mkdir -p log
51
- script/server -p $port >log/test.log 2>&1 &
52
- echo $!
53
- }
54
-
55
- start_proxy() {
56
- mkdir -p log
57
- script/proxy-server -p $proxy_port -u "faraday@test.local:there is cake" >log/proxy.log 2>&1 &
50
+ script/server -p $port >log/server.log 2>&1 &
58
51
  echo $!
59
52
  }
60
53
 
61
54
  server_started() {
62
- lsof -i :${1?} >/dev/null
55
+ lsof -i :$port >/dev/null
63
56
  }
64
57
 
65
58
  timestamp() {
@@ -69,7 +62,7 @@ timestamp() {
69
62
  wait_for_server() {
70
63
  timeout=$(( `timestamp` + $1 ))
71
64
  while true; do
72
- if server_started "$2"; then
65
+ if server_started; then
73
66
  break
74
67
  elif [ `timestamp` -gt "$timeout" ]; then
75
68
  echo "timed out after $1 seconds" >&2
@@ -116,45 +109,27 @@ fi
116
109
 
117
110
  # If there are any adapter tests, spin up the HTTP server
118
111
  if [ -n "$(filter_matching "adapters" "${test_files[@]}")" ]; then
119
- if server_started $port; then
112
+ if server_started; then
120
113
  echo "aborted: another instance of server running on $port" >&2
121
114
  exit 1
122
115
  fi
123
116
  server_pid=$(start_server)
124
- proxy_pid=$(start_proxy)
125
- wait_for_server 15 $port || {
126
- cat log/test.log
117
+ wait_for_server 15 || {
118
+ cat log/server.log
127
119
  exit 1
128
120
  }
129
- wait_for_server 5 $proxy_port
130
121
  cleanup() {
131
122
  if [ $? -ne 0 ] && [ -n "$TRAVIS" ]; then
132
123
  cat log/test.log
133
124
  fi
134
125
  kill "$server_pid"
135
- kill "$proxy_pid"
136
126
  }
137
127
  trap cleanup INT EXIT
138
128
  export LIVE="${scheme}://localhost:${port}"
139
- export LIVE_PROXY="http://faraday%40test.local:there%20is%20cake@localhost:${proxy_port}"
140
129
  fi
141
130
 
142
- warnings="${TMPDIR:-/tmp}/faraday-warnings.$$"
143
-
144
131
  run_test_files() {
145
- # Save warnings on stderr to a separate file
146
- RUBYOPT="$RUBYOPT -w" ruby -e 'while f=ARGV.shift and f!="--"; load f; end' "${test_files[@]}" -- "$@" \
147
- 2> >(tee >(grep 'warning:' >"$warnings") | grep -v 'warning:')
148
- }
149
-
150
- check_warnings() {
151
- # Display Ruby warnings from this project's source files. Abort if any were found.
152
- num="$(grep -F "$PWD" "$warnings" | grep -v "${PWD}/vendor/bundle" | sort | uniq -c | sort -rn | tee /dev/stderr | wc -l)"
153
- rm -f "$warnings"
154
- if [ "$num" -gt 0 ]; then
155
- echo "FAILED: this test suite doesn't tolerate Ruby syntax warnings!" >&2
156
- exit 1
157
- fi
132
+ ruby -e 'while f=ARGV.shift and f!="--"; load f; end' "${test_files[@]}" -- "$@"
158
133
  }
159
134
 
160
135
  if [ -n "$RBENV_VERSIONS" ]; then
@@ -167,4 +142,3 @@ else
167
142
  run_test_files "$@"
168
143
  fi
169
144
 
170
- check_warnings