faraday 0.16.0 → 0.17.4

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 (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