faraday 0.0.1

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 rick
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,88 @@
1
+ = faraday
2
+
3
+ Experiments in a REST API lib
4
+
5
+ Super alpha! Don't use it if you mind throwing away all the code when I change
6
+ the API on a whim.
7
+
8
+ This mess is gonna get raw, like sushi. So, haters to the left.
9
+
10
+ == Usage
11
+
12
+ # uses Net/HTTP, no response parsing
13
+ conn = Faraday::Connection.new("http://sushi.com")
14
+ conn.extend Faraday::Adapter::NetHttp
15
+ resp = conn.get("/sake.json")
16
+ resp.body # => %({"name":"Sake"})
17
+
18
+ # uses Net/HTTP, Yajl parsing
19
+ conn = Faraday::Connection.new("http://sushi.com")
20
+ conn.extend Faraday::Adapter::NetHttp
21
+ conn.response_class = Faraday::Response::YajlResponse
22
+ resp = conn.get("/sake.json")
23
+ resp.body # => {"name": "Sake"}
24
+
25
+ # uses Typhoeus, no response parsing
26
+ conn = Faraday::Connection.new("http://sushi.com")
27
+ conn.extend Faraday::Adapter::Typhoeus
28
+ resp = conn.get("/sake.json")
29
+ resp.body # => %({"name":"Sake"})
30
+
31
+ # uses Typhoeus, Yajl parsing, performs requests in parallel
32
+ conn = Faraday::Connection.new("http://sushi.com")
33
+ conn.extend Faraday::Adapter::Typhoeus
34
+ conn.response_class = Faraday::Response::YajlResponse
35
+ resp1, resp2 = nil, nil
36
+ conn.in_parallel do
37
+ resp1 = conn.get("/sake.json")
38
+ resp2 = conn.get("/unagi.json")
39
+
40
+ # requests have not been made yet
41
+ resp1.body # => nil
42
+ resp2.body # => nil
43
+ end
44
+ resp1.body # => {"name": "Sake"}
45
+ resp2.body # => {"name": "Unagi"}
46
+
47
+ == Testing
48
+
49
+ * Yajl is needed for tests :(
50
+ * Live Sinatra server is required for tests: `ruby test/live_server.rb` to start it.
51
+
52
+ === Writing tests based on faraday
53
+
54
+ Using the MockRequest connection adapter you can implement your own test
55
+ connection class:
56
+
57
+ class TestConnection < Faraday::Connection
58
+ extend Faraday::Adapter::MockRequest
59
+ end
60
+
61
+ conn = TestConnection.new do |stub|
62
+ stub.get('/hello.json', { 'hi world' }
63
+ end
64
+ resp = conn.get '/hello.json'
65
+ resp.body # => 'hi world'
66
+ resp = conn.get '/whatever' # => <not stubbed, raises connection error>
67
+
68
+ == TODO
69
+
70
+ * other HTTP methods besides just GET
71
+ * gracefully skip tests for Yajl and other optional libraries if they don't exist.
72
+ * gracefully skip live http server tests if the sinatra server is not running.
73
+ * use Typhoeus' request mocking facilities in the Typhoeus adapter test
74
+ * lots of other crap
75
+
76
+ == Note on Patches/Pull Requests
77
+
78
+ * Fork the project.
79
+ * Make your feature addition or bug fix.
80
+ * Add tests for it. This is important so I don't break it in a
81
+ future version unintentionally.
82
+ * Commit, do not mess with rakefile, version, or history.
83
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
84
+ * Send me a pull request. Bonus points for topic branches.
85
+
86
+ == Copyright
87
+
88
+ Copyright (c) 2009 rick. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "faraday"
8
+ gem.summary = "HTTP/REST API client library"
9
+ gem.description = "HTTP/REST API client library with pluggable components"
10
+ gem.email = "technoweenie@gmail.com"
11
+ gem.homepage = "http://github.com/technoweenie/faraday"
12
+ gem.authors = ["rick"]
13
+ end
14
+ Jeweler::GemcutterTasks.new
15
+ rescue LoadError
16
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
17
+ end
18
+
19
+ require 'rake/testtask'
20
+ Rake::TestTask.new(:test) do |test|
21
+ test.libs << 'lib' << 'test'
22
+ test.pattern = 'test/**/*_test.rb'
23
+ test.verbose = true
24
+ end
25
+
26
+ begin
27
+ require 'rcov/rcovtask'
28
+ Rcov::RcovTask.new do |test|
29
+ test.libs << 'test'
30
+ test.pattern = 'test/**/test_*.rb'
31
+ test.verbose = true
32
+ end
33
+ rescue LoadError
34
+ task :rcov do
35
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
36
+ end
37
+ end
38
+
39
+ task :test => :check_dependencies
40
+
41
+ task :default => :test
42
+
43
+ require 'rake/rdoctask'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "faraday #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/faraday.gemspec ADDED
@@ -0,0 +1,66 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{faraday}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["rick"]
12
+ s.date = %q{2009-12-19}
13
+ s.description = %q{HTTP/REST API client library with pluggable components}
14
+ s.email = %q{technoweenie@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "faraday.gemspec",
27
+ "lib/faraday.rb",
28
+ "lib/faraday/adapter/mock_request.rb",
29
+ "lib/faraday/adapter/net_http.rb",
30
+ "lib/faraday/adapter/typhoeus.rb",
31
+ "lib/faraday/connection.rb",
32
+ "lib/faraday/error.rb",
33
+ "lib/faraday/response.rb",
34
+ "lib/faraday/response/yajl_response.rb",
35
+ "test/adapter/typhoeus_test.rb",
36
+ "test/adapter_test.rb",
37
+ "test/connection_test.rb",
38
+ "test/helper.rb",
39
+ "test/live_server.rb",
40
+ "test/response_test.rb"
41
+ ]
42
+ s.homepage = %q{http://github.com/technoweenie/faraday}
43
+ s.rdoc_options = ["--charset=UTF-8"]
44
+ s.require_paths = ["lib"]
45
+ s.rubygems_version = %q{1.3.5}
46
+ s.summary = %q{HTTP/REST API client library}
47
+ s.test_files = [
48
+ "test/adapter/typhoeus_test.rb",
49
+ "test/adapter_test.rb",
50
+ "test/connection_test.rb",
51
+ "test/helper.rb",
52
+ "test/live_server.rb",
53
+ "test/response_test.rb"
54
+ ]
55
+
56
+ if s.respond_to? :specification_version then
57
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
58
+ s.specification_version = 3
59
+
60
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
61
+ else
62
+ end
63
+ else
64
+ end
65
+ end
66
+
data/lib/faraday.rb ADDED
@@ -0,0 +1,47 @@
1
+ module Faraday
2
+ # Loads each autoloaded constant. If thread safety is a concern, wrap
3
+ # this in a Mutex.
4
+ def self.load
5
+ constants.each do |const|
6
+ const_get(const) if autoload?(const)
7
+ end
8
+ end
9
+
10
+ autoload :Connection, 'faraday/connection'
11
+ autoload :Response, 'faraday/response'
12
+ autoload :Error, 'faraday/error'
13
+
14
+ module Adapter
15
+ autoload :NetHttp, 'faraday/adapter/net_http'
16
+ autoload :Typhoeus, 'faraday/adapter/typhoeus'
17
+ autoload :MockRequest, 'faraday/adapter/mock_request'
18
+
19
+ # Names of available adapters. Should not actually load them.
20
+ def self.adapters
21
+ constants
22
+ end
23
+
24
+ # Array of Adapters. These have been loaded and confirmed to work (right gems, etc).
25
+ def self.loaded_adapters
26
+ adapters.map { |c| const_get(c) }.select { |a| a.loaded? }
27
+ end
28
+ end
29
+ end
30
+
31
+ # not pulling in active-support JUST for this method.
32
+ class Object
33
+ # Yields <code>x</code> to the block, and then returns <code>x</code>.
34
+ # The primary purpose of this method is to "tap into" a method chain,
35
+ # in order to perform operations on intermediate results within the chain.
36
+ #
37
+ # (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a.
38
+ # tap { |x| puts "array: #{x.inspect}" }.
39
+ # select { |x| x%2 == 0 }.
40
+ # tap { |x| puts "evens: #{x.inspect}" }.
41
+ # map { |x| x*x }.
42
+ # tap { |x| puts "squares: #{x.inspect}" }
43
+ def tap
44
+ yield self
45
+ self
46
+ end unless Object.respond_to?(:tap)
47
+ end
@@ -0,0 +1,68 @@
1
+ module Faraday
2
+ module Adapter
3
+ module MockRequest
4
+ extend Faraday::Connection::Options
5
+ def self.loaded?() false end
6
+
7
+ include Faraday::Error # ConnectionFailed
8
+
9
+ class Stubs
10
+ def initialize
11
+ # {:get => [Stub, Stub]}
12
+ @stack = {}
13
+ yield self if block_given?
14
+ end
15
+
16
+ def empty?
17
+ @stack.empty?
18
+ end
19
+
20
+ def match(request_method, path, request_headers)
21
+ return false if !@stack.key?(request_method)
22
+ @stack[request_method].detect { |stub| stub.matches?(path, request_headers) }
23
+ end
24
+
25
+ def get(path, request_headers = {}, &block)
26
+ (@stack[:get] ||= []) << new_stub(path, request_headers, block)
27
+ end
28
+
29
+ def new_stub(path, request_headers, block)
30
+ status, response_headers, body = block.call
31
+ Stub.new(path, request_headers, status, response_headers, body)
32
+ end
33
+ end
34
+
35
+ class Stub < Struct.new(:path, :request_headers, :status, :response_headers, :body)
36
+ def matches?(request_path, headers)
37
+ return false if request_path != path
38
+ return true if request_headers.empty?
39
+ request_headers.each do |key, value|
40
+ return true if headers[key] == value
41
+ end
42
+ false
43
+ end
44
+ end
45
+
46
+ def initialize &block
47
+ super nil
48
+ yield stubs
49
+ end
50
+
51
+ def stubs
52
+ @stubs ||= Stubs.new
53
+ end
54
+
55
+ def _get(uri, headers)
56
+ raise ConnectionFailed, "no stubbed requests" if stubs.empty?
57
+ if stub = @stubs.match(:get, uri.path, headers)
58
+ response_class.new do |resp|
59
+ resp.headers = stub.response_headers
60
+ resp.process stub.body
61
+ end
62
+ else
63
+ nil
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,22 @@
1
+ require 'net/http'
2
+ module Faraday
3
+ module Adapter
4
+ module NetHttp
5
+ extend Faraday::Connection::Options
6
+
7
+ def _get(uri, request_headers)
8
+ http = Net::HTTP.new(uri.host, uri.port)
9
+ response_class.new do |resp|
10
+ http_resp = http.get(uri.path, request_headers) do |chunk|
11
+ resp.process(chunk)
12
+ end
13
+ http_resp.each_header do |key, value|
14
+ resp.headers[key] = value
15
+ end
16
+ end
17
+ rescue Errno::ECONNREFUSED
18
+ raise Faraday::Error::ConnectionFailed, "connection refused"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,55 @@
1
+ module Faraday
2
+ module Adapter
3
+ module Typhoeus
4
+ extend Faraday::Connection::Options
5
+
6
+ begin
7
+ require 'typhoeus'
8
+
9
+ def in_parallel?
10
+ !!@parallel_manager
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 _get(uri, request_headers)
29
+ response_class.new do |resp|
30
+ is_async = in_parallel?
31
+ setup_parallel_manager
32
+ req = ::Typhoeus::Request.new(uri.to_s, :headers => request_headers, :method => :get)
33
+ req.on_complete do |response|
34
+ resp.process!(response.body)
35
+ resp.headers = parse_response_headers(response.headers)
36
+ end
37
+ @parallel_manager.queue(req)
38
+ if !is_async then run_parallel_requests end
39
+ end
40
+ rescue Errno::ECONNREFUSED
41
+ raise Faraday::Error::ConnectionFailed, "connection refused"
42
+ end
43
+
44
+ def parse_response_headers(header_string)
45
+ Hash[*header_string.split(/\r\n/).
46
+ tap { |a| a.shift }. # drop the HTTP status line
47
+ map! { |h| h.split(/:\s+/,2) }. # split key and value
48
+ map! { |(k, v)| [k.downcase, v] }.flatten!]
49
+ end
50
+ rescue LoadError => e
51
+ self.load_error = e
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,100 @@
1
+ require 'addressable/uri'
2
+ module Faraday
3
+ class Connection
4
+ module Options
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
12
+
13
+ include Addressable
14
+
15
+ attr_accessor :host, :port, :scheme
16
+ attr_reader :path_prefix
17
+
18
+ def initialize(url = nil)
19
+ @response_class = nil
20
+ if url
21
+ uri = URI.parse(url)
22
+ self.scheme = uri.scheme
23
+ self.host = uri.host
24
+ self.port = uri.port
25
+ self.path_prefix = uri.path
26
+ end
27
+ end
28
+
29
+ # Override in a subclass, or include an adapter
30
+ #
31
+ # def _get(uri, headers)
32
+ # end
33
+ #
34
+ def get(url, params = {}, headers = {})
35
+ _get(build_uri(url, params), headers)
36
+ end
37
+
38
+ def response_class
39
+ @response_class || Response
40
+ end
41
+
42
+ def response_class=(v)
43
+ if v.respond_to?(:loaded?) && !v.loaded?
44
+ raise ArgumentError, "The response class: #{v.inspect} does not appear to be loaded."
45
+ end
46
+ @response_class = v
47
+ end
48
+
49
+ def in_parallel?
50
+ !!@parallel_manager
51
+ end
52
+
53
+ def in_parallel(options = {})
54
+ @parallel_manager = true
55
+ yield
56
+ @parallel_manager = false
57
+ end
58
+
59
+ def setup_parallel_manager(options = {})
60
+ end
61
+
62
+ def run_parallel_requests
63
+ end
64
+
65
+ def path_prefix=(value)
66
+ if value
67
+ value.chomp! "/"
68
+ value.replace "/#{value}" if value !~ /^\//
69
+ end
70
+ @path_prefix = value
71
+ end
72
+
73
+ def build_uri(url, params = {})
74
+ uri = URI.parse(url)
75
+ uri.scheme ||= @scheme
76
+ uri.host ||= @host
77
+ uri.port ||= @port
78
+ if @path_prefix && uri.path !~ /^\//
79
+ uri.path = "#{@path_prefix.size > 1 ? @path_prefix : nil}/#{uri.path}"
80
+ end
81
+ query = params_to_query(params)
82
+ if !query.empty? then uri.query = query end
83
+ uri
84
+ end
85
+
86
+ def params_to_query(params)
87
+ params.inject([]) do |memo, (key, val)|
88
+ memo << "#{escape_for_querystring(key)}=#{escape_for_querystring(val)}"
89
+ end.join("&")
90
+ end
91
+
92
+ # Some servers convert +'s in URL query params to spaces.
93
+ # Go ahead and encode it.
94
+ def escape_for_querystring(s)
95
+ URI.encode_component(s, Addressable::URI::CharacterClasses::QUERY).tap do |escaped|
96
+ escaped.gsub! /\+/, "%2B"
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,5 @@
1
+ module Faraday
2
+ module Error
3
+ class ConnectionFailed < StandardError; end
4
+ end
5
+ end
@@ -0,0 +1,41 @@
1
+ module Faraday
2
+ class Response < Struct.new(:headers, :body)
3
+ class << self
4
+ attr_accessor :load_error
5
+ def loaded?
6
+ !load_error
7
+ end
8
+ end
9
+
10
+ autoload :YajlResponse, 'faraday/response/yajl_response'
11
+
12
+ def initialize(headers = nil, body = nil)
13
+ super(headers || {}, body)
14
+ if block_given?
15
+ yield self
16
+ processed!
17
+ end
18
+ end
19
+
20
+ # TODO: process is a funky name. change it
21
+ # processes a chunk of the streamed body.
22
+ def process(chunk)
23
+ if !body
24
+ self.body = []
25
+ end
26
+ body << chunk
27
+ end
28
+
29
+ # Assume the given content is the full body, and not streamed.
30
+ def process!(full_body)
31
+ process(full_body)
32
+ processed!
33
+ end
34
+
35
+ # Signals the end of streamed content. Do whatever you need to clean up
36
+ # the streamed body.
37
+ def processed!
38
+ self.body = body.join if body.respond_to?(:join)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,35 @@
1
+ module Faraday
2
+ class Response
3
+ class YajlResponse < Response
4
+ attr_reader :body
5
+
6
+ begin
7
+ require 'yajl'
8
+
9
+ def initialize(headers = nil, body = nil)
10
+ super
11
+ @parser = nil
12
+ end
13
+
14
+ def process(chunk)
15
+ if !@parser
16
+ @parser = Yajl::Parser.new
17
+ @parser.on_parse_complete = method(:object_parsed)
18
+ end
19
+ @parser << chunk
20
+ end
21
+
22
+ def processed!
23
+ @parser = nil
24
+ end
25
+
26
+ def object_parsed(obj)
27
+ @body = obj
28
+ end
29
+
30
+ rescue LoadError => e
31
+ self.load_error = e
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'helper'))
2
+
3
+ if Faraday::Adapter::Typhoeus.loaded?
4
+ class TyphoeusTest < Faraday::TestCase
5
+ describe "#parse_response_headers" do
6
+ before do
7
+ @conn = Object.new.extend(Faraday::Adapter::Typhoeus)
8
+ end
9
+
10
+ it "leaves http status line out" do
11
+ headers = @conn.parse_response_headers("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n")
12
+ assert_equal %w(content-type), headers.keys
13
+ end
14
+
15
+ it "parses lower-cased header name and value" do
16
+ headers = @conn.parse_response_headers("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n")
17
+ assert_equal 'text/html', headers['content-type']
18
+ end
19
+
20
+ it "parses lower-cased header name and value with colon" do
21
+ headers = @conn.parse_response_headers("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://sushi.com/\r\n\r\n")
22
+ assert_equal 'http://sushi.com/', headers['location']
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,59 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class AdapterTest < Faraday::TestCase
4
+ before do
5
+ @connection = Faraday::Connection.new(LIVE_SERVER)
6
+ end
7
+
8
+ Faraday::Adapter.loaded_adapters.each do |adapter|
9
+ describe "#get with #{adapter} adapter" do
10
+ before do
11
+ @connection.extend adapter
12
+ end
13
+
14
+ it "retrieves the response body" do
15
+ assert_equal 'hello world', @connection.get('hello_world').body
16
+ end
17
+
18
+ it "retrieves the response body with YajlResponse" do
19
+ @connection.response_class = Faraday::Response::YajlResponse
20
+ assert_equal [1,2,3], @connection.get('json').body
21
+ end
22
+
23
+ it "retrieves the response headers" do
24
+ assert_equal 'text/html', @connection.get('hello_world').headers['content-type']
25
+ end
26
+ end
27
+
28
+ describe "async requests" do
29
+ before do
30
+ @connection.extend adapter
31
+ end
32
+
33
+ it "clears parallel manager after running a single request" do
34
+ assert !@connection.in_parallel?
35
+ resp = @connection.get('hello_world')
36
+ assert !@connection.in_parallel?
37
+ assert_equal 'hello world', @connection.get('hello_world').body
38
+ end
39
+
40
+ it "uses parallel manager to run multiple json requests" do
41
+ resp1, resp2 = nil, nil
42
+
43
+ @connection.response_class = Faraday::Response::YajlResponse
44
+ @connection.in_parallel do
45
+ resp1 = @connection.get('json')
46
+ resp2 = @connection.get('json')
47
+ assert @connection.in_parallel?
48
+ if adapter.supports_async?
49
+ assert_nil resp1.body
50
+ assert_nil resp2.body
51
+ end
52
+ end
53
+ assert !@connection.in_parallel?
54
+ assert_equal [1,2,3], resp1.body
55
+ assert_equal [1,2,3], resp2.body
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,123 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class ConnectionTest < Faraday::TestCase
4
+ describe "#initialize" do
5
+ it "parses @host out of given url" do
6
+ conn = Faraday::Connection.new "http://sushi.com"
7
+ assert_equal 'sushi.com', conn.host
8
+ end
9
+
10
+ it "parses nil @port out of given url" do
11
+ conn = Faraday::Connection.new "http://sushi.com"
12
+ assert_nil conn.port
13
+ end
14
+
15
+ it "parses @scheme out of given url" do
16
+ conn = Faraday::Connection.new "http://sushi.com"
17
+ assert_equal 'http', conn.scheme
18
+ end
19
+
20
+ it "parses @port out of given url" do
21
+ conn = Faraday::Connection.new "http://sushi.com:815"
22
+ assert_equal 815, conn.port
23
+ end
24
+
25
+ it "parses nil @path_prefix out of given url" do
26
+ conn = Faraday::Connection.new "http://sushi.com"
27
+ assert_equal '/', conn.path_prefix
28
+ end
29
+
30
+ it "parses @path_prefix out of given url" do
31
+ conn = Faraday::Connection.new "http://sushi.com/fish"
32
+ assert_equal '/fish', conn.path_prefix
33
+ end
34
+ end
35
+
36
+ describe "#build_uri" do
37
+ it "uses Connection#host as default URI host" do
38
+ conn = Faraday::Connection.new
39
+ conn.host = 'sushi.com'
40
+ uri = conn.build_uri("/sake.html")
41
+ assert_equal 'sushi.com', uri.host
42
+ end
43
+
44
+ it "uses Connection#port as default URI port" do
45
+ conn = Faraday::Connection.new
46
+ conn.port = 23
47
+ uri = conn.build_uri("http://sushi.com")
48
+ assert_equal 23, uri.port
49
+ end
50
+
51
+ it "uses Connection#scheme as default URI scheme" do
52
+ conn = Faraday::Connection.new 'http://sushi.com'
53
+ uri = conn.build_uri("/sake.html")
54
+ assert_equal 'http', uri.scheme
55
+ end
56
+
57
+ it "uses Connection#path_prefix to customize the path" do
58
+ conn = Faraday::Connection.new
59
+ conn.path_prefix = '/fish'
60
+ uri = conn.build_uri("sake.html")
61
+ assert_equal '/fish/sake.html', uri.path
62
+ end
63
+
64
+ it "uses '/' Connection#path_prefix to customize the path" do
65
+ conn = Faraday::Connection.new
66
+ conn.path_prefix = '/'
67
+ uri = conn.build_uri("sake.html")
68
+ assert_equal '/sake.html', uri.path
69
+ end
70
+
71
+ it "forces Connection#path_prefix to be absolute" do
72
+ conn = Faraday::Connection.new
73
+ conn.path_prefix = 'fish'
74
+ uri = conn.build_uri("sake.html")
75
+ assert_equal '/fish/sake.html', uri.path
76
+ end
77
+
78
+ it "ignores Connection#path_prefix trailing slash" do
79
+ conn = Faraday::Connection.new
80
+ conn.path_prefix = '/fish/'
81
+ uri = conn.build_uri("sake.html")
82
+ assert_equal '/fish/sake.html', uri.path
83
+ end
84
+
85
+ it "allows absolute URI to ignore Connection#path_prefix" do
86
+ conn = Faraday::Connection.new
87
+ conn.path_prefix = '/fish'
88
+ uri = conn.build_uri("/sake.html")
89
+ assert_equal '/sake.html', uri.path
90
+ end
91
+
92
+ it "parses url/params into #path" do
93
+ conn = Faraday::Connection.new
94
+ uri = conn.build_uri("http://sushi.com/sake.html")
95
+ assert_equal '/sake.html', uri.path
96
+ end
97
+
98
+ it "parses url/params into #query" do
99
+ conn = Faraday::Connection.new
100
+ uri = conn.build_uri("http://sushi.com/sake.html", 'a[b]' => '1 + 2')
101
+ assert_equal "a%5Bb%5D=1%20%2B%202", uri.query
102
+ end
103
+
104
+ it "parses url into #host" do
105
+ conn = Faraday::Connection.new
106
+ uri = conn.build_uri("http://sushi.com/sake.html")
107
+ assert_equal "sushi.com", uri.host
108
+ end
109
+
110
+ it "parses url into #port" do
111
+ conn = Faraday::Connection.new
112
+ uri = conn.build_uri("http://sushi.com/sake.html")
113
+ assert_nil uri.port
114
+ end
115
+ end
116
+
117
+ describe "#params_to_query" do
118
+ it "converts hash of params to URI-escaped query string" do
119
+ conn = Faraday::Connection.new
120
+ assert_equal "a%5Bb%5D=1%20%2B%202", conn.params_to_query('a[b]' => '1 + 2')
121
+ end
122
+ end
123
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'context'
3
+ if ENV['LEFTRIGHT']
4
+ require 'leftright'
5
+ end
6
+
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
+ require 'faraday'
10
+
11
+ module Faraday
12
+ class TestCase < Test::Unit::TestCase
13
+ LIVE_SERVER = 'http://localhost:4567'
14
+
15
+ class TestConnection < Faraday::Connection
16
+ include Faraday::Adapter::MockRequest
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+
4
+ get '/hello_world' do
5
+ 'hello world'
6
+ end
7
+
8
+ get '/json' do
9
+ "[1,2,3]"
10
+ end
@@ -0,0 +1,34 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'helper'))
2
+
3
+ class ResponseTest < Faraday::TestCase
4
+ describe "unloaded response class" do
5
+ it "is not allowed to be set" do
6
+ resp_class = Object.new
7
+ def resp_class.loaded?() false end
8
+ conn = Faraday::Connection.new
9
+ assert_raises ArgumentError do
10
+ conn.response_class = resp_class
11
+ end
12
+ end
13
+ end
14
+
15
+ describe "TestConnection#get with default Faraday::Response class" do
16
+ it "returns Faraday::Response" do
17
+ conn = TestConnection.new do |stub|
18
+ stub.get('/hello') { [200, {}, 'hello world']}
19
+ end
20
+ resp = conn.get('/hello')
21
+ assert_equal 'hello world', resp.body
22
+ end
23
+ end
24
+
25
+ describe "TestConnection#get with Faraday::YajlResponse class" do
26
+ it "returns string body" do
27
+ conn = TestConnection.new do |stub|
28
+ stub.get('/hello') { [200, {}, '[1,2,3]']}
29
+ end
30
+ conn.response_class = Faraday::Response::YajlResponse
31
+ assert_equal [1,2,3], conn.get('/hello').body
32
+ end
33
+ end
34
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: faraday
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - rick
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-19 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: HTTP/REST API client library with pluggable components
17
+ email: technoweenie@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.rdoc
25
+ files:
26
+ - .document
27
+ - .gitignore
28
+ - LICENSE
29
+ - README.rdoc
30
+ - Rakefile
31
+ - VERSION
32
+ - faraday.gemspec
33
+ - lib/faraday.rb
34
+ - lib/faraday/adapter/mock_request.rb
35
+ - lib/faraday/adapter/net_http.rb
36
+ - lib/faraday/adapter/typhoeus.rb
37
+ - lib/faraday/connection.rb
38
+ - lib/faraday/error.rb
39
+ - lib/faraday/response.rb
40
+ - lib/faraday/response/yajl_response.rb
41
+ - test/adapter/typhoeus_test.rb
42
+ - test/adapter_test.rb
43
+ - test/connection_test.rb
44
+ - test/helper.rb
45
+ - test/live_server.rb
46
+ - test/response_test.rb
47
+ has_rdoc: true
48
+ homepage: http://github.com/technoweenie/faraday
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options:
53
+ - --charset=UTF-8
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ requirements: []
69
+
70
+ rubyforge_project:
71
+ rubygems_version: 1.3.5
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: HTTP/REST API client library
75
+ test_files:
76
+ - test/adapter/typhoeus_test.rb
77
+ - test/adapter_test.rb
78
+ - test/connection_test.rb
79
+ - test/helper.rb
80
+ - test/live_server.rb
81
+ - test/response_test.rb