shin-faraday 0.4.7
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +84 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/faraday.gemspec +87 -0
- data/lib/faraday.rb +94 -0
- data/lib/faraday/adapter/action_dispatch.rb +33 -0
- data/lib/faraday/adapter/net_http.rb +59 -0
- data/lib/faraday/adapter/patron.rb +33 -0
- data/lib/faraday/adapter/test.rb +117 -0
- data/lib/faraday/adapter/typhoeus.rb +65 -0
- data/lib/faraday/builder.rb +82 -0
- data/lib/faraday/connection.rb +263 -0
- data/lib/faraday/error.rb +8 -0
- data/lib/faraday/middleware.rb +54 -0
- data/lib/faraday/request.rb +87 -0
- data/lib/faraday/request/active_support_json.rb +21 -0
- data/lib/faraday/request/yajl.rb +18 -0
- data/lib/faraday/response.rb +56 -0
- data/lib/faraday/response/active_support_json.rb +31 -0
- data/lib/faraday/response/nokogiri.rb +21 -0
- data/lib/faraday/response/yajl.rb +26 -0
- data/test/adapters/live_test.rb +141 -0
- data/test/adapters/test_middleware_test.rb +26 -0
- data/test/adapters/typhoeus_test.rb +26 -0
- data/test/connection_app_test.rb +60 -0
- data/test/connection_test.rb +284 -0
- data/test/env_test.rb +56 -0
- data/test/helper.rb +32 -0
- data/test/live_server.rb +34 -0
- data/test/request_middleware_test.rb +24 -0
- data/test/response_middleware_test.rb +44 -0
- metadata +142 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Middleware
|
3
|
+
include Rack::Utils
|
4
|
+
|
5
|
+
class << self
|
6
|
+
attr_accessor :load_error, :supports_parallel_requests
|
7
|
+
alias supports_parallel_requests? supports_parallel_requests
|
8
|
+
|
9
|
+
# valid parallel managers should respond to #run with no parameters.
|
10
|
+
# otherwise, return a short wrapper around it.
|
11
|
+
def setup_parallel_manager(options = {})
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.loaded?
|
17
|
+
@load_error.nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(app = nil)
|
21
|
+
@app = app
|
22
|
+
end
|
23
|
+
|
24
|
+
# assume that query and fragment are already encoded properly
|
25
|
+
def full_path_for(path, query = nil, fragment = nil)
|
26
|
+
full_path = path.dup
|
27
|
+
if query && !query.empty?
|
28
|
+
full_path << "?#{query}"
|
29
|
+
end
|
30
|
+
if fragment && !fragment.empty?
|
31
|
+
full_path << "##{fragment}"
|
32
|
+
end
|
33
|
+
full_path
|
34
|
+
end
|
35
|
+
|
36
|
+
def process_body_for_request(env)
|
37
|
+
# if it's a string, pass it through
|
38
|
+
return if env[:body].nil? || env[:body].empty? || !env[:body].respond_to?(:each_key)
|
39
|
+
env[:request_headers]['Content-Type'] ||= 'application/x-www-form-urlencoded'
|
40
|
+
env[:body] = create_form_params(env[:body])
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_form_params(params, base = nil)
|
44
|
+
[].tap do |result|
|
45
|
+
params.each_key do |key|
|
46
|
+
key_str = base ? "#{base}[#{key}]" : key
|
47
|
+
value = params[key]
|
48
|
+
wee = (value.kind_of?(Hash) ? create_form_params(value, key_str) : "#{key_str}=#{escape(value.to_s)}")
|
49
|
+
result << wee
|
50
|
+
end
|
51
|
+
end.join("&")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Faraday
|
2
|
+
# Used to setup urls, params, headers, and the request body in a sane manner.
|
3
|
+
#
|
4
|
+
# @connection.post do |req|
|
5
|
+
# req.url 'http://localhost', 'a' => '1' # 'http://localhost?a=1'
|
6
|
+
# req.headers['b'] = '2' # header
|
7
|
+
# req['b'] = '2' # header
|
8
|
+
# req.body = 'abc'
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
class Request < Struct.new(:path, :params, :headers, :body)
|
12
|
+
extend AutoloadHelper
|
13
|
+
autoload_all 'faraday/request',
|
14
|
+
:Yajl => 'yajl',
|
15
|
+
:ActiveSupportJson => 'active_support_json'
|
16
|
+
|
17
|
+
register_lookup_modules \
|
18
|
+
:yajl => :Yajl,
|
19
|
+
:activesupport_json => :ActiveSupportJson,
|
20
|
+
:rails_json => :ActiveSupportJson,
|
21
|
+
:active_support_json => :ActiveSupportJson
|
22
|
+
|
23
|
+
def self.run(connection, request_method)
|
24
|
+
req = create
|
25
|
+
yield req if block_given?
|
26
|
+
req.run(connection, request_method)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.create
|
30
|
+
req = new(nil, {}, {}, nil)
|
31
|
+
yield req if block_given?
|
32
|
+
req
|
33
|
+
end
|
34
|
+
|
35
|
+
def url(path, params = {})
|
36
|
+
self.path = path
|
37
|
+
self.params = params
|
38
|
+
end
|
39
|
+
|
40
|
+
def [](key)
|
41
|
+
headers[key]
|
42
|
+
end
|
43
|
+
|
44
|
+
def []=(key, value)
|
45
|
+
headers[key] = value
|
46
|
+
end
|
47
|
+
|
48
|
+
# ENV Keys
|
49
|
+
# :method - a symbolized request method (:get, :post)
|
50
|
+
# :body - the request body that will eventually be converted to a string.
|
51
|
+
# :url - Addressable::URI instance of the URI for the current request.
|
52
|
+
# :status - HTTP response status code
|
53
|
+
# :request_headers - hash of HTTP Headers to be sent to the server
|
54
|
+
# :response_headers - Hash of HTTP headers from the server
|
55
|
+
# :parallel_manager - sent if the connection is in parallel mode
|
56
|
+
# :response - the actual response object that stores the rack response
|
57
|
+
# :request - Hash of options for configuring the request.
|
58
|
+
# :timeout - open/read timeout Integer in seconds
|
59
|
+
# :open_timeout - read timeout Integer in seconds
|
60
|
+
# :proxy - Hash of proxy options
|
61
|
+
# :uri - Proxy Server URI
|
62
|
+
# :user - Proxy server username
|
63
|
+
# :password - Proxy server password
|
64
|
+
# :ssl - Hash of options for configuring SSL requests.
|
65
|
+
def to_env_hash(connection, request_method)
|
66
|
+
env_headers = connection.headers.dup
|
67
|
+
env_params = connection.params.dup
|
68
|
+
connection.merge_headers(env_headers, headers)
|
69
|
+
connection.merge_params(env_params, params)
|
70
|
+
|
71
|
+
{ :method => request_method,
|
72
|
+
:body => body,
|
73
|
+
:url => connection.build_url(path, env_params),
|
74
|
+
:request_headers => env_headers.update(headers),
|
75
|
+
:parallel_manager => connection.parallel_manager,
|
76
|
+
:response => Response.new,
|
77
|
+
:request => connection.options.merge(:proxy => connection.proxy),
|
78
|
+
:ssl => connection.ssl}
|
79
|
+
end
|
80
|
+
|
81
|
+
def run(connection, request_method)
|
82
|
+
app = connection.to_app
|
83
|
+
env = to_env_hash(connection, request_method)
|
84
|
+
app.call(env)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Request::ActiveSupportJson < Faraday::Middleware
|
3
|
+
begin
|
4
|
+
if !defined?(ActiveSupport::JSON)
|
5
|
+
require 'active_support'
|
6
|
+
ActiveSupport::JSON
|
7
|
+
end
|
8
|
+
|
9
|
+
rescue LoadError, NameError => e
|
10
|
+
self.load_error = e
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
env[:request_headers]['Content-Type'] = 'application/json'
|
15
|
+
if env[:body] && !env[:body].respond_to?(:to_str)
|
16
|
+
env[:body] = ActiveSupport::JSON.encode(env[:body])
|
17
|
+
end
|
18
|
+
@app.call env
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Request::Yajl < Faraday::Middleware
|
3
|
+
begin
|
4
|
+
require 'yajl'
|
5
|
+
|
6
|
+
rescue LoadError, NameError => e
|
7
|
+
self.load_error = e
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
env[:request_headers]['Content-Type'] = 'application/json'
|
12
|
+
if env[:body] && !env[:body].respond_to?(:to_str)
|
13
|
+
env[:body] = Yajl::Encoder.encode(env[:body])
|
14
|
+
end
|
15
|
+
@app.call env
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Response
|
3
|
+
class Middleware < Faraday::Middleware
|
4
|
+
self.load_error = :abstract
|
5
|
+
|
6
|
+
# Use a response callback in case the request is parallelized.
|
7
|
+
#
|
8
|
+
# env[:response].on_complete do |finished_env|
|
9
|
+
# finished_env[:body] = do_stuff_to(finished_env[:body])
|
10
|
+
# end
|
11
|
+
#
|
12
|
+
def self.register_on_complete(env)
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
self.class.register_on_complete(env)
|
17
|
+
@app.call env
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
extend AutoloadHelper
|
22
|
+
|
23
|
+
autoload_all 'faraday/response',
|
24
|
+
:Yajl => 'yajl',
|
25
|
+
:Nokogiri => 'nokogiri',
|
26
|
+
:ActiveSupportJson => 'active_support_json'
|
27
|
+
|
28
|
+
register_lookup_modules \
|
29
|
+
:yajl => :Yajl,
|
30
|
+
:nokogiri => :Nokogiri,
|
31
|
+
:activesupport_json => :ActiveSupportJson,
|
32
|
+
:rails_json => :ActiveSupportJson,
|
33
|
+
:active_support_json => :ActiveSupportJson
|
34
|
+
attr_accessor :status, :headers, :body
|
35
|
+
|
36
|
+
def initialize
|
37
|
+
@status, @headers, @body = nil, nil, nil
|
38
|
+
@on_complete_callbacks = []
|
39
|
+
end
|
40
|
+
|
41
|
+
def on_complete(&block)
|
42
|
+
@on_complete_callbacks << block
|
43
|
+
end
|
44
|
+
|
45
|
+
def finish(env)
|
46
|
+
env[:body] ||= ''
|
47
|
+
@on_complete_callbacks.each { |c| c.call(env) }
|
48
|
+
@status, @headers, @body = env[:status], env[:response_headers], env[:body]
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def success?
|
53
|
+
status == 200
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Response::ActiveSupportJson < Response::Middleware
|
3
|
+
begin
|
4
|
+
if !defined?(ActiveSupport::JSON)
|
5
|
+
require 'active_support'
|
6
|
+
ActiveSupport::JSON
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.register_on_complete(env)
|
10
|
+
env[:response].on_complete do |finished_env|
|
11
|
+
finished_env[:body] = parse(finished_env[:body])
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
rescue LoadError, NameError => e
|
16
|
+
self.load_error = e
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(app)
|
20
|
+
super
|
21
|
+
@parser = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.parse(body)
|
25
|
+
ActiveSupport::JSON.decode(body)
|
26
|
+
# TODO: Maybe rescue Exception can be better ...
|
27
|
+
rescue Yajl::ParseError => e
|
28
|
+
raise Faraday::Error::ParsingError
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Response::Nokogiri < Faraday::Response::Middleware
|
3
|
+
begin
|
4
|
+
require 'nokogiri'
|
5
|
+
|
6
|
+
def self.register_on_complete(env)
|
7
|
+
env[:response].on_complete do |finished_env|
|
8
|
+
finished_env[:body] = ::Nokogiri::XML(finished_env[:body])
|
9
|
+
end
|
10
|
+
end
|
11
|
+
rescue LoadError, NameError => e
|
12
|
+
self.load_error = e
|
13
|
+
raise e
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(app)
|
17
|
+
super
|
18
|
+
@parser = nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Response::Yajl < Response::Middleware
|
3
|
+
begin
|
4
|
+
require 'yajl'
|
5
|
+
|
6
|
+
def self.register_on_complete(env)
|
7
|
+
env[:response].on_complete do |finished_env|
|
8
|
+
finished_env[:body] = parse(finished_env[:body])
|
9
|
+
end
|
10
|
+
end
|
11
|
+
rescue LoadError, NameError => e
|
12
|
+
self.load_error = e
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(app)
|
16
|
+
super
|
17
|
+
@parser = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.parse(body)
|
21
|
+
Yajl::Parser.parse(body)
|
22
|
+
rescue Yajl::ParseError => e
|
23
|
+
raise Faraday::Error::ParsingError
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'helper'))
|
2
|
+
|
3
|
+
if Faraday::TestCase::LIVE_SERVER
|
4
|
+
module Adapters
|
5
|
+
class LiveTest < Faraday::TestCase
|
6
|
+
(Faraday::Adapter.all_loaded_constants + [:default]).each do |adapter|
|
7
|
+
define_method "test_#{adapter}_GET_retrieves_the_response_body" do
|
8
|
+
assert_equal 'hello world', create_connection(adapter).get('hello_world').body
|
9
|
+
end
|
10
|
+
|
11
|
+
define_method "test_#{adapter}_GET_send_url_encoded_params" do
|
12
|
+
resp = create_connection(adapter).get do |req|
|
13
|
+
req.url 'hello', 'name' => 'zack'
|
14
|
+
end
|
15
|
+
assert_equal('hello zack', resp.body)
|
16
|
+
end
|
17
|
+
|
18
|
+
define_method "test_#{adapter}_GET_retrieves_the_response_headers" do
|
19
|
+
assert_equal 'text/html', create_connection(adapter).get('hello_world').headers['content-type']
|
20
|
+
end
|
21
|
+
|
22
|
+
define_method "test_#{adapter}_POST_send_url_encoded_params" do
|
23
|
+
resp = create_connection(adapter).post do |req|
|
24
|
+
req.url 'echo_name'
|
25
|
+
req.body = {'name' => 'zack'}
|
26
|
+
end
|
27
|
+
assert_equal %("zack"), resp.body
|
28
|
+
end
|
29
|
+
|
30
|
+
define_method "test_#{adapter}_POST_send_url_encoded_nested_params" do
|
31
|
+
resp = create_connection(adapter).post do |req|
|
32
|
+
req.url 'echo_name'
|
33
|
+
req.body = {'name' => {'first' => 'zack'}}
|
34
|
+
end
|
35
|
+
assert_equal %({"first"=>"zack"}), resp.body
|
36
|
+
end
|
37
|
+
|
38
|
+
define_method "test_#{adapter}_POST_retrieves_the_response_headers" do
|
39
|
+
assert_equal 'text/html', create_connection(adapter).post('echo_name').headers['content-type']
|
40
|
+
end
|
41
|
+
|
42
|
+
# http://github.com/toland/patron/issues/#issue/9
|
43
|
+
if ENV['FORCE'] || adapter != Faraday::Adapter::Patron
|
44
|
+
define_method "test_#{adapter}_PUT_send_url_encoded_params" do
|
45
|
+
resp = create_connection(adapter).put do |req|
|
46
|
+
req.url 'echo_name'
|
47
|
+
req.body = {'name' => 'zack'}
|
48
|
+
end
|
49
|
+
assert_equal %("zack"), resp.body
|
50
|
+
end
|
51
|
+
|
52
|
+
define_method "test_#{adapter}_PUT_send_url_encoded_nested_params" do
|
53
|
+
resp = create_connection(adapter).put do |req|
|
54
|
+
req.url 'echo_name'
|
55
|
+
req.body = {'name' => {'first' => 'zack'}}
|
56
|
+
end
|
57
|
+
assert_equal %({"first"=>"zack"}), resp.body
|
58
|
+
end
|
59
|
+
|
60
|
+
define_method "test_#{adapter}_PUT_retrieves_the_response_headers" do
|
61
|
+
assert_equal 'text/html', create_connection(adapter).put('echo_name').headers['content-type']
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# http://github.com/pauldix/typhoeus/issues#issue/7
|
66
|
+
if ENV['FORCE'] || adapter != Faraday::Adapter::Typhoeus
|
67
|
+
define_method "test_#{adapter}_HEAD_send_url_encoded_params" do
|
68
|
+
resp = create_connection(adapter).head do |req|
|
69
|
+
req.url 'hello', 'name' => 'zack'
|
70
|
+
end
|
71
|
+
assert_equal 'text/html', resp.headers['content-type']
|
72
|
+
end
|
73
|
+
|
74
|
+
define_method "test_#{adapter}_HEAD_retrieves_no_response_body" do
|
75
|
+
assert_equal '', create_connection(adapter).head('hello_world').body.to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
define_method "test_#{adapter}_HEAD_retrieves_the_response_headers" do
|
79
|
+
assert_equal 'text/html', create_connection(adapter).head('hello_world').headers['content-type']
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
define_method "test_#{adapter}_DELETE_retrieves_the_response_headers" do
|
84
|
+
assert_equal 'text/html', create_connection(adapter).delete('delete_with_json').headers['content-type']
|
85
|
+
end
|
86
|
+
|
87
|
+
define_method "test_#{adapter}_DELETE_retrieves_the_body" do
|
88
|
+
assert_match /deleted/, create_connection(adapter).delete('delete_with_json').body
|
89
|
+
end
|
90
|
+
|
91
|
+
define_method "test_#{adapter}_async_requests_clear_parallel_manager_after_running_a_single_request" do
|
92
|
+
connection = create_connection(adapter)
|
93
|
+
assert !connection.in_parallel?
|
94
|
+
resp = connection.get('hello_world')
|
95
|
+
assert !connection.in_parallel?
|
96
|
+
assert_equal 'hello world', connection.get('hello_world').body
|
97
|
+
end
|
98
|
+
|
99
|
+
define_method "test_#{adapter}_async_requests_uses_parallel_manager_to_run_multiple_json_requests" do
|
100
|
+
resp1, resp2 = nil, nil
|
101
|
+
|
102
|
+
connection = create_connection(adapter)
|
103
|
+
adapter = real_adapter_for(adapter)
|
104
|
+
|
105
|
+
connection.in_parallel(adapter.setup_parallel_manager) do
|
106
|
+
resp1 = connection.get('json')
|
107
|
+
resp2 = connection.get('json')
|
108
|
+
if adapter.supports_parallel_requests?
|
109
|
+
assert connection.in_parallel?
|
110
|
+
assert_nil resp1.body
|
111
|
+
assert_nil resp2.body
|
112
|
+
end
|
113
|
+
end
|
114
|
+
assert !connection.in_parallel?
|
115
|
+
assert_equal '[1,2,3]', resp1.body
|
116
|
+
assert_equal '[1,2,3]', resp2.body
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def create_connection(adapter)
|
121
|
+
if adapter == :default
|
122
|
+
conn = Faraday.default_connection
|
123
|
+
conn.url_prefix = LIVE_SERVER
|
124
|
+
conn
|
125
|
+
else
|
126
|
+
Faraday::Connection.new LIVE_SERVER do |b|
|
127
|
+
b.use adapter
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def real_adapter_for(adapter)
|
133
|
+
if adapter == :default
|
134
|
+
Faraday::Adapter.lookup_module(Faraday.default_adapter)
|
135
|
+
else
|
136
|
+
adapter
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|