faraday 0.9.0 → 0.9.2
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.
- data/LICENSE.md +1 -1
- data/README.md +52 -23
- data/lib/faraday.rb +1 -1
- data/lib/faraday/adapter/em_synchrony.rb +8 -0
- data/lib/faraday/adapter/excon.rb +1 -0
- data/lib/faraday/adapter/httpclient.rb +14 -3
- data/lib/faraday/adapter/net_http.rb +26 -20
- data/lib/faraday/adapter/net_http_persistent.rb +5 -3
- data/lib/faraday/adapter/patron.rb +13 -3
- data/lib/faraday/autoload.rb +0 -1
- data/lib/faraday/connection.rb +8 -3
- data/lib/faraday/options.rb +9 -0
- data/lib/faraday/parameters.rb +62 -58
- data/lib/faraday/rack_builder.rb +3 -2
- data/lib/faraday/request/retry.rb +40 -4
- data/lib/faraday/response.rb +2 -2
- data/lib/faraday/response/logger.rb +26 -1
- data/lib/faraday/utils.rb +13 -1
- metadata +61 -120
- checksums.yaml +0 -7
- data/.document +0 -6
- data/CHANGELOG.md +0 -15
- data/CONTRIBUTING.md +0 -36
- data/Gemfile +0 -29
- data/Rakefile +0 -71
- data/faraday.gemspec +0 -34
- data/script/console +0 -7
- data/script/generate_certs +0 -42
- data/script/package +0 -7
- data/script/proxy-server +0 -42
- data/script/release +0 -17
- data/script/server +0 -36
- data/script/test +0 -172
- data/test/adapters/default_test.rb +0 -14
- data/test/adapters/em_http_test.rb +0 -20
- data/test/adapters/em_synchrony_test.rb +0 -20
- data/test/adapters/excon_test.rb +0 -20
- data/test/adapters/httpclient_test.rb +0 -21
- data/test/adapters/integration.rb +0 -254
- data/test/adapters/logger_test.rb +0 -37
- data/test/adapters/net_http_persistent_test.rb +0 -20
- data/test/adapters/net_http_test.rb +0 -14
- data/test/adapters/patron_test.rb +0 -20
- data/test/adapters/rack_test.rb +0 -31
- data/test/adapters/test_middleware_test.rb +0 -114
- data/test/adapters/typhoeus_test.rb +0 -28
- data/test/authentication_middleware_test.rb +0 -65
- data/test/composite_read_io_test.rb +0 -111
- data/test/connection_test.rb +0 -522
- data/test/env_test.rb +0 -210
- data/test/helper.rb +0 -81
- data/test/live_server.rb +0 -67
- data/test/middleware/instrumentation_test.rb +0 -88
- data/test/middleware/retry_test.rb +0 -109
- data/test/middleware_stack_test.rb +0 -173
- data/test/multibyte.txt +0 -1
- data/test/options_test.rb +0 -252
- data/test/request_middleware_test.rb +0 -142
- data/test/response_middleware_test.rb +0 -72
- data/test/strawberry.rb +0 -2
- data/test/utils_test.rb +0 -58
data/LICENSE.md
CHANGED
data/README.md
CHANGED
@@ -6,11 +6,13 @@ processing the request/response cycle.
|
|
6
6
|
|
7
7
|
Faraday supports these adapters:
|
8
8
|
|
9
|
-
* Net::HTTP
|
9
|
+
* [Net::HTTP][net_http] _(default)_
|
10
|
+
* [Net::HTTP::Persistent][persistent]
|
10
11
|
* [Excon][]
|
11
12
|
* [Typhoeus][]
|
12
13
|
* [Patron][]
|
13
14
|
* [EventMachine][]
|
15
|
+
* [HTTPClient][]
|
14
16
|
|
15
17
|
It also includes a Rack adapter for hitting loaded Rack applications through
|
16
18
|
Rack::Test, and a Test adapter for stubbing requests by hand.
|
@@ -29,7 +31,7 @@ end
|
|
29
31
|
response = conn.get '/nigiri/sake.json' # GET http://sushi.com/nigiri/sake.json
|
30
32
|
response.body
|
31
33
|
|
32
|
-
conn.get '/nigiri', { :name => 'Maguro' } # GET /nigiri?name=Maguro
|
34
|
+
conn.get '/nigiri', { :name => 'Maguro' } # GET http://sushi.com/nigiri?name=Maguro
|
33
35
|
|
34
36
|
conn.get do |req| # GET http://sushi.com/search?page=2&limit=100
|
35
37
|
req.url '/search', :page => 2
|
@@ -56,13 +58,39 @@ conn.get do |req|
|
|
56
58
|
end
|
57
59
|
```
|
58
60
|
|
59
|
-
If you don't need to set up anything, you can roll with just the
|
61
|
+
If you don't need to set up anything, you can roll with just the default middleware
|
62
|
+
stack and default adapter (see [Faraday::RackBuilder#initialize](https://github.com/lostisland/faraday/blob/master/lib/faraday/rack_builder.rb)):
|
60
63
|
|
61
64
|
```ruby
|
62
|
-
# using the default stack:
|
63
65
|
response = Faraday.get 'http://sushi.com/nigiri/sake.json'
|
64
66
|
```
|
65
67
|
|
68
|
+
### Changing how parameters are serialized
|
69
|
+
|
70
|
+
Sometimes you need to send the same URL parameter multiple times with different
|
71
|
+
values. This requires manually setting the parameter encoder and can be done on
|
72
|
+
either per-connection or per-request basis.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
# per-connection setting
|
76
|
+
conn = Faraday.new :params_encoder => Faraday::FlatParamsEncoder
|
77
|
+
|
78
|
+
conn.get do |req|
|
79
|
+
# per-request setting:
|
80
|
+
# req.options.params_encoder = my_encoder
|
81
|
+
req.params['roll'] = ['california', 'philadelphia']
|
82
|
+
end
|
83
|
+
# GET 'http://sushi.com?roll=california&roll=philadelphia'
|
84
|
+
```
|
85
|
+
|
86
|
+
The value of Faraday `params_encoder` can be any object that responds to:
|
87
|
+
|
88
|
+
* `encode(hash) #=> String`
|
89
|
+
* `decode(string) #=> Hash`
|
90
|
+
|
91
|
+
The encoder will affect both how query strings are processed and how POST bodies
|
92
|
+
get serialized. The default encoder is Faraday::NestedParamsEncoder.
|
93
|
+
|
66
94
|
## Advanced middleware usage
|
67
95
|
|
68
96
|
The order in which middleware is stacked is important. Like with Rack, the
|
@@ -106,11 +134,13 @@ Middleware are classes that implement a `call` instance method. They hook into
|
|
106
134
|
the request/response cycle.
|
107
135
|
|
108
136
|
```ruby
|
109
|
-
def call(
|
137
|
+
def call(request_env)
|
110
138
|
# do something with the request
|
139
|
+
# request_env[:request_headers].merge!(...)
|
111
140
|
|
112
|
-
@app.call(
|
141
|
+
@app.call(request_env).on_complete do |response_env|
|
113
142
|
# do something with the response
|
143
|
+
# response_env[:response_headers].merge!(...)
|
114
144
|
end
|
115
145
|
end
|
116
146
|
```
|
@@ -140,20 +170,20 @@ later, response. Some keys are:
|
|
140
170
|
```ruby
|
141
171
|
# It's possible to define stubbed request outside a test adapter block.
|
142
172
|
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
143
|
-
stub.get('/tamago') { [200, {}, 'egg'] }
|
173
|
+
stub.get('/tamago') { |env| [200, {}, 'egg'] }
|
144
174
|
end
|
145
175
|
|
146
176
|
# You can pass stubbed request to the test adapter or define them in a block
|
147
177
|
# or a combination of the two.
|
148
178
|
test = Faraday.new do |builder|
|
149
179
|
builder.adapter :test, stubs do |stub|
|
150
|
-
stub.get('/ebi') {[ 200, {}, 'shrimp' ]}
|
180
|
+
stub.get('/ebi') { |env| [ 200, {}, 'shrimp' ]}
|
151
181
|
end
|
152
182
|
end
|
153
183
|
|
154
184
|
# It's also possible to stub additional requests after the connection has
|
155
185
|
# been initialized. This is useful for testing.
|
156
|
-
stubs.get('/uni') {[ 200, {}, 'urchin' ]}
|
186
|
+
stubs.get('/uni') { |env| [ 200, {}, 'urchin' ]}
|
157
187
|
|
158
188
|
resp = test.get '/tamago'
|
159
189
|
resp.body # => 'egg'
|
@@ -180,13 +210,9 @@ stubs.verify_stubbed_calls
|
|
180
210
|
This library aims to support and is [tested against][travis] the following Ruby
|
181
211
|
implementations:
|
182
212
|
|
183
|
-
*
|
184
|
-
*
|
185
|
-
*
|
186
|
-
* MRI 2.0.0
|
187
|
-
* MRI 2.1.0
|
188
|
-
* [JRuby][]
|
189
|
-
* [Rubinius][]
|
213
|
+
* Ruby 1.8.7+
|
214
|
+
* [JRuby][] 1.7+
|
215
|
+
* [Rubinius][] 2+
|
190
216
|
|
191
217
|
If something doesn't work on one of these Ruby versions, it's a bug.
|
192
218
|
|
@@ -206,11 +232,14 @@ of a major release, support for that Ruby version may be dropped.
|
|
206
232
|
Copyright (c) 2009-2013 [Rick Olson](mailto:technoweenie@gmail.com), Zack Hobson.
|
207
233
|
See [LICENSE][] for details.
|
208
234
|
|
209
|
-
[
|
210
|
-
[
|
211
|
-
[
|
212
|
-
[
|
235
|
+
[net_http]: http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/Net/HTTP.html
|
236
|
+
[persistent]: https://github.com/drbrain/net-http-persistent
|
237
|
+
[travis]: http://travis-ci.org/lostisland/faraday
|
238
|
+
[excon]: https://github.com/geemus/excon#readme
|
239
|
+
[typhoeus]: https://github.com/typhoeus/typhoeus#readme
|
240
|
+
[patron]: http://toland.github.com/patron/
|
213
241
|
[eventmachine]: https://github.com/igrigorik/em-http-request#readme
|
214
|
-
[
|
215
|
-
[
|
216
|
-
[
|
242
|
+
[httpclient]: https://github.com/nahi/httpclient
|
243
|
+
[jruby]: http://jruby.org/
|
244
|
+
[rubinius]: http://rubini.us/
|
245
|
+
[license]: LICENSE.md
|
data/lib/faraday.rb
CHANGED
@@ -70,6 +70,14 @@ module Faraday
|
|
70
70
|
else
|
71
71
|
raise Error::ConnectionFailed, err
|
72
72
|
end
|
73
|
+
rescue Errno::ETIMEDOUT => err
|
74
|
+
raise Error::TimeoutError, err
|
75
|
+
rescue RuntimeError => err
|
76
|
+
if err.message == "connection closed by server"
|
77
|
+
raise Error::ConnectionFailed, err
|
78
|
+
else
|
79
|
+
raise
|
80
|
+
end
|
73
81
|
rescue => err
|
74
82
|
if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
|
75
83
|
raise Faraday::SSLError, err
|
@@ -10,6 +10,9 @@ module Faraday
|
|
10
10
|
def call(env)
|
11
11
|
super
|
12
12
|
|
13
|
+
# enable compression
|
14
|
+
client.transparent_gzip_decompression = true
|
15
|
+
|
13
16
|
if req = env[:request]
|
14
17
|
if proxy = req[:proxy]
|
15
18
|
configure_proxy proxy
|
@@ -37,7 +40,7 @@ module Faraday
|
|
37
40
|
save_response env, resp.status, resp.body, resp.headers
|
38
41
|
|
39
42
|
@app.call env
|
40
|
-
rescue ::HTTPClient::TimeoutError
|
43
|
+
rescue ::HTTPClient::TimeoutError, Errno::ETIMEDOUT
|
41
44
|
raise Faraday::Error::TimeoutError, $!
|
42
45
|
rescue ::HTTPClient::BadResponseError => err
|
43
46
|
if err.message.include?('status 407')
|
@@ -69,14 +72,14 @@ module Faraday
|
|
69
72
|
|
70
73
|
def configure_ssl(ssl)
|
71
74
|
ssl_config = client.ssl_config
|
75
|
+
ssl_config.verify_mode = ssl_verify_mode(ssl)
|
76
|
+
ssl_config.cert_store = ssl_cert_store(ssl)
|
72
77
|
|
73
78
|
ssl_config.add_trust_ca ssl[:ca_file] if ssl[:ca_file]
|
74
79
|
ssl_config.add_trust_ca ssl[:ca_path] if ssl[:ca_path]
|
75
|
-
ssl_config.cert_store = ssl[:cert_store] if ssl[:cert_store]
|
76
80
|
ssl_config.client_cert = ssl[:client_cert] if ssl[:client_cert]
|
77
81
|
ssl_config.client_key = ssl[:client_key] if ssl[:client_key]
|
78
82
|
ssl_config.verify_depth = ssl[:verify_depth] if ssl[:verify_depth]
|
79
|
-
ssl_config.verify_mode = ssl_verify_mode(ssl)
|
80
83
|
end
|
81
84
|
|
82
85
|
def configure_timeouts(req)
|
@@ -92,6 +95,14 @@ module Faraday
|
|
92
95
|
end
|
93
96
|
end
|
94
97
|
|
98
|
+
def ssl_cert_store(ssl)
|
99
|
+
return ssl[:cert_store] if ssl[:cert_store]
|
100
|
+
# Use the default cert store by default, i.e. system ca certs
|
101
|
+
cert_store = OpenSSL::X509::Store.new
|
102
|
+
cert_store.set_default_paths
|
103
|
+
cert_store
|
104
|
+
end
|
105
|
+
|
95
106
|
def ssl_verify_mode(ssl)
|
96
107
|
ssl[:verify_mode] || begin
|
97
108
|
if ssl.fetch(:verify, true)
|
@@ -25,34 +25,36 @@ module Faraday
|
|
25
25
|
]
|
26
26
|
|
27
27
|
NET_HTTP_EXCEPTIONS << OpenSSL::SSL::SSLError if defined?(OpenSSL)
|
28
|
+
NET_HTTP_EXCEPTIONS << Net::OpenTimeout if defined?(Net::OpenTimeout)
|
28
29
|
|
29
30
|
def call(env)
|
30
31
|
super
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
32
|
+
with_net_http_connection(env) do |http|
|
33
|
+
configure_ssl(http, env[:ssl]) if env[:url].scheme == 'https' and env[:ssl]
|
34
|
+
|
35
|
+
req = env[:request]
|
36
|
+
http.read_timeout = http.open_timeout = req[:timeout] if req[:timeout]
|
37
|
+
http.open_timeout = req[:open_timeout] if req[:open_timeout]
|
38
|
+
|
39
|
+
begin
|
40
|
+
http_response = perform_request(http, env)
|
41
|
+
rescue *NET_HTTP_EXCEPTIONS => err
|
42
|
+
if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
|
43
|
+
raise Faraday::SSLError, err
|
44
|
+
else
|
45
|
+
raise Error::ConnectionFailed, err
|
46
|
+
end
|
45
47
|
end
|
46
|
-
end
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
49
|
+
save_response(env, http_response.code.to_i, http_response.body || '') do |response_headers|
|
50
|
+
http_response.each_header do |key, value|
|
51
|
+
response_headers[key] = value
|
52
|
+
end
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
56
|
@app.call env
|
55
|
-
rescue Timeout::Error => err
|
57
|
+
rescue Timeout::Error, Errno::ETIMEDOUT => err
|
56
58
|
raise Faraday::Error::TimeoutError, err
|
57
59
|
end
|
58
60
|
|
@@ -81,12 +83,16 @@ module Faraday
|
|
81
83
|
end
|
82
84
|
end
|
83
85
|
|
86
|
+
def with_net_http_connection(env)
|
87
|
+
yield net_http_connection(env)
|
88
|
+
end
|
89
|
+
|
84
90
|
def net_http_connection(env)
|
85
91
|
if proxy = env[:request][:proxy]
|
86
92
|
Net::HTTP::Proxy(proxy[:uri].host, proxy[:uri].port, proxy[:user], proxy[:password])
|
87
93
|
else
|
88
94
|
Net::HTTP
|
89
|
-
end.new(env[:url].host, env[:url].port)
|
95
|
+
end.new(env[:url].host, env[:url].port || (env[:url].scheme == 'https' ? 443 : 80))
|
90
96
|
end
|
91
97
|
|
92
98
|
def configure_ssl(http, ssl)
|
@@ -4,11 +4,10 @@
|
|
4
4
|
|
5
5
|
module Faraday
|
6
6
|
class Adapter
|
7
|
-
# Experimental adapter for net-http-persistent
|
8
7
|
class NetHttpPersistent < NetHttp
|
9
8
|
dependency 'net/http/persistent'
|
10
9
|
|
11
|
-
def
|
10
|
+
def with_net_http_connection(env)
|
12
11
|
if proxy = env[:request][:proxy]
|
13
12
|
proxy_uri = ::URI::HTTP === proxy[:uri] ? proxy[:uri].dup : ::URI.parse(proxy[:uri].to_s)
|
14
13
|
proxy_uri.user = proxy_uri.password = nil
|
@@ -18,11 +17,14 @@ module Faraday
|
|
18
17
|
define_method(:password) { proxy[:password] }
|
19
18
|
end if proxy[:user]
|
20
19
|
end
|
21
|
-
|
20
|
+
|
21
|
+
yield Net::HTTP::Persistent.new 'Faraday', proxy_uri
|
22
22
|
end
|
23
23
|
|
24
24
|
def perform_request(http, env)
|
25
25
|
http.request env[:url], create_request(env)
|
26
|
+
rescue Errno::ETIMEDOUT => error
|
27
|
+
raise Faraday::Error::TimeoutError, error
|
26
28
|
rescue Net::HTTP::Persistent::Error => error
|
27
29
|
if error.message.include? 'Timeout'
|
28
30
|
raise Faraday::Error::TimeoutError, error
|
@@ -39,7 +39,11 @@ module Faraday
|
|
39
39
|
|
40
40
|
@app.call env
|
41
41
|
rescue ::Patron::TimeoutError => err
|
42
|
-
|
42
|
+
if err.message == "Connection time-out"
|
43
|
+
raise Faraday::Error::ConnectionFailed, err
|
44
|
+
else
|
45
|
+
raise Faraday::Error::TimeoutError, err
|
46
|
+
end
|
43
47
|
rescue ::Patron::Error => err
|
44
48
|
if err.message.include?("code 407")
|
45
49
|
raise Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
|
@@ -52,8 +56,14 @@ module Faraday
|
|
52
56
|
# HAX: helps but doesn't work completely
|
53
57
|
# https://github.com/toland/patron/issues/34
|
54
58
|
::Patron::Request::VALID_ACTIONS.tap do |actions|
|
55
|
-
|
56
|
-
|
59
|
+
if actions[0].is_a?(Symbol)
|
60
|
+
actions << :patch unless actions.include? :patch
|
61
|
+
actions << :options unless actions.include? :options
|
62
|
+
else
|
63
|
+
# Patron 0.4.20 and up
|
64
|
+
actions << "PATCH" unless actions.include? "PATCH"
|
65
|
+
actions << "OPTIONS" unless actions.include? "OPTIONS"
|
66
|
+
end
|
57
67
|
end
|
58
68
|
end
|
59
69
|
|
data/lib/faraday/autoload.rb
CHANGED
@@ -69,7 +69,6 @@ module Faraday
|
|
69
69
|
:UrlEncoded => 'url_encoded',
|
70
70
|
:Multipart => 'multipart',
|
71
71
|
:Retry => 'retry',
|
72
|
-
:Timeout => 'timeout',
|
73
72
|
:Authorization => 'authorization',
|
74
73
|
:BasicAuthentication => 'basic_authentication',
|
75
74
|
:TokenAuthentication => 'token_authentication',
|
data/lib/faraday/connection.rb
CHANGED
@@ -396,7 +396,7 @@ module Faraday
|
|
396
396
|
# of the resulting url (default: nil).
|
397
397
|
#
|
398
398
|
# Returns the resulting URI instance.
|
399
|
-
def build_exclusive_url(url = nil, params = nil)
|
399
|
+
def build_exclusive_url(url = nil, params = nil, params_encoder = nil)
|
400
400
|
url = nil if url.respond_to?(:empty?) and url.empty?
|
401
401
|
base = url_prefix
|
402
402
|
if url and base.path and base.path !~ /\/$/
|
@@ -404,7 +404,7 @@ module Faraday
|
|
404
404
|
base.path = base.path + '/' # ensure trailing slash
|
405
405
|
end
|
406
406
|
uri = url ? base + url : base
|
407
|
-
uri.query = params.to_query(options.params_encoder) if params
|
407
|
+
uri.query = params.to_query(params_encoder || options.params_encoder) if params
|
408
408
|
uri.query = nil if uri.query and uri.query.empty?
|
409
409
|
uri
|
410
410
|
end
|
@@ -413,7 +413,12 @@ module Faraday
|
|
413
413
|
#
|
414
414
|
# Returns a Faraday::Connection.
|
415
415
|
def dup
|
416
|
-
self.class.new(build_exclusive_url,
|
416
|
+
self.class.new(build_exclusive_url,
|
417
|
+
:headers => headers.dup,
|
418
|
+
:params => params.dup,
|
419
|
+
:builder => builder.dup,
|
420
|
+
:ssl => ssl.dup,
|
421
|
+
:request => options.dup)
|
417
422
|
end
|
418
423
|
|
419
424
|
# Internal: Yields username and password extracted from a URI if they both exist.
|
data/lib/faraday/options.rb
CHANGED
@@ -269,6 +269,15 @@ module Faraday
|
|
269
269
|
|
270
270
|
def_delegators :request, :params_encoder
|
271
271
|
|
272
|
+
# Public
|
273
|
+
def self.from(value)
|
274
|
+
env = super(value)
|
275
|
+
if value.respond_to?(:custom_members)
|
276
|
+
env.custom_members.update(value.custom_members)
|
277
|
+
end
|
278
|
+
env
|
279
|
+
end
|
280
|
+
|
272
281
|
# Public
|
273
282
|
def [](key)
|
274
283
|
if in_member_set?(key)
|
data/lib/faraday/parameters.rb
CHANGED
@@ -1,15 +1,10 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
1
3
|
module Faraday
|
2
4
|
module NestedParamsEncoder
|
3
|
-
|
4
|
-
|
5
|
-
|
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)
|
5
|
+
class << self
|
6
|
+
extend Forwardable
|
7
|
+
def_delegators :'Faraday::Utils', :escape, :unescape
|
13
8
|
end
|
14
9
|
|
15
10
|
def self.encode(params)
|
@@ -51,6 +46,8 @@ module Faraday
|
|
51
46
|
buffer << "#{to_query.call(new_parent, val)}&"
|
52
47
|
end
|
53
48
|
return buffer.chop
|
49
|
+
elsif value.nil?
|
50
|
+
return parent
|
54
51
|
else
|
55
52
|
encoded_value = escape(value)
|
56
53
|
return "#{parent}=#{encoded_value}"
|
@@ -68,65 +65,72 @@ module Faraday
|
|
68
65
|
|
69
66
|
def self.decode(query)
|
70
67
|
return nil if query == nil
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
68
|
+
|
69
|
+
params = {}
|
70
|
+
query.split("&").each do |pair|
|
71
|
+
next if pair.empty?
|
72
|
+
key, value = pair.split("=", 2)
|
73
|
+
key = unescape(key)
|
74
|
+
value = unescape(value.gsub(/\+/, ' ')) if value
|
75
|
+
|
76
|
+
subkeys = key.scan(/[^\[\]]+(?:\]?\[\])?/)
|
77
|
+
context = params
|
78
|
+
subkeys.each_with_index do |subkey, i|
|
79
|
+
is_array = subkey =~ /[\[\]]+\Z/
|
80
|
+
subkey = $` if is_array
|
81
|
+
last_subkey = i == subkeys.length - 1
|
82
|
+
|
83
|
+
if !last_subkey || is_array
|
84
|
+
value_type = is_array ? Array : Hash
|
85
|
+
if context[subkey] && !context[subkey].is_a?(value_type)
|
86
|
+
raise TypeError, "expected %s (got %s) for param `%s'" % [
|
87
|
+
value_type.name,
|
88
|
+
context[subkey].class.name,
|
89
|
+
subkey
|
90
|
+
]
|
91
|
+
end
|
92
|
+
context = (context[subkey] ||= value_type.new)
|
76
93
|
end
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
94
|
+
|
95
|
+
if context.is_a?(Array) && !is_array
|
96
|
+
if !context.last.is_a?(Hash) || context.last.has_key?(subkey)
|
97
|
+
context << {}
|
98
|
+
end
|
99
|
+
context = context.last
|
100
|
+
end
|
101
|
+
|
102
|
+
if last_subkey
|
103
|
+
if is_array
|
104
|
+
context << value
|
105
|
+
else
|
106
|
+
context[subkey] = value
|
107
|
+
end
|
82
108
|
end
|
83
|
-
else
|
84
|
-
hash
|
85
109
|
end
|
86
110
|
end
|
87
111
|
|
88
|
-
|
89
|
-
|
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
|
112
|
+
dehash(params, 0)
|
113
|
+
end
|
96
114
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
+
# Internal: convert a nested hash with purely numeric keys into an array.
|
116
|
+
# FIXME: this is not compatible with Rack::Utils.parse_nested_query
|
117
|
+
def self.dehash(hash, depth)
|
118
|
+
hash.each do |key, value|
|
119
|
+
hash[key] = dehash(value, depth + 1) if value.kind_of?(Hash)
|
120
|
+
end
|
121
|
+
|
122
|
+
if depth > 0 && !hash.empty? && hash.keys.all? { |k| k =~ /^\d+$/ }
|
123
|
+
hash.keys.sort.inject([]) { |all, key| all << hash[key] }
|
124
|
+
else
|
125
|
+
hash
|
115
126
|
end
|
116
127
|
end
|
117
128
|
end
|
118
129
|
|
119
130
|
module FlatParamsEncoder
|
120
|
-
|
121
|
-
|
122
|
-
|
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)
|
131
|
+
class << self
|
132
|
+
extend Forwardable
|
133
|
+
def_delegators :'Faraday::Utils', :escape, :unescape
|
130
134
|
end
|
131
135
|
|
132
136
|
def self.encode(params)
|