faraday 0.1.2 → 0.2.0
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 +1 -1
- data/README.rdoc +48 -61
- data/Rakefile +3 -1
- data/VERSION +1 -1
- data/faraday.gemspec +31 -14
- data/lib/faraday.rb +33 -26
- data/lib/faraday/adapter/net_http.rb +17 -31
- data/lib/faraday/adapter/patron.rb +33 -0
- data/lib/faraday/adapter/test.rb +96 -0
- data/lib/faraday/adapter/typhoeus.rb +43 -59
- data/lib/faraday/builder.rb +57 -0
- data/lib/faraday/connection.rb +112 -105
- data/lib/faraday/middleware.rb +54 -0
- data/lib/faraday/request.rb +77 -0
- data/lib/faraday/request/active_support_json.rb +20 -0
- data/lib/faraday/request/yajl.rb +18 -0
- data/lib/faraday/response.rb +41 -26
- data/lib/faraday/response/active_support_json.rb +22 -0
- data/lib/faraday/response/yajl.rb +20 -0
- data/test/adapters/live_test.rb +157 -0
- data/test/adapters/test_middleware_test.rb +28 -0
- data/test/adapters/typhoeus_test.rb +28 -0
- data/test/connection_app_test.rb +49 -0
- data/test/connection_test.rb +47 -18
- data/test/env_test.rb +35 -0
- data/test/helper.rb +5 -2
- data/test/live_server.rb +2 -15
- data/test/request_middleware_test.rb +19 -0
- data/test/response_middleware_test.rb +21 -0
- metadata +46 -16
- data/lib/faraday/adapter/mock_request.rb +0 -120
- data/lib/faraday/loadable.rb +0 -13
- data/lib/faraday/request/post_request.rb +0 -30
- data/lib/faraday/request/yajl_request.rb +0 -27
- data/lib/faraday/response/yajl_response.rb +0 -35
- data/lib/faraday/test_connection.rb +0 -5
- data/test/adapter/typhoeus_test.rb +0 -26
- data/test/adapter_test.rb +0 -118
- data/test/response_test.rb +0 -34
@@ -1,75 +1,59 @@
|
|
1
1
|
module Faraday
|
2
2
|
module Adapter
|
3
|
-
|
4
|
-
|
3
|
+
class Typhoeus < Middleware
|
4
|
+
self.supports_parallel_requests = true
|
5
|
+
|
6
|
+
def self.setup_parallel_manager(options = {})
|
7
|
+
options.empty? ? ::Typhoeus::Hydra.hydra : ::Typhoeus::Hydra.new(options)
|
8
|
+
end
|
5
9
|
|
6
10
|
begin
|
7
11
|
require 'typhoeus'
|
12
|
+
rescue LoadError => e
|
13
|
+
self.load_error = e
|
14
|
+
end
|
8
15
|
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
def in_parallel(options = {})
|
14
|
-
setup_parallel_manager(options)
|
15
|
-
yield
|
16
|
-
run_parallel_requests
|
17
|
-
end
|
18
|
-
|
19
|
-
def setup_parallel_manager(options = {})
|
20
|
-
@parallel_manager ||= ::Typhoeus::Hydra.new(options)
|
21
|
-
end
|
22
|
-
|
23
|
-
def run_parallel_requests
|
24
|
-
@parallel_manager.run
|
25
|
-
@parallel_manager = nil
|
26
|
-
end
|
27
|
-
|
28
|
-
def _post(uri, data, request_headers)
|
29
|
-
request = request_class.new(data, request_headers)
|
30
|
-
_perform(:post, uri, :headers => request.headers, :body => request.body)
|
31
|
-
end
|
16
|
+
def call(env)
|
17
|
+
process_body_for_request(env)
|
32
18
|
|
33
|
-
|
34
|
-
|
35
|
-
|
19
|
+
hydra = env[:parallel_manager] || self.class.setup_parallel_manager
|
20
|
+
req = ::Typhoeus::Request.new env[:url].to_s,
|
21
|
+
:method => env[:method],
|
22
|
+
:body => env[:body],
|
23
|
+
:headers => env[:request_headers]
|
36
24
|
|
37
|
-
|
38
|
-
|
39
|
-
|
25
|
+
req.on_complete do |resp|
|
26
|
+
env.update \
|
27
|
+
:status => resp.code,
|
28
|
+
:response_headers => parse_response_headers(resp.headers),
|
29
|
+
:body => resp.body
|
30
|
+
env[:response].finish(env)
|
40
31
|
end
|
41
32
|
|
42
|
-
|
43
|
-
_perform(:delete, uri, :headers => request_headers)
|
44
|
-
end
|
33
|
+
hydra.queue req
|
45
34
|
|
46
|
-
|
47
|
-
|
48
|
-
is_async = in_parallel?
|
49
|
-
setup_parallel_manager
|
50
|
-
params[:method] = method
|
51
|
-
req = ::Typhoeus::Request.new(uri.to_s, params)
|
52
|
-
req.on_complete do |response|
|
53
|
-
raise Faraday::Error::ResourceNotFound if response.code == 404
|
54
|
-
resp.process!(response.body)
|
55
|
-
resp.headers = parse_response_headers(response.headers)
|
56
|
-
end
|
57
|
-
@parallel_manager.queue(req)
|
58
|
-
if !is_async then run_parallel_requests end
|
59
|
-
end
|
60
|
-
rescue Errno::ECONNREFUSED
|
61
|
-
raise Faraday::Error::ConnectionFailed, "connection refused"
|
35
|
+
if !env[:parallel_manager]
|
36
|
+
hydra.run
|
62
37
|
end
|
63
38
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
39
|
+
@app.call env
|
40
|
+
rescue Errno::ECONNREFUSED
|
41
|
+
raise Error::ConnectionFailed, "connection refused"
|
42
|
+
end
|
43
|
+
|
44
|
+
def in_parallel(options = {})
|
45
|
+
@hydra = ::Typhoeus::Hydra.new(options)
|
46
|
+
yield
|
47
|
+
@hydra.run
|
48
|
+
@hydra = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse_response_headers(header_string)
|
52
|
+
return {} unless header_string && !header_string.empty?
|
53
|
+
Hash[*header_string.split(/\r\n/).
|
54
|
+
tap { |a| a.shift }. # drop the HTTP status line
|
55
|
+
map! { |h| h.split(/:\s+/,2) }. # split key and value
|
56
|
+
map! { |(k, v)| [k.downcase, v] }.flatten!]
|
73
57
|
end
|
74
58
|
end
|
75
59
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Faraday
|
2
|
+
# Possibly going to extend this a bit.
|
3
|
+
#
|
4
|
+
# Faraday::Connection.new(:url => 'http://sushi.com') do
|
5
|
+
# request :yajl # Faraday::Request::Yajl
|
6
|
+
# adapter :logger # Faraday::Adapter::Logger
|
7
|
+
# response :yajl # Faraday::Response::Yajl
|
8
|
+
# end
|
9
|
+
class Builder
|
10
|
+
attr_accessor :handlers
|
11
|
+
|
12
|
+
def self.create_with_inner_app(&block)
|
13
|
+
inner = lambda do |env|
|
14
|
+
if !env[:parallel_manager]
|
15
|
+
env[:response].finish(env)
|
16
|
+
else
|
17
|
+
env[:response]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
Builder.new(&block).tap { |builder| builder.run(inner) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@handlers = []
|
25
|
+
yield self
|
26
|
+
end
|
27
|
+
|
28
|
+
def run app
|
29
|
+
@handlers.unshift app
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_app
|
33
|
+
inner_app = @handlers.first
|
34
|
+
@handlers[1..-1].inject(inner_app) { |app, middleware| middleware.call(app) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def use klass, *args, &block
|
38
|
+
@handlers.unshift(lambda { |app| klass.new(app, *args, &block) })
|
39
|
+
end
|
40
|
+
|
41
|
+
def request(key, *args, &block)
|
42
|
+
use_symbol Faraday::Request, key, *args, &block
|
43
|
+
end
|
44
|
+
|
45
|
+
def response(key, *args, &block)
|
46
|
+
use_symbol Faraday::Response, key, *args, &block
|
47
|
+
end
|
48
|
+
|
49
|
+
def adapter(key, *args, &block)
|
50
|
+
use_symbol Faraday::Adapter, key, *args, &block
|
51
|
+
end
|
52
|
+
|
53
|
+
def use_symbol(mod, key, *args, &block)
|
54
|
+
use mod.lookup_module(key), *args, &block
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/faraday/connection.rb
CHANGED
@@ -1,119 +1,115 @@
|
|
1
1
|
require 'addressable/uri'
|
2
|
+
require 'set'
|
3
|
+
|
2
4
|
module Faraday
|
3
5
|
class Connection
|
4
|
-
|
5
|
-
def load_error() @load_error end
|
6
|
-
def load_error=(v) @load_error = v end
|
7
|
-
def supports_async() @supports_async end
|
8
|
-
def supports_async=(v) @supports_async = v end
|
9
|
-
def loaded?() !@load_error end
|
10
|
-
alias supports_async? supports_async
|
11
|
-
end
|
6
|
+
include Addressable, Rack::Utils
|
12
7
|
|
13
|
-
|
8
|
+
HEADERS = Hash.new { |h, k| k.respond_to?(:to_str) ? k : k.to_s.capitalize }.update \
|
9
|
+
:content_type => "Content-Type",
|
10
|
+
:content_length => "Content-Length",
|
11
|
+
:accept_charset => "Accept-Charset",
|
12
|
+
:accept_encoding => "Accept-Encoding"
|
13
|
+
HEADERS.values.each { |v| v.freeze }
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
METHODS = Set.new [:get, :post, :put, :delete, :head]
|
16
|
+
METHODS_WITH_BODIES = Set.new [:post, :put]
|
17
|
+
|
18
|
+
attr_accessor :host, :port, :scheme, :params, :headers, :parallel_manager
|
19
|
+
attr_reader :path_prefix, :builder
|
17
20
|
|
18
21
|
# :url
|
19
22
|
# :params
|
20
23
|
# :headers
|
21
|
-
|
22
|
-
def initialize(url = nil, options = {})
|
24
|
+
def initialize(url = nil, options = {}, &block)
|
23
25
|
if url.is_a?(Hash)
|
24
26
|
options = url
|
25
27
|
url = options[:url]
|
26
28
|
end
|
27
|
-
@
|
28
|
-
@params
|
29
|
-
@
|
29
|
+
@headers = HeaderHash.new
|
30
|
+
@params = {}
|
31
|
+
@parallel_manager = options[:parallel]
|
30
32
|
self.url_prefix = url if url
|
33
|
+
merge_params @params, options[:params] if options[:params]
|
34
|
+
merge_headers @headers, options[:headers] if options[:headers]
|
35
|
+
if block
|
36
|
+
@builder = Builder.create_with_inner_app(&block)
|
37
|
+
end
|
31
38
|
end
|
32
39
|
|
33
|
-
def
|
34
|
-
|
35
|
-
self.scheme = uri.scheme
|
36
|
-
self.host = uri.host
|
37
|
-
self.port = uri.port
|
38
|
-
self.path_prefix = uri.path
|
39
|
-
end
|
40
|
-
|
41
|
-
|
42
|
-
# Override in a subclass, or include an adapter
|
43
|
-
#
|
44
|
-
# def _post(uri, post_params, headers)
|
45
|
-
# end
|
46
|
-
#
|
47
|
-
def post(uri, params = {}, headers = {})
|
48
|
-
_post build_uri(uri), build_params(params), build_headers(headers)
|
49
|
-
end
|
50
|
-
|
51
|
-
# Override in a subclass, or include an adapter
|
52
|
-
#
|
53
|
-
# def _put(uri, post_params, headers)
|
54
|
-
# end
|
55
|
-
#
|
56
|
-
def put(uri, params = {}, headers = {})
|
57
|
-
_put build_uri(uri), build_params(params), build_headers(headers)
|
40
|
+
def get(url = nil, headers = nil, &block)
|
41
|
+
run_request :get, url, nil, headers, &block
|
58
42
|
end
|
59
43
|
|
60
|
-
|
61
|
-
|
62
|
-
# def _delete(uri, headers)
|
63
|
-
# end
|
64
|
-
#
|
65
|
-
def delete(uri, params = {}, headers = {})
|
66
|
-
_delete build_uri(uri, build_params(params)), build_headers(headers)
|
44
|
+
def post(url = nil, body = nil, headers = nil, &block)
|
45
|
+
run_request :post, url, body, headers, &block
|
67
46
|
end
|
68
47
|
|
69
|
-
|
70
|
-
|
71
|
-
# def _get(uri, headers)
|
72
|
-
# end
|
73
|
-
#
|
74
|
-
def get(url, params = nil, headers = nil)
|
75
|
-
uri = build_uri(url, build_params(params))
|
76
|
-
_get(uri, build_headers(headers))
|
48
|
+
def put(url = nil, body = nil, headers = nil, &block)
|
49
|
+
run_request :put, url, body, headers, &block
|
77
50
|
end
|
78
51
|
|
79
|
-
def
|
80
|
-
|
52
|
+
def head(url = nil, headers = nil, &block)
|
53
|
+
run_request :head, url, nil, headers, &block
|
81
54
|
end
|
82
55
|
|
83
|
-
def
|
84
|
-
|
56
|
+
def delete(url = nil, headers = nil, &block)
|
57
|
+
run_request :delete, url, nil, headers, &block
|
85
58
|
end
|
86
59
|
|
87
|
-
def
|
88
|
-
if
|
89
|
-
raise ArgumentError, "
|
60
|
+
def run_request(method, url, body, headers)
|
61
|
+
if !METHODS.include?(method)
|
62
|
+
raise ArgumentError, "unknown http method: #{method}"
|
90
63
|
end
|
91
|
-
@response_class = v
|
92
|
-
end
|
93
64
|
|
94
|
-
|
95
|
-
|
96
|
-
|
65
|
+
Request.run(self, method) do |req|
|
66
|
+
req.url(url) if url
|
67
|
+
req.headers.update(headers) if headers
|
68
|
+
req.body = body if body
|
69
|
+
yield req if block_given?
|
97
70
|
end
|
98
|
-
@request_class = v
|
99
71
|
end
|
100
72
|
|
101
73
|
def in_parallel?
|
102
74
|
!!@parallel_manager
|
103
75
|
end
|
104
76
|
|
105
|
-
def in_parallel(
|
106
|
-
@parallel_manager =
|
77
|
+
def in_parallel(manager)
|
78
|
+
@parallel_manager = manager
|
107
79
|
yield
|
108
|
-
@parallel_manager
|
80
|
+
@parallel_manager && @parallel_manager.run
|
81
|
+
ensure
|
82
|
+
@parallel_manager = nil
|
109
83
|
end
|
110
84
|
|
111
|
-
|
85
|
+
# return the assembled Rack application for this instance.
|
86
|
+
def to_app
|
87
|
+
@builder.to_app
|
112
88
|
end
|
113
89
|
|
114
|
-
|
90
|
+
# Parses the giving url with Addressable::URI and stores the individual
|
91
|
+
# components in this connection. These components serve as defaults for
|
92
|
+
# requests made by this connection.
|
93
|
+
#
|
94
|
+
# conn = Faraday::Connection.new { ... }
|
95
|
+
# conn.url_prefix = "https://sushi.com/api"
|
96
|
+
# conn.scheme # => https
|
97
|
+
# conn.path_prefix # => "/api"
|
98
|
+
#
|
99
|
+
# conn.get("nigiri?page=2") # accesses https://sushi.com/api/nigiri
|
100
|
+
#
|
101
|
+
def url_prefix=(url)
|
102
|
+
uri = URI.parse(url)
|
103
|
+
self.scheme = uri.scheme
|
104
|
+
self.host = uri.host
|
105
|
+
self.port = uri.port
|
106
|
+
self.path_prefix = uri.path
|
107
|
+
if uri.query && !uri.query.empty?
|
108
|
+
merge_params @params, parse_query(uri.query)
|
109
|
+
end
|
115
110
|
end
|
116
111
|
|
112
|
+
# Ensures that the path prefix always has a leading / and no trailing /
|
117
113
|
def path_prefix=(value)
|
118
114
|
if value
|
119
115
|
value.chomp! "/"
|
@@ -122,54 +118,65 @@ module Faraday
|
|
122
118
|
@path_prefix = value
|
123
119
|
end
|
124
120
|
|
125
|
-
|
126
|
-
|
121
|
+
# Takes a relative url for a request and combines it with the defaults
|
122
|
+
# set on the connection instance.
|
123
|
+
#
|
124
|
+
# conn = Faraday::Connection.new { ... }
|
125
|
+
# conn.url_prefix = "https://sushi.com/api?token=abc"
|
126
|
+
# conn.scheme # => https
|
127
|
+
# conn.path_prefix # => "/api"
|
128
|
+
#
|
129
|
+
# conn.build_url("nigiri?page=2") # => https://sushi.com/api/nigiri?token=abc&page=2
|
130
|
+
# conn.build_url("nigiri", :page => 2) # => https://sushi.com/api/nigiri?token=abc&page=2
|
131
|
+
#
|
132
|
+
def build_url(url, params = nil)
|
133
|
+
uri = URI.parse(url.to_s)
|
127
134
|
uri.scheme ||= @scheme
|
128
135
|
uri.host ||= @host
|
129
136
|
uri.port ||= @port
|
130
137
|
if @path_prefix && uri.path !~ /^\//
|
131
138
|
uri.path = "#{@path_prefix.size > 1 ? @path_prefix : nil}/#{uri.path}"
|
132
139
|
end
|
133
|
-
|
134
|
-
uri.query = params_to_query(params)
|
135
|
-
end
|
140
|
+
replace_query(uri, params)
|
136
141
|
uri
|
137
142
|
end
|
138
143
|
|
139
|
-
def
|
140
|
-
|
141
|
-
|
142
|
-
|
144
|
+
def replace_query(uri, params)
|
145
|
+
url_params = @params.dup
|
146
|
+
if uri.query && !uri.query.empty?
|
147
|
+
merge_params url_params, parse_query(uri.query)
|
143
148
|
end
|
144
|
-
|
145
|
-
|
146
|
-
def build_params(existing)
|
147
|
-
build_hash :params, existing
|
148
|
-
end
|
149
|
-
|
150
|
-
def build_headers(existing)
|
151
|
-
build_hash(:headers, existing).tap do |headers|
|
152
|
-
headers.keys.each do |key|
|
153
|
-
headers[key] = headers.delete(key).to_s
|
154
|
-
end
|
149
|
+
if params && !params.empty?
|
150
|
+
merge_params url_params, params
|
155
151
|
end
|
152
|
+
uri.query = url_params.empty? ? nil : build_query(url_params)
|
153
|
+
uri
|
156
154
|
end
|
157
155
|
|
158
|
-
|
159
|
-
|
156
|
+
# turns param keys into strings
|
157
|
+
def merge_params(existing_params, new_params)
|
158
|
+
new_params.each do |key, value|
|
159
|
+
existing_params[key.to_s] = value
|
160
|
+
end
|
160
161
|
end
|
161
162
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
163
|
+
# turns headers keys and values into strings. Look up symbol keys in the
|
164
|
+
# the HEADERS hash.
|
165
|
+
#
|
166
|
+
# h = merge_headers(HeaderHash.new, :content_type => 'text/plain')
|
167
|
+
# h['Content-Type'] # = 'text/plain'
|
168
|
+
#
|
169
|
+
def merge_headers(existing_headers, new_headers)
|
170
|
+
new_headers.each do |key, value|
|
171
|
+
existing_headers[HEADERS[key]] = value.to_s
|
172
|
+
end
|
166
173
|
end
|
167
174
|
|
168
|
-
#
|
169
|
-
#
|
170
|
-
def
|
171
|
-
|
172
|
-
|
175
|
+
# Be sure to URI escape '+' symbols to %2B. Otherwise, they get interpreted
|
176
|
+
# as spaces.
|
177
|
+
def escape(s)
|
178
|
+
s.to_s.gsub(/([^a-zA-Z0-9_.-]+)/n) do
|
179
|
+
'%' << $1.unpack('H2'*bytesize($1)).join('%').tap { |c| c.upcase! }
|
173
180
|
end
|
174
181
|
end
|
175
182
|
end
|