faraday 0.16.0 → 0.17.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +232 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +358 -18
  5. data/Rakefile +13 -0
  6. data/lib/faraday/adapter/em_http.rb +97 -140
  7. data/lib/faraday/adapter/em_http_ssl_patch.rb +17 -23
  8. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +15 -18
  9. data/lib/faraday/adapter/em_synchrony.rb +60 -104
  10. data/lib/faraday/adapter/excon.rb +55 -100
  11. data/lib/faraday/adapter/httpclient.rb +39 -61
  12. data/lib/faraday/adapter/net_http.rb +51 -103
  13. data/lib/faraday/adapter/net_http_persistent.rb +28 -49
  14. data/lib/faraday/adapter/patron.rb +35 -54
  15. data/lib/faraday/adapter/rack.rb +12 -28
  16. data/lib/faraday/adapter/test.rb +53 -86
  17. data/lib/faraday/adapter/typhoeus.rb +1 -4
  18. data/lib/faraday/adapter.rb +22 -36
  19. data/lib/faraday/autoload.rb +36 -47
  20. data/lib/faraday/connection.rb +179 -321
  21. data/lib/faraday/deprecate.rb +109 -0
  22. data/lib/faraday/error.rb +79 -21
  23. data/lib/faraday/middleware.rb +28 -4
  24. data/lib/faraday/options.rb +183 -32
  25. data/lib/faraday/parameters.rb +197 -4
  26. data/lib/faraday/rack_builder.rb +55 -66
  27. data/lib/faraday/request/authorization.rb +30 -42
  28. data/lib/faraday/request/basic_authentication.rb +7 -14
  29. data/lib/faraday/request/instrumentation.rb +27 -45
  30. data/lib/faraday/request/multipart.rb +48 -79
  31. data/lib/faraday/request/retry.rb +171 -197
  32. data/lib/faraday/request/token_authentication.rb +10 -15
  33. data/lib/faraday/request/url_encoded.rb +23 -41
  34. data/lib/faraday/request.rb +36 -68
  35. data/lib/faraday/response/logger.rb +69 -22
  36. data/lib/faraday/response/raise_error.rb +18 -36
  37. data/lib/faraday/response.rb +13 -20
  38. data/lib/faraday/upload_io.rb +67 -0
  39. data/lib/faraday/utils.rb +245 -28
  40. data/lib/faraday.rb +174 -93
  41. data/spec/faraday/deprecate_spec.rb +147 -0
  42. data/spec/faraday/error_spec.rb +102 -0
  43. data/spec/faraday/response/raise_error_spec.rb +106 -0
  44. data/spec/spec_helper.rb +105 -0
  45. data/test/adapters/default_test.rb +14 -0
  46. data/test/adapters/em_http_test.rb +30 -0
  47. data/test/adapters/em_synchrony_test.rb +32 -0
  48. data/test/adapters/excon_test.rb +30 -0
  49. data/test/adapters/httpclient_test.rb +34 -0
  50. data/test/adapters/integration.rb +263 -0
  51. data/test/adapters/logger_test.rb +136 -0
  52. data/test/adapters/net_http_persistent_test.rb +114 -0
  53. data/test/adapters/net_http_test.rb +79 -0
  54. data/test/adapters/patron_test.rb +40 -0
  55. data/test/adapters/rack_test.rb +38 -0
  56. data/test/adapters/test_middleware_test.rb +157 -0
  57. data/test/adapters/typhoeus_test.rb +38 -0
  58. data/test/authentication_middleware_test.rb +65 -0
  59. data/test/composite_read_io_test.rb +109 -0
  60. data/test/connection_test.rb +738 -0
  61. data/test/env_test.rb +268 -0
  62. data/test/helper.rb +75 -0
  63. data/test/live_server.rb +67 -0
  64. data/test/middleware/instrumentation_test.rb +88 -0
  65. data/test/middleware/retry_test.rb +282 -0
  66. data/test/middleware_stack_test.rb +260 -0
  67. data/test/multibyte.txt +1 -0
  68. data/test/options_test.rb +333 -0
  69. data/test/parameters_test.rb +157 -0
  70. data/test/request_middleware_test.rb +126 -0
  71. data/test/response_middleware_test.rb +72 -0
  72. data/test/strawberry.rb +2 -0
  73. data/test/utils_test.rb +98 -0
  74. metadata +50 -25
  75. data/lib/faraday/adapter_registry.rb +0 -28
  76. data/lib/faraday/dependency_loader.rb +0 -37
  77. data/lib/faraday/encoders/flat_params_encoder.rb +0 -94
  78. data/lib/faraday/encoders/nested_params_encoder.rb +0 -171
  79. data/lib/faraday/file_part.rb +0 -128
  80. data/lib/faraday/logging/formatter.rb +0 -92
  81. data/lib/faraday/middleware_registry.rb +0 -129
  82. data/lib/faraday/options/connection_options.rb +0 -22
  83. data/lib/faraday/options/env.rb +0 -181
  84. data/lib/faraday/options/proxy_options.rb +0 -28
  85. data/lib/faraday/options/request_options.rb +0 -21
  86. data/lib/faraday/options/ssl_options.rb +0 -59
  87. data/lib/faraday/param_part.rb +0 -53
  88. data/lib/faraday/utils/headers.rb +0 -139
  89. data/lib/faraday/utils/params_hash.rb +0 -61
  90. data/spec/external_adapters/faraday_specs_setup.rb +0 -14
data/lib/faraday/utils.rb CHANGED
@@ -1,12 +1,193 @@
1
- # frozen_string_literal: true
2
-
3
- require 'faraday/utils/headers'
4
- require 'faraday/utils/params_hash'
1
+ require 'thread'
5
2
 
6
3
  module Faraday
7
- # Utils contains various static helper methods.
8
4
  module Utils
9
- module_function
5
+ extend self
6
+
7
+ # Adapted from Rack::Utils::HeaderHash
8
+ class Headers < ::Hash
9
+ def self.from(value)
10
+ new(value)
11
+ end
12
+
13
+ def self.allocate
14
+ new_self = super
15
+ new_self.initialize_names
16
+ new_self
17
+ end
18
+
19
+ def initialize(hash = nil)
20
+ super()
21
+ @names = {}
22
+ self.update(hash || {})
23
+ end
24
+
25
+ def initialize_names
26
+ @names = {}
27
+ end
28
+
29
+ # on dup/clone, we need to duplicate @names hash
30
+ def initialize_copy(other)
31
+ super
32
+ @names = other.names.dup
33
+ end
34
+
35
+ # need to synchronize concurrent writes to the shared KeyMap
36
+ keymap_mutex = Mutex.new
37
+
38
+ # symbol -> string mapper + cache
39
+ KeyMap = Hash.new do |map, key|
40
+ value = if key.respond_to?(:to_str)
41
+ key
42
+ else
43
+ key.to_s.split('_'). # :user_agent => %w(user agent)
44
+ each { |w| w.capitalize! }. # => %w(User Agent)
45
+ join('-') # => "User-Agent"
46
+ end
47
+ keymap_mutex.synchronize { map[key] = value }
48
+ end
49
+ KeyMap[:etag] = "ETag"
50
+
51
+ def [](k)
52
+ k = KeyMap[k]
53
+ super(k) || super(@names[k.downcase])
54
+ end
55
+
56
+ def []=(k, v)
57
+ k = KeyMap[k]
58
+ k = (@names[k.downcase] ||= k)
59
+ # join multiple values with a comma
60
+ v = v.to_ary.join(', ') if v.respond_to? :to_ary
61
+ super(k, v)
62
+ end
63
+
64
+ def fetch(k, *args, &block)
65
+ k = KeyMap[k]
66
+ key = @names.fetch(k.downcase, k)
67
+ super(key, *args, &block)
68
+ end
69
+
70
+ def delete(k)
71
+ k = KeyMap[k]
72
+ if k = @names[k.downcase]
73
+ @names.delete k.downcase
74
+ super(k)
75
+ end
76
+ end
77
+
78
+ def include?(k)
79
+ @names.include? k.downcase
80
+ end
81
+
82
+ alias_method :has_key?, :include?
83
+ alias_method :member?, :include?
84
+ alias_method :key?, :include?
85
+
86
+ def merge!(other)
87
+ other.each { |k, v| self[k] = v }
88
+ self
89
+ end
90
+ alias_method :update, :merge!
91
+
92
+ def merge(other)
93
+ hash = dup
94
+ hash.merge! other
95
+ end
96
+
97
+ def replace(other)
98
+ clear
99
+ @names.clear
100
+ self.update other
101
+ self
102
+ end
103
+
104
+ def to_hash() ::Hash.new.update(self) end
105
+
106
+ def parse(header_string)
107
+ return unless header_string && !header_string.empty?
108
+
109
+ headers = header_string.split(/\r\n/)
110
+
111
+ # Find the last set of response headers.
112
+ start_index = headers.rindex { |x| x.match(/^HTTP\//) } || 0
113
+ last_response = headers.slice(start_index, headers.size)
114
+
115
+ last_response.
116
+ tap { |a| a.shift if a.first.index('HTTP/') == 0 }. # drop the HTTP status line
117
+ map { |h| h.split(/:\s*/, 2) }.reject { |p| p[0].nil? }. # split key and value, ignore blank lines
118
+ each { |key, value|
119
+ # join multiple values with a comma
120
+ if self[key]
121
+ self[key] << ', ' << value
122
+ else
123
+ self[key] = value
124
+ end
125
+ }
126
+ end
127
+
128
+ protected
129
+
130
+ def names
131
+ @names
132
+ end
133
+ end
134
+
135
+ # hash with stringified keys
136
+ class ParamsHash < Hash
137
+ def [](key)
138
+ super(convert_key(key))
139
+ end
140
+
141
+ def []=(key, value)
142
+ super(convert_key(key), value)
143
+ end
144
+
145
+ def delete(key)
146
+ super(convert_key(key))
147
+ end
148
+
149
+ def include?(key)
150
+ super(convert_key(key))
151
+ end
152
+
153
+ alias_method :has_key?, :include?
154
+ alias_method :member?, :include?
155
+ alias_method :key?, :include?
156
+
157
+ def update(params)
158
+ params.each do |key, value|
159
+ self[key] = value
160
+ end
161
+ self
162
+ end
163
+ alias_method :merge!, :update
164
+
165
+ def merge(params)
166
+ dup.update(params)
167
+ end
168
+
169
+ def replace(other)
170
+ clear
171
+ update(other)
172
+ end
173
+
174
+ def merge_query(query, encoder = nil)
175
+ if query && !query.empty?
176
+ update((encoder || Utils.default_params_encoder).decode(query))
177
+ end
178
+ self
179
+ end
180
+
181
+ def to_query(encoder = nil)
182
+ (encoder || Utils.default_params_encoder).encode(self)
183
+ end
184
+
185
+ private
186
+
187
+ def convert_key(key)
188
+ key.to_s
189
+ end
190
+ end
10
191
 
11
192
  def build_query(params)
12
193
  FlatParamsEncoder.encode(params)
@@ -16,19 +197,17 @@ module Faraday
16
197
  NestedParamsEncoder.encode(params)
17
198
  end
18
199
 
19
- ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/.freeze
200
+ ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
20
201
 
21
- def escape(str)
22
- str.to_s.gsub(ESCAPE_RE) do |match|
202
+ def escape(s)
203
+ s.to_s.gsub(ESCAPE_RE) {|match|
23
204
  '%' + match.unpack('H2' * match.bytesize).join('%').upcase
24
- end.tr(' ', '+')
205
+ }.tr(' ', '+')
25
206
  end
26
207
 
27
- def unescape(str)
28
- CGI.unescape str.to_s
29
- end
208
+ def unescape(s) CGI.unescape s.to_s end
30
209
 
31
- DEFAULT_SEP = /[&;] */n.freeze
210
+ DEFAULT_SEP = /[&;] */n
32
211
 
33
212
  # Adapted from Rack
34
213
  def parse_query(query)
@@ -47,18 +226,55 @@ module Faraday
47
226
  attr_writer :default_params_encoder
48
227
  end
49
228
 
229
+ # Stolen from Rack
230
+ def normalize_params(params, name, v = nil)
231
+ name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
232
+ k = $1 || ''
233
+ after = $' || ''
234
+
235
+ return if k.empty?
236
+
237
+ if after == ""
238
+ if params[k]
239
+ params[k] = Array[params[k]] unless params[k].kind_of?(Array)
240
+ params[k] << v
241
+ else
242
+ params[k] = v
243
+ end
244
+ elsif after == "[]"
245
+ params[k] ||= []
246
+ raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
247
+ params[k] << v
248
+ elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
249
+ child_key = $1
250
+ params[k] ||= []
251
+ raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
252
+ if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key)
253
+ normalize_params(params[k].last, child_key, v)
254
+ else
255
+ params[k] << normalize_params({}, child_key, v)
256
+ end
257
+ else
258
+ params[k] ||= {}
259
+ raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash)
260
+ params[k] = normalize_params(params[k], after, v)
261
+ end
262
+
263
+ return params
264
+ end
265
+
50
266
  # Normalize URI() behavior across Ruby versions
51
267
  #
52
268
  # url - A String or URI.
53
269
  #
54
270
  # Returns a parsed URI.
55
- def URI(url) # rubocop:disable Naming/MethodName
271
+ def URI(url)
56
272
  if url.respond_to?(:host)
57
273
  url
58
274
  elsif url.respond_to?(:to_str)
59
275
  default_uri_parser.call(url)
60
276
  else
61
- raise ArgumentError, 'bad argument (expected URI object or URI string)'
277
+ raise ArgumentError, "bad argument (expected URI object or URI string)"
62
278
  end
63
279
  end
64
280
 
@@ -71,28 +287,27 @@ module Faraday
71
287
 
72
288
  def default_uri_parser=(parser)
73
289
  @default_uri_parser = if parser.respond_to?(:call) || parser.nil?
74
- parser
75
- else
76
- parser.method(:parse)
77
- end
290
+ parser
291
+ else
292
+ parser.method(:parse)
293
+ end
78
294
  end
79
295
 
80
- # Receives a String or URI and returns just
81
- # the path with the query string sorted.
296
+ # Receives a String or URI and returns just the path with the query string sorted.
82
297
  def normalize_path(url)
83
298
  url = URI(url)
84
299
  (url.path.start_with?('/') ? url.path : '/' + url.path) +
85
- (url.query ? "?#{sort_query_params(url.query)}" : '')
300
+ (url.query ? "?#{sort_query_params(url.query)}" : "")
86
301
  end
87
302
 
88
303
  # Recursive hash update
89
304
  def deep_merge!(target, hash)
90
305
  hash.each do |key, value|
91
- target[key] = if value.is_a?(Hash) && target[key].is_a?(Hash)
92
- deep_merge(target[key], value)
93
- else
94
- value
95
- end
306
+ if Hash === value and Hash === target[key]
307
+ target[key] = deep_merge(target[key], value)
308
+ else
309
+ target[key] = value
310
+ end
96
311
  end
97
312
  target
98
313
  end
@@ -102,6 +317,8 @@ module Faraday
102
317
  deep_merge!(source.dup, hash)
103
318
  end
104
319
 
320
+ protected
321
+
105
322
  def sort_query_params(query)
106
323
  query.split('&').sort.join('&')
107
324
  end
data/lib/faraday.rb CHANGED
@@ -1,153 +1,128 @@
1
- # frozen_string_literal: true
2
-
1
+ require 'thread'
3
2
  require 'cgi'
4
3
  require 'set'
5
4
  require 'forwardable'
6
- require 'faraday/middleware_registry'
7
- require 'faraday/dependency_loader'
8
5
 
9
- # This is the main namespace for Faraday.
6
+ # Public: This is the main namespace for Faraday. You can either use it to
7
+ # create Faraday::Connection objects, or access it directly.
10
8
  #
11
- # It provides methods to create {Connection} objects, and HTTP-related
12
- # methods to use directly.
9
+ # Examples
13
10
  #
14
- # @example Helpful class methods for easy usage
15
11
  # Faraday.get "http://faraday.com"
16
12
  #
17
- # @example Helpful class method `.new` to create {Connection} objects.
18
13
  # conn = Faraday.new "http://faraday.com"
19
14
  # conn.get '/'
20
15
  #
21
16
  module Faraday
22
- VERSION = '0.16.0'
23
- METHODS_WITH_QUERY = %w[get head delete connect trace].freeze
24
- METHODS_WITH_BODY = %w[post put patch].freeze
17
+ VERSION = "0.17.4"
25
18
 
26
19
  class << self
27
- # The root path that Faraday is being loaded from.
28
- #
29
- # This is the root from where the libraries are auto-loaded.
30
- #
31
- # @return [String]
20
+ # Public: Gets or sets the root path that Faraday is being loaded from.
21
+ # This is the root from where the libraries are auto-loaded from.
32
22
  attr_accessor :root_path
33
23
 
34
- # Gets or sets the path that the Faraday libs are loaded from.
35
- # @return [String]
24
+ # Public: Gets or sets the path that the Faraday libs are loaded from.
36
25
  attr_accessor :lib_path
37
26
 
38
- # @overload default_adapter
39
- # Gets the Symbol key identifying a default Adapter to use
40
- # for the default {Faraday::Connection}. Defaults to `:net_http`.
41
- # @return [Symbol] the default adapter
42
- # @overload default_adapter=(adapter)
43
- # Updates default adapter while resetting {.default_connection}.
44
- # @return [Symbol] the new default_adapter.
27
+ # Public: Gets or sets the Symbol key identifying a default Adapter to use
28
+ # for the default Faraday::Connection.
45
29
  attr_reader :default_adapter
46
30
 
47
- # Documented below, see default_connection
31
+ # Public: Sets the default Faraday::Connection for simple scripts that
32
+ # access the Faraday constant directly.
33
+ #
34
+ # Faraday.get "https://faraday.com"
48
35
  attr_writer :default_connection
49
36
 
50
- # Tells Faraday to ignore the environment proxy (http_proxy).
51
- # Defaults to `false`.
52
- # @return [Boolean]
37
+ # Public: Tells faraday to ignore the environment proxy (http_proxy).
53
38
  attr_accessor :ignore_env_proxy
54
39
 
55
- # Initializes a new {Connection}.
56
- #
57
- # @param url [String,Hash] The optional String base URL to use as a prefix
58
- # for all requests. Can also be the options Hash. Any of these
59
- # values will be set on every request made, unless overridden
60
- # for a specific request.
61
- # @param options [Hash]
62
- # @option options [String] :url Base URL
63
- # @option options [Hash] :params Hash of unencoded URI query params.
64
- # @option options [Hash] :headers Hash of unencoded HTTP headers.
65
- # @option options [Hash] :request Hash of request options.
66
- # @option options [Hash] :ssl Hash of SSL options.
67
- # @option options [Hash] :proxy Hash of Proxy options.
68
- # @return [Faraday::Connection]
69
- #
70
- # @example With an URL argument
40
+ # Public: Initializes a new Faraday::Connection.
41
+ #
42
+ # url - The optional String base URL to use as a prefix for all
43
+ # requests. Can also be the options Hash.
44
+ # options - The optional Hash used to configure this Faraday::Connection.
45
+ # Any of these values will be set on every request made, unless
46
+ # overridden for a specific request.
47
+ # :url - String base URL.
48
+ # :params - Hash of URI query unencoded key/value pairs.
49
+ # :headers - Hash of unencoded HTTP header key/value pairs.
50
+ # :request - Hash of request options.
51
+ # :ssl - Hash of SSL options.
52
+ # :proxy - Hash of Proxy options.
53
+ #
54
+ # Examples
55
+ #
71
56
  # Faraday.new 'http://faraday.com'
72
- # # => Faraday::Connection to http://faraday.com
73
- #
74
- # @example With an URL argument and an options hash
75
- # Faraday.new 'http://faraday.com', params: { page: 1 }
76
- # # => Faraday::Connection to http://faraday.com?page=1
77
- #
78
- # @example With everything in an options hash
79
- # Faraday.new url: 'http://faraday.com',
80
- # params: { page: 1 }
81
- # # => Faraday::Connection to http://faraday.com?page=1
82
- def new(url = nil, options = {}, &block)
83
- options = default_connection_options.merge(options)
57
+ #
58
+ # # http://faraday.com?page=1
59
+ # Faraday.new 'http://faraday.com', :params => {:page => 1}
60
+ #
61
+ # # same
62
+ #
63
+ # Faraday.new :url => 'http://faraday.com',
64
+ # :params => {:page => 1}
65
+ #
66
+ # Returns a Faraday::Connection.
67
+ def new(url = nil, options = nil, &block)
68
+ options = options ? default_connection_options.merge(options) : default_connection_options
84
69
  Faraday::Connection.new(url, options, &block)
85
70
  end
86
71
 
87
- # @private
88
72
  # Internal: Requires internal Faraday libraries.
89
73
  #
90
- # @param libs [Array] one or more relative String names to Faraday classes.
91
- # @return [void]
74
+ # *libs - One or more relative String names to Faraday classes.
75
+ #
76
+ # Returns nothing.
92
77
  def require_libs(*libs)
93
78
  libs.each do |lib|
94
79
  require "#{lib_path}/#{lib}"
95
80
  end
96
81
  end
97
82
 
98
- alias require_lib require_libs
99
-
100
- # Documented elsewhere, see default_adapter reader
83
+ # Public: Updates default adapter while resetting
84
+ # #default_connection.
85
+ #
86
+ # Returns the new default_adapter.
101
87
  def default_adapter=(adapter)
102
88
  @default_connection = nil
103
89
  @default_adapter = adapter
104
90
  end
105
91
 
106
- def respond_to_missing?(symbol, include_private = false)
92
+ alias require_lib require_libs
93
+
94
+ def respond_to?(symbol, include_private = false)
107
95
  default_connection.respond_to?(symbol, include_private) || super
108
96
  end
109
97
 
110
- private
111
-
98
+ private
112
99
  # Internal: Proxies method calls on the Faraday constant to
113
- # .default_connection.
100
+ # #default_connection.
114
101
  def method_missing(name, *args, &block)
115
- if default_connection.respond_to?(name)
116
- default_connection.send(name, *args, &block)
117
- else
118
- super
119
- end
102
+ default_connection.send(name, *args, &block)
120
103
  end
121
104
  end
122
105
 
123
106
  self.ignore_env_proxy = false
124
- self.root_path = File.expand_path __dir__
125
- self.lib_path = File.expand_path 'faraday', __dir__
107
+ self.root_path = File.expand_path "..", __FILE__
108
+ self.lib_path = File.expand_path "../faraday", __FILE__
126
109
  self.default_adapter = :net_http
127
110
 
128
- # @overload default_connection
129
- # Gets the default connection used for simple scripts.
130
- # @return [Faraday::Connection] a connection configured with
131
- # the default_adapter.
132
- # @overload default_connection=(connection)
133
- # @param connection [Faraday::Connection]
134
- # Sets the default {Faraday::Connection} for simple scripts that
135
- # access the Faraday constant directly, such as
136
- # <code>Faraday.get "https://faraday.com"</code>.
111
+ # Gets the default connection used for simple scripts.
112
+ #
113
+ # Returns a Faraday::Connection, configured with the #default_adapter.
137
114
  def self.default_connection
138
115
  @default_connection ||= Connection.new(default_connection_options)
139
116
  end
140
117
 
141
- # Gets the default connection options used when calling {Faraday#new}.
118
+ # Gets the default connection options used when calling Faraday#new.
142
119
  #
143
- # @return [Faraday::ConnectionOptions]
120
+ # Returns a Faraday::ConnectionOptions.
144
121
  def self.default_connection_options
145
122
  @default_connection_options ||= ConnectionOptions.new
146
123
  end
147
124
 
148
- # Sets the default options used when calling {Faraday#new}.
149
- #
150
- # @param options [Hash, Faraday::ConnectionOptions]
125
+ # Public: Sets the default options used when calling Faraday#new.
151
126
  def self.default_connection_options=(options)
152
127
  @default_connection = nil
153
128
  @default_connection_options = ConnectionOptions.from(options)
@@ -158,9 +133,115 @@ module Faraday
158
133
  Timer = Timeout
159
134
  end
160
135
 
161
- require_libs 'utils', 'options', 'connection', 'rack_builder', 'parameters',
162
- 'middleware', 'adapter', 'request', 'response', 'error',
163
- 'file_part', 'param_part'
136
+ # Public: Adds the ability for other modules to register and lookup
137
+ # middleware classes.
138
+ module MiddlewareRegistry
139
+ # Public: Register middleware class(es) on the current module.
140
+ #
141
+ # mapping - A Hash mapping Symbol keys to classes. Classes can be expressed
142
+ # as fully qualified constant, or a Proc that will be lazily
143
+ # called to return the former.
144
+ #
145
+ # Examples
146
+ #
147
+ # module Faraday
148
+ # class Whatever
149
+ # # Middleware looked up by :foo returns Faraday::Whatever::Foo.
150
+ # register_middleware :foo => Foo
151
+ #
152
+ # # Middleware looked up by :bar returns Faraday::Whatever.const_get(:Bar)
153
+ # register_middleware :bar => :Bar
154
+ #
155
+ # # Middleware looked up by :baz requires 'baz' and returns Faraday::Whatever.const_get(:Baz)
156
+ # register_middleware :baz => [:Baz, 'baz']
157
+ # end
158
+ # end
159
+ #
160
+ # Returns nothing.
161
+ def register_middleware(autoload_path = nil, mapping = nil)
162
+ if mapping.nil?
163
+ mapping = autoload_path
164
+ autoload_path = nil
165
+ end
166
+ middleware_mutex do
167
+ @middleware_autoload_path = autoload_path if autoload_path
168
+ (@registered_middleware ||= {}).update(mapping)
169
+ end
170
+ end
164
171
 
165
- require_lib 'autoload' unless ENV['FARADAY_NO_AUTOLOAD']
172
+ # Public: Lookup middleware class with a registered Symbol shortcut.
173
+ #
174
+ # key - The Symbol key for the registered middleware.
175
+ #
176
+ # Examples
177
+ #
178
+ # module Faraday
179
+ # class Whatever
180
+ # register_middleware :foo => Foo
181
+ # end
182
+ # end
183
+ #
184
+ # Faraday::Whatever.lookup_middleware(:foo)
185
+ # # => Faraday::Whatever::Foo
186
+ #
187
+ # Returns a middleware Class.
188
+ def lookup_middleware(key)
189
+ load_middleware(key) ||
190
+ raise(Faraday::Error.new("#{key.inspect} is not registered on #{self}"))
191
+ end
192
+
193
+ def middleware_mutex(&block)
194
+ @middleware_mutex ||= begin
195
+ require 'monitor'
196
+ Monitor.new
197
+ end
198
+ @middleware_mutex.synchronize(&block)
199
+ end
200
+
201
+ def fetch_middleware(key)
202
+ defined?(@registered_middleware) && @registered_middleware[key]
203
+ end
204
+
205
+ def load_middleware(key)
206
+ value = fetch_middleware(key)
207
+ case value
208
+ when Module
209
+ value
210
+ when Symbol, String
211
+ middleware_mutex do
212
+ @registered_middleware[key] = const_get(value)
213
+ end
214
+ when Proc
215
+ middleware_mutex do
216
+ @registered_middleware[key] = value.call
217
+ end
218
+ when Array
219
+ middleware_mutex do
220
+ const, path = value
221
+ if root = @middleware_autoload_path
222
+ path = "#{root}/#{path}"
223
+ end
224
+ require(path)
225
+ @registered_middleware[key] = const
226
+ end
227
+ load_middleware(key)
228
+ end
229
+ end
230
+ end
231
+
232
+ def self.const_missing(name)
233
+ if name.to_sym == :Builder
234
+ warn "Faraday::Builder is now Faraday::RackBuilder."
235
+ const_set name, RackBuilder
236
+ else
237
+ super
238
+ end
239
+ end
240
+
241
+ require_libs "utils", "options", "connection", "rack_builder", "parameters",
242
+ "middleware", "adapter", "request", "response", "upload_io", "error"
243
+
244
+ if !ENV["FARADAY_NO_AUTOLOAD"]
245
+ require_lib 'autoload'
246
+ end
166
247
  end