mirror of
https://github.com/lostisland/faraday.git
synced 2025-08-30 00:03:09 -04:00
rename Alice => Faraday.
This commit is contained in:
parent
86f5a2a83d
commit
67d619d780
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009 rick
|
||||
Copyright (c) 2009-* rick olson, zack hobson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
105
README.rdoc
105
README.rdoc
@ -1,79 +1,66 @@
|
||||
= faraday
|
||||
|
||||
Experiments in a REST API lib
|
||||
|
||||
Super alpha! Don't use it if you mind throwing away all the code when I change
|
||||
the API on a whim.
|
||||
Modular HTTP client library using middleware heavily inspired by Rack.
|
||||
|
||||
This mess is gonna get raw, like sushi. So, haters to the left.
|
||||
|
||||
== Usage
|
||||
|
||||
# uses Net/HTTP, no response parsing
|
||||
conn = Faraday::Connection.new("http://sushi.com")
|
||||
conn.extend Faraday::Adapter::NetHttp
|
||||
resp = conn.get("/sake.json")
|
||||
resp.body # => %({"name":"Sake"})
|
||||
conn = Alice::Connection.new(:url => 'http://sushi.com') do |builder|
|
||||
builder.use Alice::Request::Yajl # convert body to json with Yajl lib
|
||||
builder.use Alice::Adapter::Logger # log the request somewhere?
|
||||
builder.use Alice::Adapter::Typhoeus # make http request with typhoeus
|
||||
builder.use Alice::Response::Yajl # # parse body with yajl
|
||||
|
||||
# uses Net/HTTP, Yajl parsing
|
||||
conn = Faraday::Connection.new("http://sushi.com")
|
||||
conn.extend Faraday::Adapter::NetHttp
|
||||
conn.response_class = Faraday::Response::YajlResponse
|
||||
resp = conn.get("/sake.json")
|
||||
resp.body # => {"name": "Sake"}
|
||||
# or use shortcuts
|
||||
builder.request :yajl # Alice::Request::Yajl
|
||||
builder.adapter :logger # Alice::Adapter::Logger
|
||||
builder.adapter :typhoeus # Alice::Adapter::Typhoeus
|
||||
builder.response :yajl # Alice::Response::Yajl
|
||||
end
|
||||
|
||||
# uses Typhoeus, no response parsing
|
||||
conn = Faraday::Connection.new("http://sushi.com")
|
||||
conn.extend Faraday::Adapter::Typhoeus
|
||||
resp = conn.get("/sake.json")
|
||||
resp.body # => %({"name":"Sake"})
|
||||
|
||||
# uses Typhoeus, Yajl parsing, performs requests in parallel
|
||||
conn = Faraday::Connection.new("http://sushi.com")
|
||||
conn.extend Faraday::Adapter::Typhoeus
|
||||
conn.response_class = Faraday::Response::YajlResponse
|
||||
resp1, resp2 = nil, nil
|
||||
conn.in_parallel do
|
||||
resp1 = conn.get("/sake.json")
|
||||
resp2 = conn.get("/unagi.json")
|
||||
|
||||
# requests have not been made yet
|
||||
resp1.body # => nil
|
||||
resp2.body # => nil
|
||||
end
|
||||
resp1.body # => {"name": "Sake"}
|
||||
resp2.body # => {"name": "Unagi"}
|
||||
resp1 = conn.get '/nigiri/sake.json'
|
||||
resp2 = conn.post do |req|
|
||||
req.url "/nigiri.json", :page => 2
|
||||
req[:content_type] = 'application/json'
|
||||
req.body = {:name => 'Unagi'}
|
||||
end
|
||||
|
||||
== Testing
|
||||
|
||||
* Yajl is needed for tests :(
|
||||
* Live Sinatra server is required for tests: `ruby test/live_server.rb` to start it.
|
||||
# It's possible to define stubbed request outside a test adapter block.
|
||||
stubs = Alice::Test::Stubs.new do |stub|
|
||||
stub.get('/tamago') { [200, 'egg', {} }
|
||||
end
|
||||
|
||||
=== Writing tests based on faraday
|
||||
|
||||
Using the MockRequest connection adapter you can implement your own test
|
||||
connection class:
|
||||
|
||||
# customize your own TestConnection or just use Faraday::TestConnection
|
||||
class TestConnection < Faraday::Connection
|
||||
include Faraday::Adapter::MockRequest
|
||||
# You can pass stubbed request to the test adapter or define them in a block
|
||||
# or a combination of the two.
|
||||
test = Alice::Connection.new do |builder|
|
||||
builder.adapter :test, stubs do |stub|
|
||||
stub.get('/ebi') {[ 200, 'shrimp', {} ]}
|
||||
end
|
||||
end
|
||||
|
||||
conn = TestConnection.new do |stub|
|
||||
# response mimics a rack response
|
||||
stub.get('/hello.json') { [200, {}, 'hi world'] }
|
||||
end
|
||||
resp = conn.get '/hello.json'
|
||||
resp.body # => 'hi world'
|
||||
resp = conn.get '/whatever' # => <not stubbed, raises connection error>
|
||||
# It's also possible to stub additional requests after the connection has
|
||||
# been initialized. This is useful for testing.
|
||||
stubs.get('/uni') {[ 200, 'urchin', {} ]}
|
||||
|
||||
resp = test.get '/tamago'
|
||||
resp.body # => 'egg'
|
||||
resp = test.get '/ebi'
|
||||
resp.body # => 'shrimp'
|
||||
resp = test.get '/uni'
|
||||
resp.body # => 'urchin'
|
||||
resp = test.get '/else' #=> raises "no such stub" error
|
||||
|
||||
== TODO
|
||||
|
||||
* other HTTP methods besides just GET
|
||||
* gracefully skip tests for Yajl and other optional libraries if they don't exist.
|
||||
* gracefully skip live http server tests if the sinatra server is not running.
|
||||
* use Typhoeus' request mocking facilities in the Typhoeus adapter test
|
||||
* lots of other crap
|
||||
* Add curb/em-http support
|
||||
* Add xml parsing
|
||||
* Support timeouts, proxy servers, ssl options
|
||||
* Add streaming requests and responses
|
||||
* Add default middleware load out for common cases
|
||||
* Add symbol => string index for mime types (:json => 'application/json')
|
||||
|
||||
== Note on Patches/Pull Requests
|
||||
|
||||
@ -87,4 +74,4 @@ connection class:
|
||||
|
||||
== Copyright
|
||||
|
||||
Copyright (c) 2009 rick. See LICENSE for details.
|
||||
Copyright (c) 2009-2010 rick, hobson. See LICENSE for details.
|
||||
|
@ -1,5 +1,16 @@
|
||||
require 'rack/utils'
|
||||
|
||||
module Faraday
|
||||
module AutoloadHelper
|
||||
def register_lookup_modules(mods)
|
||||
(@lookup_module_index ||= {}).update(mods)
|
||||
end
|
||||
|
||||
def lookup_module(key)
|
||||
return if !@lookup_module_index
|
||||
const_get @lookup_module_index[key] || key
|
||||
end
|
||||
|
||||
def autoload_all(prefix, options)
|
||||
options.each do |const_name, path|
|
||||
autoload const_name, File.join(prefix, path)
|
||||
@ -8,45 +19,41 @@ module Faraday
|
||||
|
||||
# Loads each autoloaded constant. If thread safety is a concern, wrap
|
||||
# this in a Mutex.
|
||||
def load
|
||||
def load_autoloaded_constants
|
||||
constants.each do |const|
|
||||
const_get(const) if autoload?(const)
|
||||
end
|
||||
end
|
||||
|
||||
def all_loaded_constants
|
||||
constants.map { |c| const_get(c) }.select { |a| a.loaded? }
|
||||
end
|
||||
end
|
||||
|
||||
extend AutoloadHelper
|
||||
|
||||
autoload_all 'faraday',
|
||||
:Connection => 'connection',
|
||||
:TestConnection => 'test_connection',
|
||||
:Response => 'response',
|
||||
:Error => 'error',
|
||||
:Loadable => 'loadable'
|
||||
|
||||
module Request
|
||||
extend AutoloadHelper
|
||||
autoload_all 'faraday/request',
|
||||
:YajlRequest => 'yajl_request',
|
||||
:PostRequest => 'post_request'
|
||||
end
|
||||
:Connection => 'connection',
|
||||
:Middleware => 'middleware',
|
||||
:Builder => 'builder',
|
||||
:Request => 'request',
|
||||
:Response => 'response',
|
||||
:Error => 'error'
|
||||
|
||||
module Adapter
|
||||
extend AutoloadHelper
|
||||
autoload_all 'faraday/adapter',
|
||||
:NetHttp => 'net_http',
|
||||
:Typhoeus => 'typhoeus',
|
||||
:MockRequest => 'mock_request'
|
||||
autoload_all 'faraday/adapter',
|
||||
:NetHttp => 'net_http',
|
||||
:Typhoeus => 'typhoeus',
|
||||
:Patron => 'patron',
|
||||
:Test => 'test'
|
||||
|
||||
# Names of available adapters. Should not actually load them.
|
||||
def self.adapters
|
||||
constants
|
||||
end
|
||||
|
||||
# Array of Adapters. These have been loaded and confirmed to work (right gems, etc).
|
||||
def self.loaded_adapters
|
||||
adapters.map { |c| const_get(c) }.select { |a| a.loaded? }
|
||||
end
|
||||
register_lookup_modules \
|
||||
:test => :Test,
|
||||
:net_http => :NetHttp,
|
||||
:typhoeus => :Typhoeus,
|
||||
:patron => :patron,
|
||||
:net_http => :NetHttp
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,120 +0,0 @@
|
||||
module Faraday
|
||||
module Adapter
|
||||
module MockRequest
|
||||
extend Faraday::Connection::Options
|
||||
def self.loaded?() false end
|
||||
|
||||
include Faraday::Error # ConnectionFailed
|
||||
|
||||
class Stubs
|
||||
def initialize
|
||||
# {:get => [Stub, Stub]}
|
||||
@stack = {}
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
def empty?
|
||||
@stack.empty?
|
||||
end
|
||||
|
||||
def match(request_method, path, data, request_headers)
|
||||
return false if !@stack.key?(request_method)
|
||||
stub = @stack[request_method].detect { |stub| stub.matches?(path, data, request_headers) }
|
||||
@stack[request_method].delete(stub) if stub
|
||||
end
|
||||
|
||||
def get(path, request_headers = {}, &block)
|
||||
(@stack[:get] ||= []) << new_stub(path, {}, request_headers, block)
|
||||
end
|
||||
|
||||
def delete(path, request_headers = {}, &block)
|
||||
(@stack[:delete] ||= []) << new_stub(path, {}, request_headers, block)
|
||||
end
|
||||
|
||||
def post(path, data, request_headers = {}, &block)
|
||||
(@stack[:post] ||= []) << new_stub(path, data, request_headers, block)
|
||||
end
|
||||
|
||||
def put(path, data, request_headers = {}, &block)
|
||||
(@stack[:put] ||= []) << new_stub(path, data, request_headers, block)
|
||||
end
|
||||
|
||||
def new_stub(path, data, request_headers, block)
|
||||
status, response_headers, body = block.call
|
||||
Stub.new(path, request_headers, status, response_headers, body, data)
|
||||
end
|
||||
end
|
||||
|
||||
class Stub < Struct.new(:path, :request_headers, :status, :response_headers, :body, :data)
|
||||
def matches?(request_path, params, headers)
|
||||
return false if request_path != path
|
||||
return false if params != data
|
||||
return true if request_headers.empty?
|
||||
request_headers.each do |key, value|
|
||||
return false if headers[key] != value
|
||||
end
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def initialize &block
|
||||
super
|
||||
configure(&block) if block
|
||||
end
|
||||
|
||||
def configure
|
||||
yield stubs
|
||||
end
|
||||
|
||||
def stubs
|
||||
@stubs ||= Stubs.new
|
||||
end
|
||||
|
||||
def _get(uri, headers)
|
||||
raise ConnectionFailed, "no stubbed requests" if stubs.empty?
|
||||
if stub = @stubs.match(:get, uri.path, {}, headers)
|
||||
response_class.new do |resp|
|
||||
resp.headers = stub.response_headers
|
||||
resp.process stub.body
|
||||
end
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def _delete(uri, headers)
|
||||
raise ConnectionFailed, "no stubbed requests" if stubs.empty?
|
||||
if stub = @stubs.match(:delete, uri.path, {}, headers)
|
||||
response_class.new do |resp|
|
||||
resp.headers = stub.response_headers
|
||||
resp.process stub.body
|
||||
end
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
def _post(uri, data, headers)
|
||||
raise ConnectionFailed, "no stubbed requests" if stubs.empty?
|
||||
if stub = @stubs.match(:post, uri.path, data, headers)
|
||||
response_class.new do |resp|
|
||||
resp.headers = stub.response_headers
|
||||
resp.process stub.body
|
||||
end
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
def _put(uri, data, headers)
|
||||
raise ConnectionFailed, "no stubbed requests" if stubs.empty?
|
||||
if stub = @stubs.match(:put, uri.path, data, headers)
|
||||
response_class.new do |resp|
|
||||
resp.headers = stub.response_headers
|
||||
resp.process stub.body
|
||||
end
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,42 +1,28 @@
|
||||
require 'net/http'
|
||||
require 'cgi'
|
||||
module Faraday
|
||||
module Adapter
|
||||
module NetHttp
|
||||
extend Faraday::Connection::Options
|
||||
class NetHttp < Middleware
|
||||
def call(env)
|
||||
process_body_for_request(env)
|
||||
|
||||
def _perform(method, uri, data, request_headers)
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
response_class.new do |resp|
|
||||
http_resp = http.send_request(method, path_for(uri), data, request_headers)
|
||||
raise Faraday::Error::ResourceNotFound if http_resp.code == '404'
|
||||
resp.process http_resp.body
|
||||
http_resp.each_header do |key, value|
|
||||
resp.headers[key] = value
|
||||
end
|
||||
http = Net::HTTP.new(env[:url].host, env[:url].port)
|
||||
full_path = full_path_for(env[:url].path, env[:url].query, env[:url].fragment)
|
||||
http_resp = http.send_request(env[:method].to_s.upcase, full_path, env[:body], env[:request_headers])
|
||||
|
||||
resp_headers = {}
|
||||
http_resp.each_header do |key, value|
|
||||
resp_headers[key] = value
|
||||
end
|
||||
|
||||
env.update \
|
||||
:status => http_resp.code.to_i,
|
||||
:response_headers => resp_headers,
|
||||
:body => http_resp.body
|
||||
|
||||
@app.call env
|
||||
rescue Errno::ECONNREFUSED
|
||||
raise Faraday::Error::ConnectionFailed, "connection refused"
|
||||
raise Error::ConnectionFailed, "connection refused"
|
||||
end
|
||||
|
||||
def _put(uri, data, request_headers)
|
||||
request = request_class.new(data, request_headers)
|
||||
_perform('PUT', uri, request.body, request.headers)
|
||||
end
|
||||
|
||||
def _post(uri, data, request_headers)
|
||||
request = request_class.new(data, request_headers)
|
||||
_perform('POST', uri, request.body, request.headers)
|
||||
end
|
||||
|
||||
def _get(uri, request_headers)
|
||||
_perform('GET', uri, uri.query, request_headers)
|
||||
end
|
||||
|
||||
def _delete(uri, request_headers)
|
||||
_perform('DELETE', uri, uri.query, request_headers)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
33
lib/faraday/adapter/patron.rb
Normal file
33
lib/faraday/adapter/patron.rb
Normal file
@ -0,0 +1,33 @@
|
||||
module Faraday
|
||||
module Adapter
|
||||
class Patron < Middleware
|
||||
begin
|
||||
require 'patron'
|
||||
rescue LoadError => e
|
||||
self.load_error = e
|
||||
end
|
||||
|
||||
def call(env)
|
||||
process_body_for_request(env)
|
||||
|
||||
sess = ::Patron::Session.new
|
||||
args = [env[:method], env[:url].to_s, env[:request_headers]]
|
||||
if Faraday::Connection::METHODS_WITH_BODIES.include?(env[:method])
|
||||
args.insert(2, env[:body].to_s)
|
||||
end
|
||||
resp = sess.send *args
|
||||
|
||||
env.update \
|
||||
:status => resp.status,
|
||||
:response_headers => resp.headers.
|
||||
inject({}) { |memo, (k, v)| memo.update(k.downcase => v) },
|
||||
:body => resp.body
|
||||
env[:response].finish(env)
|
||||
|
||||
@app.call env
|
||||
rescue Errno::ECONNREFUSED
|
||||
raise Error::ConnectionFailed, "connection refused"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
96
lib/faraday/adapter/test.rb
Normal file
96
lib/faraday/adapter/test.rb
Normal file
@ -0,0 +1,96 @@
|
||||
module Faraday
|
||||
module Adapter
|
||||
# test = Faraday::Connection.new do
|
||||
# use Faraday::Adapter::Test do |stub|
|
||||
# stub.get '/nigiri/sake.json' do
|
||||
# [200, {}, 'hi world']
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# resp = test.get '/nigiri/sake.json'
|
||||
# resp.body # => 'hi world'
|
||||
#
|
||||
class Test < Middleware
|
||||
attr_accessor :stubs
|
||||
|
||||
def self.loaded?() false end
|
||||
|
||||
class Stubs
|
||||
def initialize
|
||||
# {:get => [Stub, Stub]}
|
||||
@stack = {}
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
def empty?
|
||||
@stack.empty?
|
||||
end
|
||||
|
||||
def match(request_method, path, body)
|
||||
return false if !@stack.key?(request_method)
|
||||
stub = @stack[request_method].detect { |stub| stub.matches?(path, body) }
|
||||
@stack[request_method].delete stub
|
||||
end
|
||||
|
||||
def get(path, &block)
|
||||
new_stub(:get, path, &block)
|
||||
end
|
||||
|
||||
def head(path, &block)
|
||||
new_stub(:head, path, &block)
|
||||
end
|
||||
|
||||
def post(path, body=nil, &block)
|
||||
new_stub(:post, path, body, &block)
|
||||
end
|
||||
|
||||
def put(path, body=nil, &block)
|
||||
new_stub(:put, path, body, &block)
|
||||
end
|
||||
|
||||
def delete(path, &block)
|
||||
new_stub(:delete, path, &block)
|
||||
end
|
||||
|
||||
def new_stub(request_method, path, body=nil, &block)
|
||||
(@stack[request_method] ||= []) << Stub.new(path, body, block)
|
||||
end
|
||||
end
|
||||
|
||||
class Stub < Struct.new(:path, :body, :block)
|
||||
def matches?(request_path, request_body)
|
||||
request_path == path && request_body == body
|
||||
end
|
||||
end
|
||||
|
||||
def initialize app, stubs=nil, &block
|
||||
super(app)
|
||||
@stubs = stubs || Stubs.new
|
||||
configure(&block) if block
|
||||
end
|
||||
|
||||
def configure
|
||||
yield stubs
|
||||
end
|
||||
|
||||
def request_uri url
|
||||
(url.path != "" ? url.path : "/") +
|
||||
(url.query ? "?#{url.query}" : "")
|
||||
end
|
||||
|
||||
def call(env)
|
||||
if stub = stubs.match(env[:method], request_uri(env[:url]), env[:body])
|
||||
status, headers, body = stub.block.call(env)
|
||||
env.update \
|
||||
:status => status,
|
||||
:response_headers => headers,
|
||||
:body => body
|
||||
else
|
||||
raise "no stubbed request for #{env[:method]} #{request_uri(env[:url])} #{env[:body]}"
|
||||
end
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,76 +1,60 @@
|
||||
module Faraday
|
||||
module Adapter
|
||||
module Typhoeus
|
||||
extend Faraday::Connection::Options
|
||||
class Typhoeus < Middleware
|
||||
self.supports_parallel_requests = true
|
||||
|
||||
def self.setup_parallel_manager(options = {})
|
||||
options.empty? ? ::Typhoeus::Hydra.hydra : ::Typhoeus::Hydra.new(options)
|
||||
end
|
||||
|
||||
begin
|
||||
require 'typhoeus'
|
||||
|
||||
def in_parallel?
|
||||
!!@parallel_manager
|
||||
end
|
||||
|
||||
def in_parallel(options = {})
|
||||
setup_parallel_manager(options)
|
||||
yield
|
||||
run_parallel_requests
|
||||
end
|
||||
|
||||
def setup_parallel_manager(options = {})
|
||||
@parallel_manager ||= ::Typhoeus::Hydra.new(options)
|
||||
end
|
||||
|
||||
def run_parallel_requests
|
||||
@parallel_manager.run
|
||||
@parallel_manager = nil
|
||||
end
|
||||
|
||||
def _post(uri, data, request_headers)
|
||||
request = request_class.new(data, request_headers)
|
||||
_perform(:post, uri, :headers => request.headers, :body => request.body)
|
||||
end
|
||||
|
||||
def _get(uri, request_headers)
|
||||
_perform(:get, uri, :headers => request_headers)
|
||||
end
|
||||
|
||||
def _put(uri, data, request_headers)
|
||||
request = request_class.new(data, request_headers)
|
||||
_perform(:put, uri, :headers => request.headers, :body => request.body)
|
||||
end
|
||||
|
||||
def _delete(uri, request_headers)
|
||||
_perform(:delete, uri, :headers => request_headers)
|
||||
end
|
||||
|
||||
def _perform method, uri, params
|
||||
response_class.new do |resp|
|
||||
is_async = in_parallel?
|
||||
setup_parallel_manager
|
||||
params[:method] = method
|
||||
req = ::Typhoeus::Request.new(uri.to_s, params)
|
||||
req.on_complete do |response|
|
||||
raise Faraday::Error::ResourceNotFound if response.code == 404
|
||||
resp.process!(response.body)
|
||||
resp.headers = parse_response_headers(response.headers)
|
||||
end
|
||||
@parallel_manager.queue(req)
|
||||
if !is_async then run_parallel_requests end
|
||||
end
|
||||
rescue Errno::ECONNREFUSED
|
||||
raise Faraday::Error::ConnectionFailed, "connection refused"
|
||||
end
|
||||
|
||||
def parse_response_headers(header_string)
|
||||
return {} unless header_string # XXX
|
||||
Hash[*header_string.split(/\r\n/).
|
||||
tap { |a| a.shift }. # drop the HTTP status line
|
||||
map! { |h| h.split(/:\s+/,2) }. # split key and value
|
||||
map! { |(k, v)| [k.downcase, v] }.flatten!]
|
||||
end
|
||||
rescue LoadError => e
|
||||
self.load_error = e
|
||||
end
|
||||
|
||||
def call(env)
|
||||
process_body_for_request(env)
|
||||
|
||||
hydra = env[:parallel_manager] || self.class.setup_parallel_manager
|
||||
req = ::Typhoeus::Request.new env[:url].to_s,
|
||||
:method => env[:method],
|
||||
:body => env[:body],
|
||||
:headers => env[:request_headers]
|
||||
|
||||
req.on_complete do |resp|
|
||||
env.update \
|
||||
:status => resp.code,
|
||||
:response_headers => parse_response_headers(resp.headers),
|
||||
:body => resp.body
|
||||
env[:response].finish(env)
|
||||
end
|
||||
|
||||
hydra.queue req
|
||||
|
||||
if !env[:parallel_manager]
|
||||
hydra.run
|
||||
end
|
||||
|
||||
@app.call env
|
||||
rescue Errno::ECONNREFUSED
|
||||
raise Error::ConnectionFailed, "connection refused"
|
||||
end
|
||||
|
||||
def in_parallel(options = {})
|
||||
@hydra = ::Typhoeus::Hydra.new(options)
|
||||
yield
|
||||
@hydra.run
|
||||
@hydra = nil
|
||||
end
|
||||
|
||||
def parse_response_headers(header_string)
|
||||
return {} unless header_string && !header_string.empty?
|
||||
Hash[*header_string.split(/\r\n/).
|
||||
tap { |a| a.shift }. # drop the HTTP status line
|
||||
map! { |h| h.split(/:\s+/,2) }. # split key and value
|
||||
map! { |(k, v)| [k.downcase, v] }.flatten!]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
57
lib/faraday/builder.rb
Normal file
57
lib/faraday/builder.rb
Normal file
@ -0,0 +1,57 @@
|
||||
module Faraday
|
||||
# Possibly going to extend this a bit.
|
||||
#
|
||||
# Faraday::Connection.new(:url => 'http://sushi.com') do
|
||||
# request :yajl # Faraday::Request::Yajl
|
||||
# adapter :logger # Faraday::Adapter::Logger
|
||||
# response :yajl # Faraday::Response::Yajl
|
||||
# end
|
||||
class Builder
|
||||
attr_accessor :handlers
|
||||
|
||||
def self.create_with_inner_app(&block)
|
||||
inner = lambda do |env|
|
||||
if !env[:parallel_manager]
|
||||
env[:response].finish(env)
|
||||
else
|
||||
env[:response]
|
||||
end
|
||||
end
|
||||
Builder.new(&block).tap { |builder| builder.run(inner) }
|
||||
end
|
||||
|
||||
def initialize
|
||||
@handlers = []
|
||||
yield self
|
||||
end
|
||||
|
||||
def run app
|
||||
@handlers.unshift app
|
||||
end
|
||||
|
||||
def to_app
|
||||
inner_app = @handlers.first
|
||||
@handlers[1..-1].inject(inner_app) { |app, middleware| middleware.call(app) }
|
||||
end
|
||||
|
||||
def use klass, *args, &block
|
||||
@handlers.unshift(lambda { |app| klass.new(app, *args, &block) })
|
||||
end
|
||||
|
||||
def request(key, *args, &block)
|
||||
use_symbol Faraday::Request, key, *args, &block
|
||||
end
|
||||
|
||||
def response(key, *args, &block)
|
||||
use_symbol Faraday::Response, key, *args, &block
|
||||
end
|
||||
|
||||
def adapter(key, *args, &block)
|
||||
use_symbol Faraday::Adapter, key, *args, &block
|
||||
end
|
||||
|
||||
def use_symbol(mod, key, *args, &block)
|
||||
use mod.lookup_module(key), *args, &block
|
||||
end
|
||||
end
|
||||
end
|
@ -1,119 +1,115 @@
|
||||
require 'addressable/uri'
|
||||
require 'set'
|
||||
|
||||
module Faraday
|
||||
class Connection
|
||||
module Options
|
||||
def load_error() @load_error end
|
||||
def load_error=(v) @load_error = v end
|
||||
def supports_async() @supports_async end
|
||||
def supports_async=(v) @supports_async = v end
|
||||
def loaded?() !@load_error end
|
||||
alias supports_async? supports_async
|
||||
end
|
||||
include Addressable, Rack::Utils
|
||||
|
||||
include Addressable
|
||||
HEADERS = Hash.new { |h, k| k.respond_to?(:to_str) ? k : k.to_s.capitalize }.update \
|
||||
:content_type => "Content-Type",
|
||||
:content_length => "Content-Length",
|
||||
:accept_charset => "Accept-Charset",
|
||||
:accept_encoding => "Accept-Encoding"
|
||||
HEADERS.values.each { |v| v.freeze }
|
||||
|
||||
attr_accessor :host, :port, :scheme, :params, :headers
|
||||
attr_reader :path_prefix
|
||||
METHODS = Set.new [:get, :post, :put, :delete, :head]
|
||||
METHODS_WITH_BODIES = Set.new [:post, :put]
|
||||
|
||||
attr_accessor :host, :port, :scheme, :params, :headers, :parallel_manager
|
||||
attr_reader :path_prefix, :builder
|
||||
|
||||
# :url
|
||||
# :params
|
||||
# :headers
|
||||
# :response
|
||||
def initialize(url = nil, options = {})
|
||||
def initialize(url = nil, options = {}, &block)
|
||||
if url.is_a?(Hash)
|
||||
options = url
|
||||
url = options[:url]
|
||||
end
|
||||
@response_class = options[:response]
|
||||
@params = options[:params] || {}
|
||||
@headers = options[:headers] || {}
|
||||
@headers = HeaderHash.new
|
||||
@params = {}
|
||||
@parallel_manager = options[:parallel]
|
||||
self.url_prefix = url if url
|
||||
end
|
||||
|
||||
def url_prefix=(url)
|
||||
uri = URI.parse(url)
|
||||
self.scheme = uri.scheme
|
||||
self.host = uri.host
|
||||
self.port = uri.port
|
||||
self.path_prefix = uri.path
|
||||
end
|
||||
|
||||
|
||||
# Override in a subclass, or include an adapter
|
||||
#
|
||||
# def _post(uri, post_params, headers)
|
||||
# end
|
||||
#
|
||||
def post(uri, params = {}, headers = {})
|
||||
_post build_uri(uri), build_params(params), build_headers(headers)
|
||||
end
|
||||
|
||||
# Override in a subclass, or include an adapter
|
||||
#
|
||||
# def _put(uri, post_params, headers)
|
||||
# end
|
||||
#
|
||||
def put(uri, params = {}, headers = {})
|
||||
_put build_uri(uri), build_params(params), build_headers(headers)
|
||||
end
|
||||
|
||||
# Override in a subclass, or include an adapter
|
||||
#
|
||||
# def _delete(uri, headers)
|
||||
# end
|
||||
#
|
||||
def delete(uri, params = {}, headers = {})
|
||||
_delete build_uri(uri, build_params(params)), build_headers(headers)
|
||||
end
|
||||
|
||||
# Override in a subclass, or include an adapter
|
||||
#
|
||||
# def _get(uri, headers)
|
||||
# end
|
||||
#
|
||||
def get(url, params = nil, headers = nil)
|
||||
uri = build_uri(url, build_params(params))
|
||||
_get(uri, build_headers(headers))
|
||||
end
|
||||
|
||||
def request_class
|
||||
@request_class || Request::PostRequest
|
||||
end
|
||||
|
||||
def response_class
|
||||
@response_class || Response
|
||||
end
|
||||
|
||||
def response_class=(v)
|
||||
if v.respond_to?(:loaded?) && !v.loaded?
|
||||
raise ArgumentError, "The response class: #{v.inspect} does not appear to be loaded."
|
||||
merge_params @params, options[:params] if options[:params]
|
||||
merge_headers @headers, options[:headers] if options[:headers]
|
||||
if block
|
||||
@builder = Builder.create_with_inner_app(&block)
|
||||
end
|
||||
@response_class = v
|
||||
end
|
||||
|
||||
def request_class=(v)
|
||||
if v.respond_to?(:loaded?) && !v.loaded?
|
||||
raise ArgumentError, "The request class: #{v.inspect} does not appear to be loaded."
|
||||
def get(url = nil, headers = nil, &block)
|
||||
run_request :get, url, nil, headers, &block
|
||||
end
|
||||
|
||||
def post(url = nil, body = nil, headers = nil, &block)
|
||||
run_request :post, url, body, headers, &block
|
||||
end
|
||||
|
||||
def put(url = nil, body = nil, headers = nil, &block)
|
||||
run_request :put, url, body, headers, &block
|
||||
end
|
||||
|
||||
def head(url = nil, headers = nil, &block)
|
||||
run_request :head, url, nil, headers, &block
|
||||
end
|
||||
|
||||
def delete(url = nil, headers = nil, &block)
|
||||
run_request :delete, url, nil, headers, &block
|
||||
end
|
||||
|
||||
def run_request(method, url, body, headers)
|
||||
if !METHODS.include?(method)
|
||||
raise ArgumentError, "unknown http method: #{method}"
|
||||
end
|
||||
|
||||
Request.run(self, method) do |req|
|
||||
req.url(url) if url
|
||||
req.headers.update(headers) if headers
|
||||
req.body = body if body
|
||||
yield req if block_given?
|
||||
end
|
||||
@request_class = v
|
||||
end
|
||||
|
||||
def in_parallel?
|
||||
!!@parallel_manager
|
||||
end
|
||||
|
||||
def in_parallel(options = {})
|
||||
@parallel_manager = true
|
||||
def in_parallel(manager)
|
||||
@parallel_manager = manager
|
||||
yield
|
||||
@parallel_manager = false
|
||||
@parallel_manager && @parallel_manager.run
|
||||
ensure
|
||||
@parallel_manager = nil
|
||||
end
|
||||
|
||||
def setup_parallel_manager(options = {})
|
||||
# return the assembled Rack application for this instance.
|
||||
def to_app
|
||||
@builder.to_app
|
||||
end
|
||||
|
||||
def run_parallel_requests
|
||||
# Parses the giving url with Addressable::URI and stores the individual
|
||||
# components in this connection. These components serve as defaults for
|
||||
# requests made by this connection.
|
||||
#
|
||||
# conn = Faraday::Connection.new { ... }
|
||||
# conn.url_prefix = "https://sushi.com/api"
|
||||
# conn.scheme # => https
|
||||
# conn.path_prefix # => "/api"
|
||||
#
|
||||
# conn.get("nigiri?page=2") # accesses https://sushi.com/api/nigiri
|
||||
#
|
||||
def url_prefix=(url)
|
||||
uri = URI.parse(url)
|
||||
self.scheme = uri.scheme
|
||||
self.host = uri.host
|
||||
self.port = uri.port
|
||||
self.path_prefix = uri.path
|
||||
if uri.query && !uri.query.empty?
|
||||
merge_params @params, parse_query(uri.query)
|
||||
end
|
||||
end
|
||||
|
||||
# Ensures that the path prefix always has a leading / and no trailing /
|
||||
def path_prefix=(value)
|
||||
if value
|
||||
value.chomp! "/"
|
||||
@ -122,54 +118,65 @@ module Faraday
|
||||
@path_prefix = value
|
||||
end
|
||||
|
||||
def build_uri(url, params = nil)
|
||||
uri = URI.parse(url)
|
||||
# Takes a relative url for a request and combines it with the defaults
|
||||
# set on the connection instance.
|
||||
#
|
||||
# conn = Faraday::Connection.new { ... }
|
||||
# conn.url_prefix = "https://sushi.com/api?token=abc"
|
||||
# conn.scheme # => https
|
||||
# conn.path_prefix # => "/api"
|
||||
#
|
||||
# conn.build_url("nigiri?page=2") # => https://sushi.com/api/nigiri?token=abc&page=2
|
||||
# conn.build_url("nigiri", :page => 2) # => https://sushi.com/api/nigiri?token=abc&page=2
|
||||
#
|
||||
def build_url(url, params = nil)
|
||||
uri = URI.parse(url.to_s)
|
||||
uri.scheme ||= @scheme
|
||||
uri.host ||= @host
|
||||
uri.port ||= @port
|
||||
if @path_prefix && uri.path !~ /^\//
|
||||
uri.path = "#{@path_prefix.size > 1 ? @path_prefix : nil}/#{uri.path}"
|
||||
end
|
||||
if params && !params.empty?
|
||||
uri.query = params_to_query(params)
|
||||
end
|
||||
replace_query(uri, params)
|
||||
uri
|
||||
end
|
||||
|
||||
def path_for(uri)
|
||||
uri.path.tap do |s|
|
||||
s << "?#{uri.query}" if uri.query
|
||||
s << "##{uri.fragment}" if uri.fragment
|
||||
def replace_query(uri, params)
|
||||
url_params = @params.dup
|
||||
if uri.query && !uri.query.empty?
|
||||
merge_params url_params, parse_query(uri.query)
|
||||
end
|
||||
if params && !params.empty?
|
||||
merge_params url_params, params
|
||||
end
|
||||
uri.query = url_params.empty? ? nil : build_query(url_params)
|
||||
uri
|
||||
end
|
||||
|
||||
# turns param keys into strings
|
||||
def merge_params(existing_params, new_params)
|
||||
new_params.each do |key, value|
|
||||
existing_params[key.to_s] = value
|
||||
end
|
||||
end
|
||||
|
||||
def build_params(existing)
|
||||
build_hash :params, existing
|
||||
end
|
||||
|
||||
def build_headers(existing)
|
||||
build_hash(:headers, existing).tap do |headers|
|
||||
headers.keys.each do |key|
|
||||
headers[key] = headers.delete(key).to_s
|
||||
end
|
||||
# turns headers keys and values into strings. Look up symbol keys in the
|
||||
# the HEADERS hash.
|
||||
#
|
||||
# h = merge_headers(HeaderHash.new, :content_type => 'text/plain')
|
||||
# h['Content-Type'] # = 'text/plain'
|
||||
#
|
||||
def merge_headers(existing_headers, new_headers)
|
||||
new_headers.each do |key, value|
|
||||
existing_headers[HEADERS[key]] = value.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def build_hash(method, existing)
|
||||
existing ? send(method).merge(existing) : send(method)
|
||||
end
|
||||
|
||||
def params_to_query(params)
|
||||
params.inject([]) do |memo, (key, val)|
|
||||
memo << "#{escape_for_querystring(key)}=#{escape_for_querystring(val)}"
|
||||
end.join("&")
|
||||
end
|
||||
|
||||
# Some servers convert +'s in URL query params to spaces.
|
||||
# Go ahead and encode it.
|
||||
def escape_for_querystring(s)
|
||||
URI.encode_component(s.to_s, Addressable::URI::CharacterClasses::QUERY).tap do |escaped|
|
||||
escaped.gsub! /\+/, "%2B"
|
||||
# Be sure to URI escape '+' symbols to %2B. Otherwise, they get interpreted
|
||||
# as spaces.
|
||||
def escape(s)
|
||||
s.to_s.gsub(/([^a-zA-Z0-9_.-]+)/n) do
|
||||
'%' << $1.unpack('H2'*bytesize($1)).join('%').tap { |c| c.upcase! }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,13 +0,0 @@
|
||||
module Faraday
|
||||
module Loadable
|
||||
def self.extended mod
|
||||
class << mod
|
||||
attr_accessor :load_error
|
||||
end
|
||||
end
|
||||
|
||||
def self.loaded?
|
||||
load_error.nil?
|
||||
end
|
||||
end
|
||||
end
|
54
lib/faraday/middleware.rb
Normal file
54
lib/faraday/middleware.rb
Normal file
@ -0,0 +1,54 @@
|
||||
module Faraday
|
||||
class Middleware
|
||||
include Rack::Utils
|
||||
|
||||
class << self
|
||||
attr_accessor :load_error, :supports_parallel_requests
|
||||
alias supports_parallel_requests? supports_parallel_requests
|
||||
|
||||
# valid parallel managers should respond to #run with no parameters.
|
||||
# otherwise, return a short wrapper around it.
|
||||
def setup_parallel_manager(options = {})
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def self.loaded?
|
||||
@load_error.nil?
|
||||
end
|
||||
|
||||
def initialize(app = nil)
|
||||
@app = app
|
||||
end
|
||||
|
||||
# assume that query and fragment are already encoded properly
|
||||
def full_path_for(path, query = nil, fragment = nil)
|
||||
full_path = path.dup
|
||||
if query && !query.empty?
|
||||
full_path << "?#{query}"
|
||||
end
|
||||
if fragment && !fragment.empty?
|
||||
full_path << "##{fragment}"
|
||||
end
|
||||
full_path
|
||||
end
|
||||
|
||||
def process_body_for_request(env)
|
||||
# if it's a string, pass it through
|
||||
return if env[:body].nil? || env[:body].empty? || !env[:body].respond_to?(:each_key)
|
||||
env[:request_headers]['Content-Type'] ||= 'application/x-www-form-urlencoded'
|
||||
env[:body] = create_form_params(env[:body])
|
||||
end
|
||||
|
||||
def create_form_params(params, base = nil)
|
||||
[].tap do |result|
|
||||
params.each_key do |key|
|
||||
key_str = base ? "#{base}[#{key}]" : key
|
||||
value = params[key]
|
||||
wee = (value.kind_of?(Hash) ? create_form_params(value, key_str) : "#{key_str}=#{escape(value.to_s)}")
|
||||
result << wee
|
||||
end
|
||||
end.join("&")
|
||||
end
|
||||
end
|
||||
end
|
77
lib/faraday/request.rb
Normal file
77
lib/faraday/request.rb
Normal file
@ -0,0 +1,77 @@
|
||||
module Faraday
|
||||
# Used to setup urls, params, headers, and the request body in a sane manner.
|
||||
#
|
||||
# @connection.post do |req|
|
||||
# req.url 'http://localhost', 'a' => '1' # 'http://localhost?a=1'
|
||||
# req.headers['b'] = '2' # header
|
||||
# req['b'] = '2' # header
|
||||
# req.body = 'abc'
|
||||
# end
|
||||
#
|
||||
class Request < Struct.new(:path, :params, :headers, :body)
|
||||
extend AutoloadHelper
|
||||
autoload_all 'faraday/request',
|
||||
:Yajl => 'yajl',
|
||||
:ActiveSupportJson => 'active_support_json'
|
||||
|
||||
register_lookup_modules \
|
||||
:yajl => :Yajl,
|
||||
:activesupport_json => :ActiveSupportJson,
|
||||
:rails_json => :ActiveSupportJson,
|
||||
:active_support_json => :ActiveSupportJson
|
||||
|
||||
def self.run(connection, request_method)
|
||||
req = create
|
||||
yield req if block_given?
|
||||
req.run(connection, request_method)
|
||||
end
|
||||
|
||||
def self.create
|
||||
req = new(nil, {}, {}, nil)
|
||||
yield req if block_given?
|
||||
req
|
||||
end
|
||||
|
||||
def url(path, params = {})
|
||||
self.path = path
|
||||
self.params = params
|
||||
end
|
||||
|
||||
def [](key)
|
||||
headers[key]
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
headers[key] = value
|
||||
end
|
||||
|
||||
# ENV Keys
|
||||
# :method - a symbolized request method (:get, :post)
|
||||
# :body - the request body that will eventually be converted to a string.
|
||||
# :url - Addressable::URI instance of the URI for the current request.
|
||||
# :status - HTTP response status code
|
||||
# :request_headers - hash of HTTP Headers to be sent to the server
|
||||
# :response_headers - Hash of HTTP headers from the server
|
||||
# :parallel_manager - sent if the connection is in parallel mode
|
||||
# :response - the actual response object that stores the rack response
|
||||
def to_env_hash(connection, request_method)
|
||||
env_headers = connection.headers.dup
|
||||
env_params = connection.params.dup
|
||||
connection.merge_headers env_headers, headers
|
||||
connection.merge_params env_params, params
|
||||
|
||||
{ :method => request_method,
|
||||
:body => body,
|
||||
:url => connection.build_url(path, env_params),
|
||||
:request_headers => env_headers,
|
||||
:parallel_manager => connection.parallel_manager,
|
||||
:response => Response.new}
|
||||
end
|
||||
|
||||
def run(connection, request_method)
|
||||
app = connection.to_app
|
||||
env = to_env_hash(connection, request_method)
|
||||
app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
20
lib/faraday/request/active_support_json.rb
Normal file
20
lib/faraday/request/active_support_json.rb
Normal file
@ -0,0 +1,20 @@
|
||||
module Faraday
|
||||
class Request::ActiveSupportJson < Faraday::Middleware
|
||||
begin
|
||||
if !defined?(ActiveSupport::JSON)
|
||||
require 'active_support'
|
||||
end
|
||||
|
||||
rescue LoadError => e
|
||||
self.load_error = e
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:request_headers]['Content-Type'] = 'application/json'
|
||||
if env[:body] && !env[:body].respond_to?(:to_str)
|
||||
env[:body] = ActiveSupport::JSON.encode env[:body]
|
||||
end
|
||||
@app.call env
|
||||
end
|
||||
end
|
||||
end
|
@ -1,30 +0,0 @@
|
||||
module Faraday
|
||||
module Request
|
||||
class PostRequest
|
||||
extend Loadable
|
||||
|
||||
def initialize params, headers={}
|
||||
@params = params
|
||||
@headers = headers
|
||||
end
|
||||
|
||||
def headers
|
||||
@headers.merge('Content-Type' => 'application/x-www-form-urlencoded')
|
||||
end
|
||||
|
||||
def body
|
||||
create_post_params @params
|
||||
end
|
||||
|
||||
private
|
||||
def create_post_params(params, base = "")
|
||||
[].tap do |toreturn|
|
||||
params.each_key do |key|
|
||||
keystring = base == '' ? key : "#{base}[#{key}]"
|
||||
toreturn << (params[key].kind_of?(Hash) ? create_post_params(params[key], keystring) : "#{keystring}=#{CGI.escape(params[key].to_s)}")
|
||||
end
|
||||
end.join('&')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
18
lib/faraday/request/yajl.rb
Normal file
18
lib/faraday/request/yajl.rb
Normal file
@ -0,0 +1,18 @@
|
||||
module Faraday
|
||||
class Request::Yajl < Faraday::Middleware
|
||||
begin
|
||||
require 'yajl'
|
||||
|
||||
rescue LoadError => e
|
||||
self.load_error = e
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:request_headers]['Content-Type'] = 'application/json'
|
||||
if env[:body] && !env[:body].respond_to?(:to_str)
|
||||
env[:body] = Yajl::Encoder.encode env[:body]
|
||||
end
|
||||
@app.call env
|
||||
end
|
||||
end
|
||||
end
|
@ -1,27 +0,0 @@
|
||||
module Faraday
|
||||
module Request
|
||||
class YajlRequest
|
||||
extend Loadable
|
||||
|
||||
begin
|
||||
require 'yajl'
|
||||
|
||||
def initialize params, headers={}
|
||||
@params = params
|
||||
@headers = headers
|
||||
end
|
||||
|
||||
def headers
|
||||
@headers.merge('Content-Type' => 'application/json')
|
||||
end
|
||||
|
||||
# TODO streaming
|
||||
def body
|
||||
Yajl::Encoder.encode @params
|
||||
end
|
||||
rescue LoadError => e
|
||||
self.load_error = e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,38 +1,53 @@
|
||||
module Faraday
|
||||
class Response < Struct.new(:headers, :body)
|
||||
extend Loadable
|
||||
class Response
|
||||
class Middleware < Faraday::Middleware
|
||||
self.load_error = :abstract
|
||||
|
||||
# Use a response callback in case the request is parallelized.
|
||||
#
|
||||
# env[:response].on_complete do |finished_env|
|
||||
# finished_env[:body] = do_stuff_to(finished_env[:body])
|
||||
# end
|
||||
#
|
||||
def self.register_on_complete(env)
|
||||
end
|
||||
|
||||
def call(env)
|
||||
self.class.register_on_complete(env)
|
||||
@app.call env
|
||||
end
|
||||
end
|
||||
|
||||
extend AutoloadHelper
|
||||
autoload_all 'faraday/response',
|
||||
:YajlResponse => 'yajl_response'
|
||||
|
||||
def initialize(headers = nil, body = nil)
|
||||
super(headers || {}, body)
|
||||
if block_given?
|
||||
yield self
|
||||
processed!
|
||||
end
|
||||
autoload_all 'faraday/response',
|
||||
:Yajl => 'yajl',
|
||||
:ActiveSupportJson => 'active_support_json'
|
||||
|
||||
register_lookup_modules \
|
||||
:yajl => :Yajl,
|
||||
:activesupport_json => :ActiveSupportJson,
|
||||
:rails_json => :ActiveSupportJson,
|
||||
:active_support_json => :ActiveSupportJson
|
||||
attr_accessor :status, :headers, :body
|
||||
|
||||
def initialize
|
||||
@status, @headers, @body = nil, nil, nil
|
||||
@on_complete_callbacks = []
|
||||
end
|
||||
|
||||
# TODO: process is a funky name. change it
|
||||
# processes a chunk of the streamed body.
|
||||
def process(chunk)
|
||||
if !body
|
||||
self.body = []
|
||||
end
|
||||
body << chunk
|
||||
def on_complete(&block)
|
||||
@on_complete_callbacks << block
|
||||
end
|
||||
|
||||
# Assume the given content is the full body, and not streamed.
|
||||
def process!(full_body)
|
||||
process(full_body)
|
||||
processed!
|
||||
def finish(env)
|
||||
@on_complete_callbacks.each { |c| c.call(env) }
|
||||
@status, @headers, @body = env[:status], env[:response_headers], env[:body]
|
||||
self
|
||||
end
|
||||
|
||||
# Signals the end of streamed content. Do whatever you need to clean up
|
||||
# the streamed body.
|
||||
def processed!
|
||||
self.body = body.join if body.respond_to?(:join)
|
||||
def success?
|
||||
status == 200
|
||||
end
|
||||
end
|
||||
end
|
||||
|
22
lib/faraday/response/active_support_json.rb
Normal file
22
lib/faraday/response/active_support_json.rb
Normal file
@ -0,0 +1,22 @@
|
||||
module Faraday
|
||||
class Response::ActiveSupportJson < Response::Middleware
|
||||
begin
|
||||
if !defined?(ActiveSupport::JSON)
|
||||
require 'active_support'
|
||||
end
|
||||
|
||||
def self.register_on_complete(env)
|
||||
env[:response].on_complete do |finished_env|
|
||||
finished_env[:body] = ActiveSupport::JSON.decode(finished_env[:body])
|
||||
end
|
||||
end
|
||||
rescue LoadError => e
|
||||
self.load_error = e
|
||||
end
|
||||
|
||||
def initialize(app)
|
||||
super
|
||||
@parser = nil
|
||||
end
|
||||
end
|
||||
end
|
20
lib/faraday/response/yajl.rb
Normal file
20
lib/faraday/response/yajl.rb
Normal file
@ -0,0 +1,20 @@
|
||||
module Faraday
|
||||
class Response::Yajl < Response::Middleware
|
||||
begin
|
||||
require 'yajl'
|
||||
|
||||
def self.register_on_complete(env)
|
||||
env[:response].on_complete do |finished_env|
|
||||
finished_env[:body] = Yajl::Parser.parse(finished_env[:body])
|
||||
end
|
||||
end
|
||||
rescue LoadError => e
|
||||
self.load_error = e
|
||||
end
|
||||
|
||||
def initialize(app)
|
||||
super
|
||||
@parser = nil
|
||||
end
|
||||
end
|
||||
end
|
@ -1,35 +0,0 @@
|
||||
module Faraday
|
||||
class Response
|
||||
class YajlResponse < Response
|
||||
attr_reader :body
|
||||
|
||||
begin
|
||||
require 'yajl'
|
||||
|
||||
def initialize(headers = nil, body = nil)
|
||||
super
|
||||
@parser = nil
|
||||
end
|
||||
|
||||
def process(chunk)
|
||||
if !@parser
|
||||
@parser = Yajl::Parser.new
|
||||
@parser.on_parse_complete = method(:object_parsed)
|
||||
end
|
||||
@parser << chunk
|
||||
end
|
||||
|
||||
def processed!
|
||||
@parser = nil
|
||||
end
|
||||
|
||||
def object_parsed(obj)
|
||||
@body = obj
|
||||
end
|
||||
|
||||
rescue LoadError => e
|
||||
self.load_error = e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,5 +0,0 @@
|
||||
module Faraday
|
||||
class TestConnection < Connection
|
||||
include Faraday::Adapter::MockRequest
|
||||
end
|
||||
end
|
@ -1,26 +0,0 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'helper'))
|
||||
|
||||
if Faraday::Adapter::Typhoeus.loaded?
|
||||
class TyphoeusTest < Faraday::TestCase
|
||||
describe "#parse_response_headers" do
|
||||
before do
|
||||
@conn = Object.new.extend(Faraday::Adapter::Typhoeus)
|
||||
end
|
||||
|
||||
it "leaves http status line out" do
|
||||
headers = @conn.parse_response_headers("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n")
|
||||
assert_equal %w(content-type), headers.keys
|
||||
end
|
||||
|
||||
it "parses lower-cased header name and value" do
|
||||
headers = @conn.parse_response_headers("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n")
|
||||
assert_equal 'text/html', headers['content-type']
|
||||
end
|
||||
|
||||
it "parses lower-cased header name and value with colon" do
|
||||
headers = @conn.parse_response_headers("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://sushi.com/\r\n\r\n")
|
||||
assert_equal 'http://sushi.com/', headers['location']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,118 +0,0 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
||||
|
||||
class AdapterTest < Faraday::TestCase
|
||||
before do
|
||||
@connection = Faraday::Connection.new(LIVE_SERVER)
|
||||
end
|
||||
|
||||
Faraday::Adapter.loaded_adapters.each do |adapter|
|
||||
describe "with #{adapter} adapter" do
|
||||
before do
|
||||
@connection.extend adapter
|
||||
end
|
||||
|
||||
describe "#delete" do
|
||||
it "retrieves the response body with YajlResponse" do
|
||||
@connection.response_class = Faraday::Response::YajlResponse
|
||||
assert_equal({'deleted' => true},
|
||||
@connection.delete('delete_with_json').body)
|
||||
end
|
||||
|
||||
it "send url-encoded params" do
|
||||
assert_equal('foobar', @connection.delete('delete_with_params', 'deleted' => 'foobar').body)
|
||||
end
|
||||
end
|
||||
|
||||
it "passes params" do
|
||||
@connection.params = {:a => 1}
|
||||
assert_equal "params[:a] == 1", @connection.get('params').body
|
||||
end
|
||||
|
||||
it "passes headers" do
|
||||
@connection.headers = {"X-Test" => 1}
|
||||
assert_equal "env[HTTP_X_TEST] == 1", @connection.get('headers').body
|
||||
end
|
||||
|
||||
it "retrieves the response body" do
|
||||
assert_equal 'hello world', @connection.get('hello_world').body
|
||||
end
|
||||
|
||||
describe "#put" do
|
||||
it "sends params" do
|
||||
assert_equal 'hello zack', @connection.put('hello', 'name' => 'zack').body
|
||||
end
|
||||
|
||||
it "retrieves the response body with YajlResponse" do
|
||||
@connection.response_class = Faraday::Response::YajlResponse
|
||||
assert_equal({'name' => 'zack'},
|
||||
@connection.put('echo_name', 'name' => 'zack').body)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#post" do
|
||||
it "sends params" do
|
||||
assert_equal 'hello zack', @connection.post('hello', 'name' => 'zack').body
|
||||
end
|
||||
|
||||
it "retrieves the response body with YajlResponse" do
|
||||
@connection.response_class = Faraday::Response::YajlResponse
|
||||
assert_equal({'name' => 'zack'},
|
||||
@connection.post('echo_name', 'name' => 'zack').body)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#get" do
|
||||
it "raises on 404" do
|
||||
assert_raise(Faraday::Error::ResourceNotFound) { @connection.get('/nothing') }
|
||||
end
|
||||
it "retrieves the response body" do
|
||||
assert_equal 'hello world', @connection.get('hello_world').body
|
||||
end
|
||||
|
||||
it "send url-encoded params" do
|
||||
assert_equal('hello zack', @connection.get('hello', 'name' => 'zack').body)
|
||||
end
|
||||
|
||||
it "retrieves the response body with YajlResponse" do
|
||||
@connection.response_class = Faraday::Response::YajlResponse
|
||||
assert_equal [1,2,3], @connection.get('json').body
|
||||
end
|
||||
|
||||
it "retrieves the response headers" do
|
||||
assert_equal 'text/html', @connection.get('hello_world').headers['content-type']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "async requests" do
|
||||
before do
|
||||
@connection.extend adapter
|
||||
end
|
||||
|
||||
it "clears parallel manager after running a single request" do
|
||||
assert !@connection.in_parallel?
|
||||
resp = @connection.get('hello_world')
|
||||
assert !@connection.in_parallel?
|
||||
assert_equal 'hello world', @connection.get('hello_world').body
|
||||
end
|
||||
|
||||
it "uses parallel manager to run multiple json requests" do
|
||||
resp1, resp2 = nil, nil
|
||||
|
||||
@connection.response_class = Faraday::Response::YajlResponse
|
||||
@connection.in_parallel do
|
||||
resp1 = @connection.get('json')
|
||||
resp2 = @connection.get('json')
|
||||
assert @connection.in_parallel?
|
||||
if adapter.supports_async?
|
||||
assert_nil resp1.body
|
||||
assert_nil resp2.body
|
||||
end
|
||||
end
|
||||
assert !@connection.in_parallel?
|
||||
assert_equal [1,2,3], resp1.body
|
||||
assert_equal [1,2,3], resp2.body
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
157
test/adapters/live_test.rb
Normal file
157
test/adapters/live_test.rb
Normal file
@ -0,0 +1,157 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'helper'))
|
||||
|
||||
if Faraday::TestCase::LIVE_SERVER
|
||||
module Adapters
|
||||
class LiveTest < Faraday::TestCase
|
||||
Faraday::Adapter.all_loaded_constants.each do |adapter|
|
||||
describe "with #{adapter} adapter" do
|
||||
before do
|
||||
@connection = Faraday::Connection.new LIVE_SERVER do
|
||||
use adapter
|
||||
end
|
||||
end
|
||||
|
||||
describe "#get" do
|
||||
it "raises on 404" do
|
||||
assert_raise(Faraday::Error::ResourceNotFound) { @connection.get('/nothing') }
|
||||
end
|
||||
|
||||
it "retrieves the response body" do
|
||||
assert_equal 'hello world', @connection.get('hello_world').body
|
||||
end
|
||||
|
||||
it "send url-encoded params" do
|
||||
resp = @connection.get do |req|
|
||||
req.url 'hello', 'name' => 'zack'
|
||||
end
|
||||
assert_equal('hello zack', resp.body)
|
||||
end
|
||||
|
||||
it "retrieves the response headers" do
|
||||
assert_equal 'text/html', @connection.get('hello_world').headers['content-type']
|
||||
end
|
||||
end
|
||||
|
||||
describe "#post" do
|
||||
it "raises on 404" do
|
||||
assert_raise(Faraday::Error::ResourceNotFound) { @connection.post('/nothing') }
|
||||
end
|
||||
|
||||
it "send url-encoded params" do
|
||||
resp = @connection.post do |req|
|
||||
req.url 'echo_name'
|
||||
req.body = {'name' => 'zack'}
|
||||
end
|
||||
assert_equal %("zack"), resp.body
|
||||
end
|
||||
|
||||
it "send url-encoded nested params" do
|
||||
resp = @connection.post do |req|
|
||||
req.url 'echo_name'
|
||||
req.body = {'name' => {'first' => 'zack'}}
|
||||
end
|
||||
assert_equal %({"first"=>"zack"}), resp.body
|
||||
end
|
||||
|
||||
it "retrieves the response headers" do
|
||||
assert_equal 'text/html', @connection.post('echo_name').headers['content-type']
|
||||
end
|
||||
end
|
||||
|
||||
# http://github.com/toland/patron/issues/#issue/9
|
||||
if ENV['FORCE'] || adapter != Faraday::Adapter::Patron
|
||||
describe "#put" do
|
||||
it "raises on 404" do
|
||||
assert_raise(Faraday::Error::ResourceNotFound) { @connection.put('/nothing') }
|
||||
end
|
||||
|
||||
it "send url-encoded params" do
|
||||
resp = @connection.put do |req|
|
||||
req.url 'echo_name'
|
||||
req.body = {'name' => 'zack'}
|
||||
end
|
||||
assert_equal %("zack"), resp.body
|
||||
end
|
||||
|
||||
it "send url-encoded nested params" do
|
||||
resp = @connection.put do |req|
|
||||
req.url 'echo_name'
|
||||
req.body = {'name' => {'first' => 'zack'}}
|
||||
end
|
||||
assert_equal %({"first"=>"zack"}), resp.body
|
||||
end
|
||||
|
||||
it "retrieves the response headers" do
|
||||
assert_equal 'text/html', @connection.put('echo_name').headers['content-type']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# http://github.com/pauldix/typhoeus/issues#issue/7
|
||||
if ENV['FORCE'] || adapter != Faraday::Adapter::Typhoeus
|
||||
describe "#head" do
|
||||
it "raises on 404" do
|
||||
assert_raise(Faraday::Error::ResourceNotFound) { @connection.head('/nothing') }
|
||||
end
|
||||
|
||||
it "send url-encoded params" do
|
||||
resp = @connection.head do |req|
|
||||
req.url 'hello', 'name' => 'zack'
|
||||
end
|
||||
assert_equal 'text/html', resp.headers['content-type']
|
||||
end
|
||||
|
||||
it "retrieves no response body" do
|
||||
assert_equal '', @connection.head('hello_world').body.to_s
|
||||
end
|
||||
|
||||
it "retrieves the response headers" do
|
||||
assert_equal 'text/html', @connection.head('hello_world').headers['content-type']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#delete" do
|
||||
it "raises on 404" do
|
||||
assert_raise(Faraday::Error::ResourceNotFound) { @connection.delete('/nothing') }
|
||||
end
|
||||
|
||||
it "retrieves the response headers" do
|
||||
assert_equal 'text/html', @connection.delete('delete_with_json').headers['content-type']
|
||||
end
|
||||
|
||||
it "retrieves the body" do
|
||||
assert_match /deleted/, @connection.delete('delete_with_json').body
|
||||
end
|
||||
end
|
||||
|
||||
describe "async requests" do
|
||||
it "clears parallel manager after running a single request" do
|
||||
assert !@connection.in_parallel?
|
||||
resp = @connection.get('hello_world')
|
||||
assert !@connection.in_parallel?
|
||||
assert_equal 'hello world', @connection.get('hello_world').body
|
||||
end
|
||||
|
||||
it "uses parallel manager to run multiple json requests" do
|
||||
resp1, resp2 = nil, nil
|
||||
|
||||
@connection.in_parallel(adapter.setup_parallel_manager) do
|
||||
resp1 = @connection.get('json')
|
||||
resp2 = @connection.get('json')
|
||||
if adapter.supports_parallel_requests?
|
||||
assert @connection.in_parallel?
|
||||
assert_nil resp1.body
|
||||
assert_nil resp2.body
|
||||
end
|
||||
end
|
||||
assert !@connection.in_parallel?
|
||||
assert_equal '[1,2,3]', resp1.body
|
||||
assert_equal '[1,2,3]', resp2.body
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
28
test/adapters/test_middleware_test.rb
Normal file
28
test/adapters/test_middleware_test.rb
Normal file
@ -0,0 +1,28 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'helper'))
|
||||
|
||||
module Adapters
|
||||
class TestMiddleware < Faraday::TestCase
|
||||
describe "Test Middleware with simple path" do
|
||||
before :all do
|
||||
@stubs = Faraday::Adapter::Test::Stubs.new
|
||||
@conn = Faraday::Connection.new do |builder|
|
||||
builder.adapter :test, @stubs
|
||||
end
|
||||
@stubs.get('/hello') { [200, {'Content-Type' => 'text/html'}, 'hello'] }
|
||||
@resp = @conn.get('/hello')
|
||||
end
|
||||
|
||||
it "sets status" do
|
||||
assert_equal 200, @resp.status
|
||||
end
|
||||
|
||||
it "sets headers" do
|
||||
assert_equal 'text/html', @resp.headers['Content-Type']
|
||||
end
|
||||
|
||||
it "sets body" do
|
||||
assert_equal 'hello', @resp.body
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
28
test/adapters/typhoeus_test.rb
Normal file
28
test/adapters/typhoeus_test.rb
Normal file
@ -0,0 +1,28 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'helper'))
|
||||
|
||||
if Faraday::Adapter::Typhoeus.loaded?
|
||||
module Adapters
|
||||
class TestTyphoeus < Faraday::TestCase
|
||||
describe "#parse_response_headers" do
|
||||
before do
|
||||
@adapter = Faraday::Adapter::Typhoeus.new
|
||||
end
|
||||
|
||||
it "leaves http status line out" do
|
||||
headers = @adapter.parse_response_headers("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n")
|
||||
assert_equal %w(content-type), headers.keys
|
||||
end
|
||||
|
||||
it "parses lower-cased header name and value" do
|
||||
headers = @adapter.parse_response_headers("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n")
|
||||
assert_equal 'text/html', headers['content-type']
|
||||
end
|
||||
|
||||
it "parses lower-cased header name and value with colon" do
|
||||
headers = @adapter.parse_response_headers("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://sushi.com/\r\n\r\n")
|
||||
assert_equal 'http://sushi.com/', headers['location']
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
49
test/connection_app_test.rb
Normal file
49
test/connection_app_test.rb
Normal file
@ -0,0 +1,49 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
||||
|
||||
class TestConnectionApps < Faraday::TestCase
|
||||
class TestAdapter
|
||||
def initialize(app)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
[200, {}, env[:test]]
|
||||
end
|
||||
end
|
||||
|
||||
class TestMiddleWare
|
||||
def initialize(app)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env[:test] = 'hi'
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
@conn = Faraday::Connection.new do |b|
|
||||
b.use TestMiddleWare
|
||||
b.use TestAdapter
|
||||
end
|
||||
end
|
||||
|
||||
describe "#builder" do
|
||||
it "is built from Faraday::Connection constructor" do
|
||||
assert_kind_of Faraday::Builder, @conn.builder
|
||||
assert_equal 3, @conn.builder.handlers.size
|
||||
end
|
||||
|
||||
it "adds middleware to the Builder stack" do
|
||||
assert_kind_of TestMiddleWare, @conn.builder.handlers[2].call(nil)
|
||||
assert_kind_of TestAdapter, @conn.builder.handlers[1].call(nil)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_app" do
|
||||
it "returns rack-compatible object" do
|
||||
assert @conn.to_app.respond_to?(:call)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,6 +1,6 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
||||
|
||||
class ConnectionTest < Faraday::TestCase
|
||||
class TestConnection < Faraday::TestCase
|
||||
describe "#initialize" do
|
||||
it "parses @host out of given url" do
|
||||
conn = Faraday::Connection.new "http://sushi.com"
|
||||
@ -39,92 +39,118 @@ class ConnectionTest < Faraday::TestCase
|
||||
|
||||
it "stores default params from options" do
|
||||
conn = Faraday::Connection.new :params => {:a => 1}
|
||||
assert_equal 1, conn.params[:a]
|
||||
assert_equal 1, conn.params['a']
|
||||
end
|
||||
|
||||
it "stores default params from uri" do
|
||||
conn = Faraday::Connection.new "http://sushi.com/fish?a=1", :params => {'b' => '2'}
|
||||
assert_equal '1', conn.params['a']
|
||||
assert_equal '2', conn.params['b']
|
||||
end
|
||||
|
||||
it "stores default headers from options" do
|
||||
conn = Faraday::Connection.new :headers => {:a => 1}
|
||||
assert_equal 1, conn.headers[:a]
|
||||
assert_equal '1', conn.headers['A']
|
||||
end
|
||||
end
|
||||
|
||||
describe "#build_uri" do
|
||||
describe "#build_url" do
|
||||
it "uses Connection#host as default URI host" do
|
||||
conn = Faraday::Connection.new
|
||||
conn.host = 'sushi.com'
|
||||
uri = conn.build_uri("/sake.html")
|
||||
uri = conn.build_url("/sake.html")
|
||||
assert_equal 'sushi.com', uri.host
|
||||
end
|
||||
|
||||
it "uses Connection#port as default URI port" do
|
||||
conn = Faraday::Connection.new
|
||||
conn.port = 23
|
||||
uri = conn.build_uri("http://sushi.com")
|
||||
uri = conn.build_url("http://sushi.com")
|
||||
assert_equal 23, uri.port
|
||||
end
|
||||
|
||||
it "uses Connection#scheme as default URI scheme" do
|
||||
conn = Faraday::Connection.new 'http://sushi.com'
|
||||
uri = conn.build_uri("/sake.html")
|
||||
uri = conn.build_url("/sake.html")
|
||||
assert_equal 'http', uri.scheme
|
||||
end
|
||||
|
||||
it "uses Connection#path_prefix to customize the path" do
|
||||
conn = Faraday::Connection.new
|
||||
conn.path_prefix = '/fish'
|
||||
uri = conn.build_uri("sake.html")
|
||||
uri = conn.build_url("sake.html")
|
||||
assert_equal '/fish/sake.html', uri.path
|
||||
end
|
||||
|
||||
it "uses '/' Connection#path_prefix to customize the path" do
|
||||
conn = Faraday::Connection.new
|
||||
conn.path_prefix = '/'
|
||||
uri = conn.build_uri("sake.html")
|
||||
uri = conn.build_url("sake.html")
|
||||
assert_equal '/sake.html', uri.path
|
||||
end
|
||||
|
||||
it "forces Connection#path_prefix to be absolute" do
|
||||
conn = Faraday::Connection.new
|
||||
conn.path_prefix = 'fish'
|
||||
uri = conn.build_uri("sake.html")
|
||||
uri = conn.build_url("sake.html")
|
||||
assert_equal '/fish/sake.html', uri.path
|
||||
end
|
||||
|
||||
it "ignores Connection#path_prefix trailing slash" do
|
||||
conn = Faraday::Connection.new
|
||||
conn.path_prefix = '/fish/'
|
||||
uri = conn.build_uri("sake.html")
|
||||
uri = conn.build_url("sake.html")
|
||||
assert_equal '/fish/sake.html', uri.path
|
||||
end
|
||||
|
||||
it "allows absolute URI to ignore Connection#path_prefix" do
|
||||
conn = Faraday::Connection.new
|
||||
conn.path_prefix = '/fish'
|
||||
uri = conn.build_uri("/sake.html")
|
||||
uri = conn.build_url("/sake.html")
|
||||
assert_equal '/sake.html', uri.path
|
||||
end
|
||||
|
||||
it "parses url/params into #path" do
|
||||
conn = Faraday::Connection.new
|
||||
uri = conn.build_uri("http://sushi.com/sake.html")
|
||||
uri = conn.build_url("http://sushi.com/sake.html")
|
||||
assert_equal '/sake.html', uri.path
|
||||
end
|
||||
|
||||
it "parses url/params into #query" do
|
||||
conn = Faraday::Connection.new
|
||||
uri = conn.build_uri("http://sushi.com/sake.html", 'a[b]' => '1 + 2')
|
||||
uri = conn.build_url("http://sushi.com/sake.html", 'a[b]' => '1 + 2')
|
||||
assert_equal "a%5Bb%5D=1%20%2B%202", uri.query
|
||||
end
|
||||
|
||||
it "mashes default params and given params together" do
|
||||
conn = Faraday::Connection.new 'http://sushi.com/api?token=abc', :params => {'format' => 'json'}
|
||||
url = conn.build_url("nigiri?page=1", :limit => 5)
|
||||
assert_match /limit=5/, url.query
|
||||
assert_match /page=1/, url.query
|
||||
assert_match /format=json/, url.query
|
||||
assert_match /token=abc/, url.query
|
||||
end
|
||||
|
||||
it "overrides default params with given params" do
|
||||
conn = Faraday::Connection.new 'http://sushi.com/api?token=abc', :params => {'format' => 'json'}
|
||||
url = conn.build_url("nigiri?page=1", :limit => 5, :token => 'def', :format => 'xml')
|
||||
assert_match /limit=5/, url.query
|
||||
assert_match /page=1/, url.query
|
||||
assert_match /format=xml/, url.query
|
||||
assert_match /token=def/, url.query
|
||||
assert_no_match /format=json/, url.query
|
||||
assert_no_match /token=abc/, url.query
|
||||
end
|
||||
|
||||
it "parses url into #host" do
|
||||
conn = Faraday::Connection.new
|
||||
uri = conn.build_uri("http://sushi.com/sake.html")
|
||||
uri = conn.build_url("http://sushi.com/sake.html")
|
||||
assert_equal "sushi.com", uri.host
|
||||
end
|
||||
|
||||
it "parses url into #port" do
|
||||
conn = Faraday::Connection.new
|
||||
uri = conn.build_uri("http://sushi.com/sake.html")
|
||||
uri = conn.build_url("http://sushi.com/sake.html")
|
||||
assert_nil uri.port
|
||||
end
|
||||
end
|
||||
@ -132,7 +158,10 @@ class ConnectionTest < Faraday::TestCase
|
||||
describe "#params_to_query" do
|
||||
it "converts hash of params to URI-escaped query string" do
|
||||
conn = Faraday::Connection.new
|
||||
assert_equal "a%5Bb%5D=1%20%2B%202", conn.params_to_query('a[b]' => '1 + 2')
|
||||
class << conn
|
||||
public :build_query
|
||||
end
|
||||
assert_equal "a%5Bb%5D=1%20%2B%202", conn.build_query('a[b]' => '1 + 2')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
35
test/env_test.rb
Normal file
35
test/env_test.rb
Normal file
@ -0,0 +1,35 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
||||
|
||||
class TestEnv < Faraday::TestCase
|
||||
describe "Request#create" do
|
||||
before :all do
|
||||
@conn = Faraday::Connection.new :url => 'http://sushi.com/api'
|
||||
@input = {
|
||||
:body => 'abc',
|
||||
:headers => {'Server' => 'Faraday'}}
|
||||
@env_setup = Faraday::Request.create do |req|
|
||||
req.url 'foo.json', 'a' => 1
|
||||
req['Server'] = 'Faraday'
|
||||
req.body = @input[:body]
|
||||
end
|
||||
@env = @env_setup.to_env_hash(@conn, :get)
|
||||
end
|
||||
|
||||
it "stores method in :method" do
|
||||
assert_equal :get, @env[:method]
|
||||
end
|
||||
|
||||
it "stores Addressable::URI in :url" do
|
||||
assert_equal 'http://sushi.com/api/foo.json?a=1', @env[:url].to_s
|
||||
end
|
||||
|
||||
it "stores headers in :headers" do
|
||||
assert_kind_of Rack::Utils::HeaderHash, @env[:request_headers]
|
||||
assert_equal @input[:headers], @env[:request_headers]
|
||||
end
|
||||
|
||||
it "stores body in :body" do
|
||||
assert_equal @input[:body], @env[:body]
|
||||
end
|
||||
end
|
||||
end
|
@ -1,6 +1,5 @@
|
||||
require 'rubygems'
|
||||
require 'context'
|
||||
require 'ruby-debug'
|
||||
if ENV['LEFTRIGHT']
|
||||
require 'leftright'
|
||||
end
|
||||
@ -17,6 +16,10 @@ end
|
||||
|
||||
module Faraday
|
||||
class TestCase < Test::Unit::TestCase
|
||||
LIVE_SERVER = 'http://localhost:4567'
|
||||
LIVE_SERVER = case ENV['LIVE']
|
||||
when /^http/ then ENV['LIVE']
|
||||
when nil then nil
|
||||
else 'http://localhost:4567'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -17,16 +17,12 @@ get '/hello' do
|
||||
"hello #{params[:name]}"
|
||||
end
|
||||
|
||||
put '/hello' do
|
||||
"hello #{params[:name]}"
|
||||
end
|
||||
|
||||
post '/echo_name' do
|
||||
%/{"name":#{params[:name].inspect}}/
|
||||
params[:name].inspect
|
||||
end
|
||||
|
||||
put '/echo_name' do
|
||||
%/{"name":#{params[:name].inspect}}/
|
||||
params[:name].inspect
|
||||
end
|
||||
|
||||
delete '/delete_with_json' do
|
||||
@ -36,12 +32,3 @@ end
|
||||
delete '/delete_with_params' do
|
||||
params[:deleted]
|
||||
end
|
||||
|
||||
get '/params' do
|
||||
%(params[:a] == #{params[:a]})
|
||||
end
|
||||
|
||||
get "/headers" do
|
||||
%(env[HTTP_X_TEST] == #{env["HTTP_X_TEST"]})
|
||||
end
|
||||
|
||||
|
19
test/request_middleware_test.rb
Normal file
19
test/request_middleware_test.rb
Normal file
@ -0,0 +1,19 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
||||
|
||||
class RequestMiddlewareTest < Faraday::TestCase
|
||||
describe "encoding json" do
|
||||
[:yajl, :rails_json].each do |key|
|
||||
encoder = Faraday::Request.lookup_module(key)
|
||||
next if !encoder.loaded?
|
||||
it "uses #{encoder}" do
|
||||
@connection = Faraday::Connection.new do |b|
|
||||
b.use encoder
|
||||
b.adapter :test do |stub|
|
||||
stub.post('echo_body', '{"a":1}') { |env| [200, {'Content-Type' => 'text/html'}, env[:body]] }
|
||||
end
|
||||
end
|
||||
assert_equal %({"a":1}), @connection.post('echo_body', :a => 1).body
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
21
test/response_middleware_test.rb
Normal file
21
test/response_middleware_test.rb
Normal file
@ -0,0 +1,21 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
||||
|
||||
class ResponseMiddlewareTest < Faraday::TestCase
|
||||
describe "parsing json" do
|
||||
[:yajl, :rails_json].each do |key|
|
||||
parser = Faraday::Response.lookup_module(key)
|
||||
next if !parser.loaded?
|
||||
it "uses #{parser}" do
|
||||
@connection = Faraday::Connection.new do |b|
|
||||
b.adapter :test do |stub|
|
||||
stub.get('json') { [200, {'Content-Type' => 'text/html'}, "[1,2,3]"] }
|
||||
end
|
||||
b.use parser
|
||||
end
|
||||
response = @connection.get('json')
|
||||
assert response.success?
|
||||
assert_equal [1,2,3], response.body
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,34 +0,0 @@
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
|
||||
|
||||
class ResponseTest < Faraday::TestCase
|
||||
describe "unloaded response class" do
|
||||
it "is not allowed to be set" do
|
||||
resp_class = Object.new
|
||||
def resp_class.loaded?() false end
|
||||
conn = Faraday::Connection.new
|
||||
assert_raises ArgumentError do
|
||||
conn.response_class = resp_class
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "TestConnection#get with default Faraday::Response class" do
|
||||
it "returns Faraday::Response" do
|
||||
conn = Faraday::TestConnection.new do |stub|
|
||||
stub.get('/hello') { [200, {}, 'hello world']}
|
||||
end
|
||||
resp = conn.get('/hello')
|
||||
assert_equal 'hello world', resp.body
|
||||
end
|
||||
end
|
||||
|
||||
describe "TestConnection#get with Faraday::YajlResponse class" do
|
||||
it "returns string body" do
|
||||
conn = Faraday::TestConnection.new do |stub|
|
||||
stub.get('/hello') { [200, {}, '[1,2,3]']}
|
||||
end
|
||||
conn.response_class = Faraday::Response::YajlResponse
|
||||
assert_equal [1,2,3], conn.get('/hello').body
|
||||
end
|
||||
end
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user